#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#------------------------------------------------------------
package Common::DiskHash;
use strict;
use warnings;

use Data::Dumper;
use File::Path;

sub _fixKey {
    my ($inKey) = @_;

    my $outKey = $inKey;

    return $outKey;
}

sub DESTROY {
    my ($self) = @_;
    if ( $self->{_deleteOnDestroy} ) {
        rmtree( $self->{_baseDir} );
    }
}

sub TIEHASH {
    my ( $pkg, %data ) = @_;

    my $self = bless {}, $pkg;

    my $baseDir = $data{base};
    if ( !$baseDir ) { $baseDir = '/tmp/DiskHash'; }
    $self->{_baseDir} = $baseDir;

    if ( !-e $baseDir ) {
        mkpath($baseDir);
    }

    if ( defined $data{deleteOnDestroy} ) {
        $self->{_deleteOnDestroy} = $data{deleteOnDestroy};
    }

    return $self;
}

sub FETCH {
    my ( $self, $key ) = @_;

    # If we're being asked for something that does not exist...
    # we might be trying to do a nested hash lookup.
    # So, I should go ahead and create a subdirectory.
    #
    my $path = $self->{_baseDir} . "/" . $key;
    if ( !-e $path || -d $path ) {
        my %subHash;
        tie %subHash, 'Common::DiskHash', base => $path;
        return \%subHash;
    }

    open DATA, "$path" or die "ERROR - can't open $path for reading: $!\n";
    my $data;
    while ( my $line = <DATA> ) {
        $data .= $line;
    }
    return $data;
}

sub STORE {
    my ( $self, $key, $value ) = @_;

    # !!! Assuming plain old scalars being stored so far - need to
    # !!! extend this to handle objects and/or hashes.
    #
    my $path = $self->{_baseDir} . "/" . $key;

    # Nuke as we go
    #
    unlink($path);

    # What are we trying to store, anyway?
    #
    if ( !ref($value) ) {

        # Regular old scalar
        #
        open DATA, "> $path" or die "ERROR - can't open $path for writing: $!\n";
        print DATA $value;
        close DATA;
    } elsif ( 'HASH' eq ref($value) ) {

        # Transform this hash into one of these DiskHash thingies.
        #
        my $subHash = $self->FETCH($key);
        foreach my $subKey ( keys %$value ) {
            $subHash->{$subKey} = $value->{$subKey};
        }
    } else {
        die "ERROR - DiskHash does not support storing arrays or objects (yet)\n";
    }

    return $value;
}

sub DELETE {
    my ( $self, $key ) = @_;

    my $path = $self->{_baseDir} . "/" . $key;
    unlink $path;
}

sub EXISTS {
    my ( $self, $key ) = @_;
    my $path = $self->{_baseDir} . "/" . $key;
    return ( -e $path );
}

sub FIRSTKEY {
    my ($self) = @_;

    if ( $self->{_dirFH} ) {
        close $self->{_dirFH};
        $self->{_dirFH} = undef;
    }

    # Need to use a local filehandle, or this won't work if called recursively.
    #
    local *DIR;
    opendir( DIR, $self->{_baseDir} ) or die "ERROR - can't open " . $self->{_baseDir} . " for scanning: $!\n";
    $self->{_dirFH} = *DIR;

    return $self->NEXTKEY();
}

sub NEXTKEY {
    my ($self) = @_;

    if ( !$self->{_dirFH} ) {
        return undef;
    }

    my $file;
    while ( defined( $file = readdir( $self->{_dirFH} ) ) ) {

        # skip any file or directory that starts with '.'.
        # This is to avoid the magic '.' and '..' directories,
        # and to also avoid exposing our '.*' metadata files.
        #
        last unless '.' eq substr( $file, 0, 1 );
    }
    if ( !defined $file ) {
        close $self->{_dirFH};
        $self->{_dirFH} = undef;
    }

    return $file;
}
###
1;    #
###
