package APE::FlushController::DN_FlushController;

use base 'APE::FlushController';

use strict;
use warnings;
use Carp;
use SqlEngine2;
use Class::Throwable;

use APE::FlushController::Result;
use APE::BarcodeFactory::DN_BarcodeFactory;

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

use constant LOGIN                 => 'snp';
use constant ROLE_ID               => '23342';
use constant TEAM                  => '71';
use constant DNA_PLATE_STATUS      => 1; # Created.
use constant DNA_WELL_CONTENT_TYPE => 1; # contents_dict.id_dict@tsnp = DNA_TUBE.

$Class::Throwable::Debug = 0;
$SqlEngine2::verbosity   = 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};
	    }
	}   
	return $self->SUPER::_init(%args);
    }

    
    #################################################################
    ## Start a new transaction.
    #################################################################
    sub beginTransaction {
    	my $self = shift;
    	$object{ $self }->{sqlEngine} = new SqlEngine2();
    	$object{ $self }->{dbh} = SqlEngine2::getDbh(LOGIN);
    	$object{ $self }->{sqlEngine}->beginTransaction( SqlEngine2::getDbh(LOGIN) );
    }
    
    
    #################################################################
    ## Over-ridden method from FlushController base class.
    #################################################################
    sub flush {
	my $self = shift;
	
	my $entity      = $self->getArrayingEntity();
	my $user        = ${$self->getUserObj};
	my $parentProcessId = $entity->getParentProcessId();
	my $outFormat       = $entity->getOutFormat();
	my $sessionId       = $entity->getSessionId();
	my $programName     = $self->getProgramName();

	my $team   = $user->getWorkingTeam()->getTeamName();
	my $roleId = $user->getWorkingTeam()->getWorkingRole()->getRoleId();

	croak "ERROR: there is no working team set.\n" unless $team;
	croak "ERROR: there is no working role set.\n" unless $roleId;
	croak "ERROR: there is no sessionId set.\n"    unless $sessionId;
	croak "ERROR: there is no programName set.\n"  unless $programName;

	my $idDnaPlate = eval { $self->createDnaPlate(sessionId => $sessionId,
						      outFormat => $outFormat,
						      team      => $team,
						      status    => DNA_PLATE_STATUS,
						      roleId    => $roleId,
						      program   => $programName); };
	if($@) { croak $@; }
	
	my @summary;
	my $dnaName;

	foreach my $atomicProcess (@{$entity->getAtomicProcessList()}) {
	    my $inDataId = $atomicProcess->getInDataId();
	    my $aph = $atomicProcess->getArrayPatternHash();
	    foreach my $inMapId (keys(%{$aph})) {
		foreach my $arrayPattern (sort{$a->getOutMapId() cmp $b->getOutMapId()} @{$aph->{$inMapId}}) {
		    my $inFormat = $arrayPattern->getInFormat();
		    my $inRow    = $arrayPattern->getInRow();
		    my $inCol    = $arrayPattern->getInColumn();
		    my $outRow   = $arrayPattern->getOutRow();
		    my $outCol   = $arrayPattern->getOutColumn();
		    my $outMapId = $arrayPattern->getOutMapId();
		    foreach my $outData (@{$arrayPattern->getOutData()}) {
			## Create a new well id.
			my $idDnaWell = eval { $self->getNextDnaWell(); };
			if($@) { croak "ERROR: can't get new id_dnawell: $@\n"; }
			## Get the out data - this was gathered by the
			## InputController getData method and is
			## specific to creating a DN.
			my $dbInFormat      = $outData->[0];
			my $dbInMapId       = $outData->[1];
			my $dbIdContent     = $outData->[2];
			my $wellContentType = $outData->[5];
			## Capture the DNA being put into the first
			## well of the target plate.
			if($outRow . $outCol eq 'a1') {
			    $dnaName = $outData->[3];
			}
			## Store summary data.
			push( @summary, [$sessionId,$idDnaPlate,$inDataId,$dbInFormat,
					 $inFormat,"$inRow$inCol","$outRow$outCol",$outMapId,
					 $dbIdContent,$wellContentType,"@$outData"] );
			## Create the new well with contents.
			eval  { $self->newDnaWell($idDnaWell,$idDnaPlate,$outMapId,
						  $dbIdContent,$wellContentType); };
			if($@){ croak "ERROR: can't create new dna well: $@\n"; }
		    }
		}
	    }
	    ## Populate the exo_process table with parentage data.
	    $self->populateExoProcess(-sqlEngine    => $object{ $self }->{sqlEngine}, 
				      -dbh          => $object{ $self }->{dbh},
				      -processId    => $atomicProcess->getProcessId,
				      -inputEntity  => $inDataId,
				      -inputPrefix  => $atomicProcess->getInPrefix, 
				      -outputId     => $idDnaPlate, 
				      -outputPrefix => 'DN');
	}
	
	## Delegate the creation of the description to the
	## BarcodeFactory - god knows what it'll produce!
	my $barcodeFactory = new APE::BarcodeFactory::DN_BarcodeFactory
	    (flushController => $self,
	     processId       => $parentProcessId,
	     entityId        => $idDnaPlate,
	     reservedData    => [$dnaName]);
	
	my $barcodeObj = $barcodeFactory->getInstance; ## Returns a ConcreteBarcode object.
	my $barcode    = $barcodeObj->createBarcode;   ## Object creates barcode formatting.

	## Return a newly created Result object.
	return new APE::FlushController::Result(sessionId       => $sessionId,
						id              => $idDnaPlate,
						type            => 'DN',
						description     => $barcode,
						'format'        => $outFormat,
						report          => $self->createReport(\@summary),
						flushController => \$self);
    }


    #################################################################
    ## Method to output a sorted summary of the insertion.
    #################################################################
    sub createReport {
	my ($self,$summary) = @_;
	my @report=();
	push(@report,sprintf("%-13s %-13s %-10s %-12s %-12s %-8s %-9s %-9s %-11s %-13s %-12s\n",
			     "SessionId","DnaPlatId","Tube","DbInFormat","InFormat","InCoord",
			     "OutCoord","OutMapId","DbIdContent","ContentType","OtherData"));
	push(@report,sprintf("%-13s %-13s %-10s %-12s %-12s %-8s %-9s %-9s %-11s %-13s %-12s\n",
			     "-"x13,"-"x13,"-"x10,"-"x12,"-"x12,"-"x8,"-"x9,"-"x9,"-"x11,"-"x13,"-"x12));
	foreach my $line (sort{ $a->[7] <=> $b->[7]  } @{$summary}) {
	    push(@report,sprintf("%-13s %-13s %-10s %-12s %-12s %-8s %-9s %-9s %-11s %-13s %-12s\n",
				 $line->[0],$line->[1],$line->[2],$line->[3],$line->[4],
				 $line->[5],$line->[6],$line->[7],$line->[8],$line->[9],
				 $line->[10]));
	}
	return \@report;
    }


    #################################################################
    ## Method to create a new DNA plate with a status.
    #################################################################
    sub createDnaPlate {
	my ($self,%options) = @_;
	croak "ERROR: argument outFormat is missing." unless $options{outFormat};
	croak "ERROR: argument team is missing."      unless $options{team};
	croak "ERROR: argument status is missing."    unless $options{status};
	croak "ERROR: argument roleId is missing."    unless $options{roleId};
	croak "ERROR: argument program is missing."   unless $options{program};
	croak "ERROR: argument sessionId is missing." unless $options{sessionId};
	## Get the next id_dnaplate.
	my $idDnaPlate = eval { $self->getNextDnaPlateId(); };
	if($@) { croak "ERROR: can't get new id_dnaplate: $@\n"; }
	## Create the new DNA plate.
	eval  { $self->newDnaPlate($idDnaPlate,$options{outFormat},$options{team}); };
	if($@){ croak "ERROR: can't create new dnaplate: $@\n";  }
	## Create a new DNA plate status.
	eval  { $self->newDnaPlateStatus($idDnaPlate,$options{status},$options{roleId},
					 $options{sessionId},$options{program}); };
	if($@){ croak "ERROR: can't create new dnaplate: $@\n";  }
	warn "Created DNA plate: $idDnaPlate\n";
	return $idDnaPlate;
    }
    

    #################################################################
    ## Method to get the next id_dnaplate from the sequence.
    #################################################################
    sub getNextDnaPlateId {
    	my $self = shift;
    	my $idDnaPlate = 
    	    eval { $object{ $self }->{sqlEngine}
    		   ->getRow('ExoSeq::next_dnaplate',
    			    [],$object{ $self }->{dbh});};
    	
    	if($@) { croak $@;           }
    	else   { return $idDnaPlate; }
    }
    
    
    #################################################################
    ## Method to get the next id_dnawell from the sequence.
    #################################################################
    sub getNextDnaWell {
    	my $self = shift;
    	my $idDnaWell = 
    	    eval { $object{ $self }->{sqlEngine}->getRow('ExoSeq::next_dnawell',
    							 [],$object{ $self }->{dbh}); };
    	if($@) { croak $@;          }
    	else   { return $idDnaWell; }
    }
    
    
    #################################################################
    ## Method to create a new DNA plate.
    #################################################################
    sub newDnaPlate {
    	my ($self,$idDnaPlate,$format,$team) = @_;
    	eval  { $object{ $self }->{sqlEngine}->do('ExoSeq::insert_dnaplate',
    						  [$idDnaPlate,$format,$team], 
    						  $object{ $self }->{dbh}); };
    	if($@) { croak $@; }
    }
    

    #################################################################
    ## Method to create a new DNA plate status.
    #################################################################
    sub newDnaPlateStatus {
    	my ($self,$idDnaPlate,$status,$idRole,$sessionId,$program) = @_;
    	eval { $object{ $self }->{sqlEngine}->do('ExoSeq::insert_dnaplatestatus',
    						 [$idDnaPlate,$status,1,$idRole,$sessionId,$program],
    						 $object{ $self }->{dbh}); };
    	if($@) { croak $@; }
    }


    #################################################################
    ## Method to create a DNA well in a DNA plate.
    #################################################################
    sub newDnaWell {
    	my ($self,$idDnaWell,$idDnaPlate,$idMap,$idContent,$contentType) = @_;
	warn "Attempting to insert: $idDnaWell -> $idDnaPlate -> $idMap -> $idContent -> $contentType\n";
    	eval { $object{ $self }->{sqlEngine}->do('ExoSeq::insert_dnawell',
    						 [$idDnaWell,$idDnaPlate,$idMap,$idContent,$contentType],
    						 $object{ $self }->{dbh}); };
    	if($@) { croak $@; }
    }

    #################################################################
    ## Over-ridden method from FlushController base class.
    #################################################################
    sub rollbackTransaction {
    	my $self = shift;
    	$object{ $self }->{sqlEngine}->rollbackTransaction( $object{ $self }->{dbh} );
    }


    #################################################################
    ## Over-ridden method from FlushController base class.
    #################################################################
    sub commitTransaction {
    	my $self = shift;
    	$object{ $self }->{sqlEngine}->commitTransaction( $object{ $self }->{dbh} );
    	#$self->rollbackTransaction();
    }
    

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

1;
