# Copyright 2002-2008 Josh Clark and Global Moxie, LLC. This code cannot be 
# redistributed without permission from globalmoxie.com.  For more
# information, consult your Big Medium license.
#
# $Id: Driver.pm 3043 2008-03-31 14:00:38Z josh $

package BigMed::Driver;
use strict;
use utf8;
use Carp;
use base qw(BigMed::Error);
use BigMed::Elements;
use BigMed::DiskUtil qw(bm_file_path bm_confirm_dir);
use BigMed::Trigger;

###########################################################
# CONSTRUCTORS
###########################################################

sub new {
    my $class    = shift;
    my $subclass = shift;
    $subclass &&= $class . '::' . $subclass;
    $subclass ||= $class . '::File'; #use File as default
    ( my $file = "$subclass.pm" ) =~ s{::}{/}msg;
    local $SIG{'__DIE__'}; #set to undef
    eval { require $file };
    croak "Bad driver: $subclass. $@" if $@;
    my $driver = bless {}, $subclass;
    return $driver->initialize(@_);
}

sub initialize {
    ref ( $_[0]->{_bigmed} = $_[1] ) eq 'BigMed'
      or croak "BigMed object required to initialize driver";
    $_[0]->{_DATADIR} = {};
    return $_[0];
}

sub bigmed {
    $_[0]->{_bigmed};
}

###########################################################
# DATA ACCESS AND PACK/UNPACK
###########################################################

#handled by subclasses
sub save;
sub fetch;
sub trash;
sub trash_all; #needs to discover and execute before_trash_all/after_trash_all
sub update_id;
sub select;
sub selection_class;
sub tune;

#patch through element packers/unpackers to the element routine
sub pack {
    BigMed::Elements->pack( name => $_[1], data => $_[2], param => $_[3] );
}

#this is deprecated, call elements directly instead
sub unpack {
    shift; #kill driver class
    BigMed::Elements->unpack( @_ );
}

sub is_numeric {
    BigMed::Elements->is_numeric( $_[1] );
}

###########################################################
# STASH
###########################################################

sub set_stash {
    my $driver = shift;
    croak 'usage: $driver->set_stash("key", $value)' if @_ %2;
    my %arg = @_;
    while ( my ($k,$v) = each (%arg) ) {
        $driver->{_STASH}->{$k} = $v;
    }
    $driver;
}

sub stash {
    my $driver = shift;
    my @keys = grep { defined $_ } @_;
    return wantarray ? ( map { $driver->{_STASH}->{$_} } @keys )
      : defined $keys[0] ? $driver->{_STASH}->{ $keys[0] }
      : undef;
}

###########################################################
# META INFO
###########################################################

sub data_directory_path {
    my ($driver, $data_info) = @_;
    
    #data_info is data object or {source=>data_source, site=>site_id}
    my $param_class = ref $data_info
      or croak 'Invalid object/info supplied in data_directory_path request';
    my ($source, $class);
    if ($param_class eq 'HASH') {
        $source = $data_info->{source}
          or croak 'Unknown data source in data_directory_path request';
        $class = BigMed::Data->source_class($source)
          or croak 'Unknown data class in data_directory_path request';
    }
    else {
        $source = $data_info->data_source
          or croak 'Unknown data source in data_directory_path request';
        $class = $param_class
          or croak 'Unknown data class in data_directory_path request';
    }
    
    #get directory from cache if available, buys a pretty significant savings
    #when cycling through big selection.

    my ($data_path, $cache_path);
    my $rcache = $driver->{_DATADIR};
    if (!$class->systemwide) {
        my $site_id = $param_class eq 'HASH'
          ? $data_info->{site}
          : $data_info->site;
        $site_id += 0
          or croak 'Must specify site id to look up data directory for '
          . $source;
        
        return $rcache->{$source}->{$site_id}
          if $rcache->{$source} && $rcache->{$source}->{$site_id};
        $data_path = bm_file_path( $driver->bigmed->env('MOXIEDATA'),
            'data', 'sites', "site$site_id", $source );
        bm_confirm_dir( $data_path, { build_path => 1, data => 1 } )
          or return;
        $rcache->{$source}->{$site_id} = $data_path;
    }
    else {
        return $rcache->{$source} if $rcache->{$source};
        $data_path = bm_file_path( $driver->bigmed->env('MOXIEDATA'), 'data', $source );
        bm_confirm_dir( $data_path, { build_path => 1, data => 1 } )
          or return;
        $rcache->{$source} = $data_path;
    }
    return $data_path;
}

sub site_directory_path {
    my $driver = shift;
    my $site   = shift
      or croak "No site provided in request for site_directory_path";
    $site = ref $site ? $site->id : $site;
    $site or croak "Site id unknown in request for site_directory_path";
    my $bigmed = $driver->bigmed;
    bm_file_path( $bigmed->env('MOXIEDATA'), 'data', 'sites', "site$site" );
}

1;
__END__

