package APE::FlushController;

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

use constant TRUE  => 1;
use constant FALSE => 0;
use constant PROCESS_LOGIN => 'gt_process';

{
    my %_attributes = (
		       arrayingEntity   => undef,
		       userObj          => undef,
		       outputController => undef,
		       programName      => undef,
		       );

    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} }
    }

    
    #################################################################
    ## Constructor method for this object. Returns a new
    ## FlushController object.
    #################################################################
    sub new {
	my ($proto,%args) = @_;
	
	my $class = ref($proto) || $proto;
	my $self  = bless {}, $class;
	
	%args = $self->_init(%args);
	
	my @leftover = keys %args;
	
	confess "Error: the following arguments are unknown - @leftover\n" if @leftover;
	
	return $self;
    }


    #################################################################
    ## 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};
	    }
	}

	## These arguments are required by this object and the objects
	## inheriting from it.
	foreach my $arg (qw/arrayingEntity userObj outputController programName/) {
	    unless(defined $object{ $self }->{$arg}) {
		croak "ERROR: argument '$arg' is missing."
	    }
	}

	## Strip the program name down to just the name.
	$object{ $self }->{programName} =~ s!.*/([^/]+)$!$1!;

	return %args;
    }

    
    #################################################################
    ## Method to flush the arrayed data to STDOUT. 
    #################################################################
    sub flush {
	my $self = shift;
	eval { 
	    my $entity    = $object{ $self }->{arrayingEntity};
	    my $user      = ${$object{ $self }->{userObj}};
	    my $sessionId = $entity->getSessionId();
	    my $atomicProcessCount = 0;
	    warn "Flushingx as user: ". $user->getEmail().
	    " Team: ". $user->getWorkingTeam()->getTeamName().
	    " RoleId: ". $user->getWorkingTeam()->getWorkingRole()->getRoleId().
	    " RoleType: ". $user->getWorkingTeam()->getWorkingRole()->getRoleType(). "\n";
	    warn "Creating a '". $entity->getOutDescription(). "' (".$entity->getOutPrefix(). ") entity - " .
		"sessionId: $sessionId ....\n";
	    #warn "**** ArrayingEntity has nothing to rearray ****\n", return if !$entity->hasArrayingData();
	    foreach my $atomicProcess (@{$entity->getAtomicProcessList()}) {
		#warn "**** AtomicProcess has nothing to array ****\n", next if !$atomicProcess->hasArrayingData();
		my $aph = $atomicProcess->getArrayPatternHash();
		my $arrayPatternCount = 0;
		foreach my $inMapId (keys(%{$aph})) {
		    foreach my $arrayPattern (sort{$a->getOutMapId() <=> $b->getOutMapId()} @{$aph->{$inMapId}}) {
			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()}) {
			    warn "\tInDataId: ". $atomicProcess->getInDataId() . 
				" -> In '$inRow$inCol' to Out '$outRow$outCol' with data '@$outData'\n";
			}
			$arrayPatternCount++;
		    }
		}
		$atomicProcessCount++;
		warn "\tArrayPattern count: $arrayPatternCount\n";
	    }
	    warn "AtomicProcess count: $atomicProcessCount\n";
	};
	if($@) { croak $@; }
    }
    
    
    #################################################################
    ## Method offered to subclasses. Further functionality to come.
    #################################################################
    sub beginTransaction    {}
    sub rollbackTransaction {}
    sub commitTransaction   {}
    sub getDescription      { die "ERROR: abstract method called - not implemented in sub class."; }


    #################################################################
    ## Method to populate the snp.exo_process table. This table holds
    ## data corresponding to an APE flush so that the parentage of a 
    ## created entity can be traced, including the tract that it was
    ## created in.
    #################################################################
    sub populateExoProcess {
	my ($self,%arguments) = @_;
	warn "*** MAKING NEW ENTRY TO EXO_PROCESS ***\n";
	my %args = ();

	## Supplying an input or output entity overrides the id and prefix
	## arguments if the argument is a proper entity - otherwise the prefix
	## and id are left to fend for themselves in the next block.
	foreach(qw/input output/) {
	    my $key = "-".$_."Entity";
	    warn "*** Checking inputEntity and outputEntity arguments using key: $key ***\n";
	    if(exists($arguments{$key}) && $arguments{$key} =~ /^(\w\w)(\d+)/) {
		throw Class::Throwable::Simple("ERROR: pattern match failed on $key: $arguments{$key} -> '$1' '$2'")
		    unless $1 && $2;
		$arguments{"-".$_."Prefix"} = $1;
		$arguments{"-".$_."Id"}     = $2;
		delete $arguments{$key};
		warn "*** Split '$key' to '".$arguments{"-".$_."Id"}."' -> '".
		    $arguments{"-".$_."Prefix"}."' ***\n";
	    }
	}

	## Verify the passed arguments.
	foreach(qw/-sqlEngine -dbh -processId -inputId -inputPrefix -outputId -outputPrefix/) {
	    throw Class::Throwable::Simple("ERROR: argument '$_' is missing")
		unless $arguments{$_};
	    $args{$_} = delete $arguments{$_};
	    warn "$_ = $args{$_}\n";
	}

	## An unknown option has been passed - oops!
	croak "ERROR: unknown option '",join(",",keys %arguments),"'\n"
	    if keys %arguments;

	## Get the next id and make the insert into the exo_process table.
	my $nextExoProcessId = $self->_getNextExoProcessId($args{-sqlEngine},$args{-dbh}); 
	warn "Using id_exo_process: $nextExoProcessId\n";
	$args{-sqlEngine}->do('ExoSeq::insert_exo_process',
			      [$nextExoProcessId,$args{-processId},$args{-inputId},
			       $args{-inputPrefix},$args{-outputId},$args{-outputPrefix}],
			      $args{-dbh});
    }
    

    #################################################################
    ## Method to get the next primary key id from the sequence used
    ## on the exo_process table.
    #################################################################
    sub _getNextExoProcessId {
	my ($self,$sqlEngine,$dbh) = @_;
	my $nextExoProcessId = 
	    eval { $sqlEngine->getRow('ExoSeq::next_exo_process',
				      [],$dbh); };
	croak $@ if $@;
	return $nextExoProcessId;
    }

       
    #################################################################
    ## Method to destory this object.
    #################################################################
    sub DESTROY { 
	my $self = shift;
	delete $object{ $self };
    }
}

1;
