package GTx::APE::InputController::EPSA_InputController;

BEGIN {
    #$ENV{WRAPDBI_TEST_CONFIG} = "/nfs/team71/usg/holroyd/badger-cvs/GTx/APE/DBlogs";
    $ENV{DEBUG_WRAPDBI} = 1;
};

use base 'GTx::APE::InputController';

use strict;
use warnings;
use Carp;

use GTx::APE::InputController::Requirement;

use constant LOGIN => 'snp';

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

{
    my %_attributes = ( 
			## ** Add your object attributes here **
			## They will automatically be given a get method.
			## e.g. attribute 'personForename' can be accessed
			## using getPersonForname(). There are no automatic 
			## set routines, they must be declared or the attribute 
			## value must be parsed in the constructor 
			## i.e new InputController(personForename=>'Simon');
		       );

    my %object;
    
    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 can distinguish between an EP sense and an ".
	    "EP antisense plate and perform a lookup in SNP to retrieve information on them.";

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

    #################################################################
    ## Overridden method from InputController base class.
    #################################################################
    sub createRequirements {
	my $self = shift;
	## Call the super classes createRequirements method to get the
	## basic requirements.
	$self->SUPER::createRequirements();
    }   
    

    #################################################################
    ## Overridden method from InputController base class.
    ## Processes a Requirment object from the requirementObjectList
    ## built by the createRequirements method.
    #################################################################
    sub process {
	my ($self,$requirementObj) = @_;
	## Checks the data attribute of the Requirement object against
	## each of the Requriements objects in the completedRequirementList.
	if($self->isDuplicateData($requirementObj)) {
	    croak "ERROR: '", $requirementObj->getData(), "' has already been entered.\n";
	}
	## Get data associated with Requirement.
	## Ignore 'Input' type Requirement objects.
	unless($requirementObj->getType() eq 'Input') {
	    $requirementObj = eval  { $self->getData($requirementObj); };
	    if($@)                  { croak $@; }
	}

	## Push the Requirement object on to the completedRequirementList.
	$self->pushCompletedRequirement($requirementObj);

	## Wait until all of the Requirement objects have been
	## returned.
	print STDERR "Testing: ",$self->getRequirementCount()." == ". 
	    $self->getCompletedRequirementCount()."\n";
	if($self->getRequirementCount() == $self->getCompletedRequirementCount()) {
	    ## Set the sessionId foreach ArrayingEntity via the AtomicProcessContainer.
	    eval  { ${$self->getAtomicProcessContainer()}->setSessionId($self->getSessionId()); };
	    if($@){ croak "ERROR: can't set the sessionId: $@\n";                               }
	    foreach my $req (@{$self->getCompletedRequirementList()}) {
		$self->arrayData($req);
	    }
	}
    }


    #################################################################
    ## Overridden method to get data from the database based on the 
    ## Requirement object. 
    #################################################################
    sub getData {
	my ($self,$requirementObj) = @_;
	#croak "ERROR: Requirement object is not type 'EP' - '", $requirementObj->getType() 
	#    unless $requirementObj->getType() eq 'EP';
	## Split the data. Default is a plate name i.e. EP1234.
	my ($prefix,$id) = $requirementObj->getData() =~ /^(\w\w)(\d+)$/;
	## Get data down to the AtomicProcess.
	my $arrayingEntityIndex    = $requirementObj->getArrayingEntityIndex();
	my $atomicProcessIndex     = $requirementObj->getAtomicProcessIndex();

	## The list may be an array, just check the first index and
	## assume the others are the same.
	if(ref($arrayingEntityIndex) eq 'ARRAY') {
	    $arrayingEntityIndex = $arrayingEntityIndex->[0];
	    $atomicProcessIndex  = $atomicProcessIndex->[0];
	}

	my $atomicProcessContainer = ${$self->getAtomicProcessContainer()};
	my $arrayingEntity         = $atomicProcessContainer->getArrayingEntityList()->[$arrayingEntityIndex];
	my $atomicProcess          = $arrayingEntity->getAtomicProcessList()->[$atomicProcessIndex];
	my $plateType;
	my $plateDesc;

	## This isn't an EP type - pass it to the super class.
	unless($requirementObj->getType() eq 'EP') {
	    my $requirementObjNew = eval { print STDERR "Passing Requirement object to base class\n"; 
					   $self->SUPER::getData($requirementObj); };
	    if($@){ croak $@; }
	    else  { print STDERR "Base class managed to do its job!\n"; return $requirementObjNew; }
	}

	## Find the plate type - sense, antisense or mix. Type EP
	## plates do exist that we don't want to deal with here. If it
	## is not an EP then let the base class have a go before
	## throwing an error.
	unless(($plateType,$plateDesc) = getType($atomicProcess->getInEntityTypeId())) {
	    croak "ERROR: can't find plate type and description using in entity type id: ",
	    $atomicProcess->getInEntityTypeId(), "\n";
	}
	
	## Execute the lookup.
	my $lookupData = eval { 
	    $self->getSqlEngineObj()
		->getAll('GTx/APE/InputController/EPSA_InputController::EPSA_lookup',
			 [$id,$plateType],
			 SqlEngine2::getDbh(LOGIN)); 
	};
	
	## Errors in SQL execution and nothing returned.
	if($@) { croak "ERROR: can't get data for: ",$requirementObj->getData()," - $id - $plateType: $@\n"; }
	if(scalar(@{$lookupData})==0) { croak "ERROR: nothing found for '",$requirementObj->getData(), 
					"' - wrong type?\n"; }


	my $inFormat = $atomicProcess->getInFormat();
	## Check that the number of rows returned equals the
	## AtomicProcess inFormat attribute. In the case of a mixer
	## plate the format should be doubled as there are two primers
	## in each well, thus twice the number of rows.
	print STDERR "Testing format: ",scalar(@{$lookupData})," = ",($plateType == 3 ? $inFormat * 2 : $inFormat),"\n";
	unless(scalar(@{$lookupData}) == ($plateType == 3 ? $inFormat * 2 : $inFormat)) {
	    croak "ERROR: '$prefix$id' appears to be a partial plate - required format is '$inFormat', " . 
		"format found is '",scalar(@{$lookupData}),"'\n";
	}

	## Store the lookup data in the Requirement object.
	$requirementObj->setDataStore($lookupData);
	return $requirementObj;
    }


    #################################################################
    ## Static method to find the SNP id and description of an EP plate.
    #################################################################
    sub getType {
	my $entityTypeId = shift;
	croak "ERROR: this method is static.\n" if ref($entityTypeId);
	my ($plateType,$plateDesc);
      SWITCH: for($entityTypeId) {
	  /20/ && do { $plateType = 1; $plateDesc = "sense";     last; };
	  /21/ && do { $plateType = 2; $plateDesc = "antisense"; last; };
	  /22/ && do { $plateType = 3; $plateDesc = "mixed";     last; };
	  $plateType = undef;
      }
	return($plateType,$plateDesc);
    }


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

1;
