package LIMS::Model::Base;

# base package for LIMS::Model classes that want $self->user_profile
# generic Rose::DB::Object::Manager methods - all require $args
# (hashref of object_class (required) & args_for_search (optional)

use Moose;
with 'LIMS::Model::Roles::SessionData'; # provides $self->user_profile
    
use namespace::clean -except => 'meta';

# passed in from LIMS::model as my $model = $package->new(%args):
has _lims_db  => ( is => 'ro', isa => 'LIMS::DB', required => 1 );

has sql_lib   => ( is => 'ro', isa => 'SQL::Library', lazy_build => 1 );

__PACKAGE__->meta->make_immutable;

use LIMS::Local::Debug;
use LIMS::Local::Utils;

use DBIx::Simple;
use SQL::Library;
use IO::All;

use Data::Dumper;

# return LIMS::DB object passed in from LIMS::model():
sub lims_db { return shift->_lims_db }
# return LIMS config settings:
sub lims_cfg { LIMS::Local::Config->instance; }

#-------------------------------------------------------------------------------
sub get_objects {
    my $self  = shift;
    my $class = shift; # scalar    
    my $args  = shift || {}; # optional hashref, passed straight to Manager

    my $rows = Rose::DB::Object::Manager->get_objects(
        %$args,
        object_class => 'LIMS::DB::' . $class,
    );

    return $rows;
}

#-------------------------------------------------------------------------------
sub get_objects_with {
    my $self = shift;
    my $args = shift; # hashref of keys = class & with
    
    my $rows = Rose::DB::Object::Manager->get_objects(
        require_objects => $args->{with},
        object_class    => 'LIMS::DB::' . $args->{class},
    );

    return $rows;
}

#-------------------------------------------------------------------------------
sub get_meta {
    my $self  = shift;
    my $class = shift; # scalar    
    
    my $object_class = 'LIMS::DB::' . $class;
    
    return $object_class->new->meta;
}

#-------------------------------------------------------------------------------
sub get_objects_count {
    my $self  = shift;
    my $class = shift; # scalar    
    my $args  = shift || {}; # optional hashref, passed straight to Manager

    my $count = Rose::DB::Object::Manager->get_objects_count(
        %$args,
        object_class => 'LIMS::DB::' . $class,
    );

    return $count;
}

#-------------------------------------------------------------------------------
sub get_objects_iterator {
    my $self  = shift;
    my $class = shift; # scalar    
    my $args  = shift || {}; # optional hashref, passed straight to Manager

    my $iterator = Rose::DB::Object::Manager->get_objects_iterator(
        %$args,
        object_class => 'LIMS::DB::' . $class,
    );

    return $iterator;
}

#-------------------------------------------------------------------------------
sub lims_dbix {
    my $self = shift;
    
	my $dbh = $self->lims_db->dbh;
	
	my $dbix = DBIx::Simple->new($dbh);
    
    return $dbix;
}

#-------------------------------------------------------------------------------
sub time_now { # returns DateTime object
	my $self = shift;
	
	return LIMS::Local::Utils::time_now; # considers BST
}

#-------------------------------------------------------------------------------
# generic update method - uses insert_or_update(), and will update record if 'id'
# supplied, otherwise insert new. Assumes any unique_key(s) has passed validation
# check & belongs to record being edited. Expects hashref with keys 'class' & 'data':
sub update_object {
    my $self = shift; 
    my $args = shift; # $self->debug( $args );
    
    my $class = $args->{class};    
    my $data  = $args->{data}; 

    my $object_class = 'LIMS::DB::' . $class;
    my $o = $object_class->new();

    my %args = ( data => $data, obj => $o );

    # get form_params for specimens table into specimen object:
    $self->populate_object_with_form_data(\%args);

#$self->set_rose_debug(1);
    eval {
        $o->insert_or_update( changes_only => 1 ); # doesn't do 'changes_only'
    };
#$self->set_rose_debug(0);

    return $@ if $@;
}

#-------------------------------------------------------------------------------
# used in validation_models to check uniqueness of column value
# expects hashref of args: class_name, column_name & column_value
sub get_object_by_param {
    my $self = shift;
    my $args = shift; # $self->debug($args);
    
    my $class = $args->{class_name};
    my %query = ( $args->{column_name} => $args->{column_value} );
    
    my $object_class = 'LIMS::DB::'.$class;
    
#$self->set_rose_debug(1);
    my $o = $object_class->new(%query)->load(speculative => 1);
#$self->set_rose_debug(0);

    return $o;    
}

#-------------------------------------------------------------------------------
# accepts hash(ref) of object to be populated, and submitted form_params
# and populates object with form_params - returns nothing: 
sub populate_object_with_form_data {
    my $self = shift;
    my $args = shift; # $self->debug($args);
    
    my $data = $args->{data};
    my $obj  = $args->{obj};
    
    my @cols = $obj->meta->column_names;
    
    # foreach table col:
    foreach my $col (@cols) {
        # value = corresponding form_param value:
        my $val = $data->{$col};
        # populate object accessor with value:
        $obj->$col($val);
    } 
}

#-------------------------------------------------------------------------------
sub set_rose_debug {
    my ($self, $switch) = @_;

    $Rose::DB::Object::Debug =
        $Rose::DB::Object::Manager::Debug
            = $switch || 0; # zero probably not captured in $switch ??
}

#-------------------------------------------------------------------------------
sub debug {
    my ($self, $str) = @_;

    warn Dumper $str;

    DEBUG($str);
}


#-------------------------------------------------------------------------------
sub _build_sql_lib {
    my $self = shift; 

    my $path_to_app_root = $self->lims_cfg->{path_to_app_root}; 
    
#    my $sql_lib
#        = new SQL::Library( { lib => "$path_to_app_root/src/lib/library.sql" } );
    
	# can only handle multiple sql libraries as strings:
	my @libs
		= map { io( sprintf '%s/src/lib/%s.sql', $path_to_app_root, $_ )->slurp }
            qw(library local);
	
    my $sql_lib = new SQL::Library( { lib => \@libs	} ); # warn Dumper $sql_lib->elements; 

    return $sql_lib;
}

1;
