package APE::InputControllerFactory::ConcreteInputController::COORD2PLATE_InputController;

use base
'APE::InputControllerFactory::ConcreteInputController::DefaultExoApeInputController';

$APE::InputControllerFactory::ConcreteInputController::COORD2PLATE_InputController::VERSION = '1.1';

use strict;
use warnings;
use Carp;

use APE::InputControllerFactory::ConcreteInputController::Requirement;
use APE::InputControllerFactory::ConcreteInputController::RequirementContainer;

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 adds a Requirement object to "
          . "the requirementList. The new Requirement object is type 'Input' and "
          . "requires the user to enter a plate well coord.";

        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();
        $self->SUPER::createRequirements();
        my $inputRequirementContainer = new
          APE::InputControllerFactory::ConcreteInputController::RequirementContainer(
            outDescription => "Well co-ordinate" );

        $inputRequirementContainer->pushRequirement
			(new APE::InputControllerFactory::ConcreteInputController::Requirement
				(type         => 'Input',
				 description  => 'Enter well coord [rowcol]: ',
                 checkingCode => qr{^[a-h]([1-9]|1[012])$}));

        $self->unshiftRequirementContainer($inputRequirementContainer);
    }

    #################################################################
    ## Overridden method from base class to process a requirement
    ## object. In the coord stored in the Input Requirement object
    ## is stored in this object and used when each of the Requirement
    ## Container objects are arrayed.
    #################################################################
    sub process {
        my ( $self, $requirementObj ) = @_;
        ## Set the requested coord if this is the input Requirement
        ## object.
        if ( $requirementObj->getType eq 'Input' ) {
            $object{$self}->{coord} = $requirementObj->getData;
            return;
        }

        ## Call supers process method that will call this objects
        ## getData method if the Requirement object is not type Input.
        eval { $self->SUPER::process($requirementObj); };
        if ($@) { croak $@; }
    }

    #################################################################
    ## Method to array the data stored in the Requirement objects in
    ## the passed RequirementContainer object. This method over-rides
    ## the base class method of the same name so that the ArrayPattern
    ## objects can be modified to only store the contents of the
    ## selected coordinate.
    #################################################################
    sub arrayContainerData {
        my ( $self, $requirementContainer ) = @_;
        croak "ERROR: coord has not been set - the Input Requirement "
          . "has not been received by the process method."
          unless $object{$self}->{coord};
        warn "\n*** Using coord: " . $object{$self}->{coord} . " ***\n";
        eval {
            $self->_alterArrayPatterns
				($object{$self}->{coord},
                 $requirementContainer);
        };
        if ($@) { croak $@; }
        $self->SUPER::arrayContainerData($requirementContainer);
    }

    ## Method to alter each of the ArrayPattern objects in coordinate
    ## so that it matches the passed $coord. This new coordinate
    ## represents the selected coordinate.
    sub _alterArrayPatterns {
        my ( $self, $coord, $requirementContainer ) = @_;
        return if $requirementContainer->isInput;

        #warn "Altering ArrayPatterns.\n";
        foreach
          my $requirementObj ( @{ $requirementContainer->getRequirementList } )
        {
            next if $requirementObj->isInput;
            my $attachedList = $requirementObj->getAttachedList;
            print "**** Found '"
              . scalar(@$attachedList)
              . "' attached AtomicProcess objects\n";
            foreach my $atomicProcess (@$attachedList) {
                my $inFormat      = $atomicProcess->getInFormat;
                my $rowColMapData = $self->getRowColMapFromFormat($inFormat);
                ## Fetch then delete the hash of ArrayPattern objects from the
                ## AtomicProcess object.
                my $arrayPatternHash = $atomicProcess->deleteArrayPatternHash();
                ## Adjust the ArrayPattern object to show the new inMapId data.
                foreach my $mapId ( keys( %{$arrayPatternHash} ) ) {
                    foreach
                      my $arrayPattern ( @{ $arrayPatternHash->{$mapId} } )
                    {
                        print STDERR "Before AP($coord): "
                          . $arrayPattern->getInRow
                          . $arrayPattern->getInColumn . " -> "
                          . $arrayPattern->getOutRow
                          . $arrayPattern->getOutColumn . "\n";
                        my $mapData  = $rowColMapData->{coord}{$coord};
                        my $newIdMap = $mapData->[0];
                        my $newRow   = $mapData->[1];
                        my $newCol   = $mapData->[2];
                        $arrayPattern->setInCoords( $newIdMap, $newRow,
                            $newCol );
                        print STDERR "After AP($coord): "
                          . $arrayPattern->getInRow
                          . $arrayPattern->getInColumn . " -> "
                          . $arrayPattern->getOutRow
                          . $arrayPattern->getOutColumn . "\n";
                        $atomicProcess->addArrayPattern($arrayPattern);
                    }
                }
            }
        }
    }

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

1;
