package APE::InputController::DNTODN_InputController;

use base 'APE::InputController';

use strict;
use warnings;
use Carp;

use constant LOGIN => 'snp';

use constant TRUE  => 1;
use constant FALSE => 0;

{
    my %_attributes = ( );

    my %object;

    ## Autoloader wannabe. Inserts the sub calls that I want to be
    ## accessible into the symbol table.
    foreach my $attName (keys(%_attributes)) {
	no strict 'refs';
	*{__PACKAGE__ . "::get\u$attName"} = sub { $object{ $_[0] }->{$attName} }
    }


    #################################################################
    ## Initialise the objects private attributes. The argument is
    ## deleted as it is found, initialised and set.
    #################################################################
    sub _init {	
	my ($self,%args) = @_;

	foreach my $attName (keys(%args)) {
	    if(exists($_attributes{$attName})) { 
		$object{ $self }->{$attName} = delete $args{$attName};
	    }
	} 
  	## Set the description as this is a specialised InputController.
	$args{description} = "An InputController that sorts " .
	"requirements to convert multiple 96 well entities to 384 well ".
	"entities.";

	return $self->SUPER::_init(%args);
    }


    #################################################################
    ## Overridden method from base class to create the Requirement
    ## objects. Calls the base class method of the same name to build
    ## the basic number of Requirement objects. It then adds its own
    ## Requirement object (type='Input') that requests the user to
    ## enter a plate coord.
    #################################################################
    sub createRequirements {
	my $self = shift;
	return if $self->hasRequirements();
	my $atomicProcessContainer = $self->getAtomicProcessContainer;

	my $arrayingEntityList = $atomicProcessContainer->getArrayingEntityList();
	my %lookupPlate  =();
	my $indexPlate   =-1;
	my @storePlate   =();
	my $description;
	## Create Requirements for each inConcreteEntity.
	for(my $aei=0; $aei < @{$arrayingEntityList}; $aei++) {
	    my $atomicProcessList = $arrayingEntityList->[$aei]->getAtomicProcessList();
	    for(my $api=0; $api < @{$atomicProcessList}; $api++) {
		my $description = $atomicProcessList->[$api]->getInDescription();
		my $inConcreteEntity  = $atomicProcessList->[$api]->getInConcreteEntity();
		my $outConcreteEntity = $atomicProcessList->[$api]->getOutConcreteEntity();
		## Create a new Requirement per in concrete entity id.
		if(!exists($lookupPlate{$inConcreteEntity})) {
		    $lookupPlate{$inConcreteEntity} = ++$indexPlate;
		    my $localDescription = $description . " - (".$atomicProcessList->[$api]->getInPrefix()."): ";
		    $storePlate[$lookupPlate{$inConcreteEntity}] 
			= new APE::InputController::Requirement
			(arrayingEntityIndex=>[$aei],
			 atomicProcessIndex =>[$api],
			 type               =>$atomicProcessList->[$api]->getInPrefix(),
			 description        =>$localDescription);
		}else{
		    ## Push the ArrayingEntity index to the Requirement object.
		    my $res = $storePlate[$lookupPlate{$inConcreteEntity}]->pushIndexes($aei,$api);
		}
	    }
	}
	
	my $requirementContainer = 
	    new APE::InputController::RequirementContainer();

	## Push the plate and column Requirement objects onto the
	## queue.
	for(my $i=0; $i < scalar(@storePlate); $i++) {
	    $requirementContainer->pushRequirement($storePlate[$i]);
	}
	
	$self->pushRequirementContainer($requirementContainer);
    }


    #################################################################
    ## Overridden method from base class. This method overrides the  
    ## default SQL statement used to retrieve DN data so that it 
    ## matches the data required to create a new DN plate (usually made
    ## with a group of DNA tubes - their data is different, see the  
    ## base classes sql_lib InputController.sql_lib).
    #################################################################
    sub getData {
    	my ($self,$requirementObj) = @_;
	## Split the data. Expects a plate name i.e. EP1234.
	my ($prefix,$id) = $requirementObj->getData() =~ /^(\w\w)(\d+)$/;
	croak "ERROR: input should be a DN" unless $prefix eq 'DN';

	## Get a list of the AtomicProcess objects this Requriement object
	## references. Only use the first AtomicProcess object as they're
	## all assumed to be the same format.
	my $atomicProcessList = $self->getAtomicProcess($requirementObj);
	my $atomicProcess     = $atomicProcessList->[0];

	## Execute the SQL.
	my $lookupData = eval { 
	    $self->getSqlEngineObj()->getAll('APE/InputController/DNTODN_InputController::DNTODN_lookup',
					     [$id],
					     SqlEngine2::getDbh(LOGIN)); };
	if($@) { 
	    throw Class::Throwable::Simple("ERROR: can't execute sql 'DNTODN_lookup' as part of the ". 
					   "specialised InputController class DNTODN_InputController: $@");  
	}

	if(scalar(@{$lookupData})==0) { 
	    throw Class::Throwable::Simple("ERROR: nothing found using '$prefix$id'"); 
	}

	my $inFormat = $atomicProcess->getInFormat();
	## Check that the number of rows returned equals the
	## AtomicProcess inFormat attribute. 
	unless(scalar(@{$lookupData}) == $inFormat) {
	    throw Class::Throwable::Simple("ERROR: '$prefix$id' appears to be a partial plate - ".
					   "required format is '$inFormat' " . 
					   "format found is '".scalar(@{$lookupData})."'");
	}

	$requirementObj->setDataStore($lookupData);
    	return $requirementObj;
    }


    #################################################################
    ## Method to destroy this object.
    #################################################################
    sub DESTROY {
	my $self = shift;
	delete $object{$self};
	$self->SUPER::DESTROY;
    }
}

1;

