package APE::InputController::RequirementContainer;

use strict;
use warnings;
use Carp;

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

{
    my %_attributes = (
		       outDescription           => undef,
		       requirementList          => [],
		       completedRequirementList => [],
		       linkedData               => [],
		       otherData                => []
		       );
    
    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};
	    }
	}
	
	return %args;
    }


    #################################################################
    ## Method to return a description of this RequirementContainer
    ## and the number of Requirement objects it contains.
    #################################################################
    sub getDescription {
	my $self = shift;
	my $reqCount = scalar(@{$object{ $self }->{requirementList}});
	return "$self containing '$reqCount' Requirement objects.";
    }


    #################################################################
    ## Method to set the description of the out data attribute.
    #################################################################
    sub setOutDescription {
	my ($self,$outDescription) = @_;
	$object{ $self }->{outDescription} = $outDescription;
    }


    #################################################################
    ## Method to test if this RequirementContainer only contains
    ## Requirement objects of type 'Input'.
    #################################################################
    sub isInput {
	my $self = shift;
	foreach my $requirement (@{$object{ $self }->{requirementList}}) {
	    if($requirement->getType ne 'Input') {
		return FALSE;
	    }
	}
	return TRUE;
    }


    #################################################################
    ## Method to add data to the linkedData attribute.
    #################################################################
    sub pushLinkedData {
	my ($self,$otherData) = @_;
	return push(@{$object{ $self }->{linkedData}},$otherData);
    }


    #################################################################
    ## Method to add data to the other data attribute.
    #################################################################
    sub pushOtherData {
	my ($self,$otherData) = @_;
	return push(@{$object{ $self }->{otherData}},$otherData);
    }


    #################################################################
    ## Method to clear the data held in the otherData attribute.
    #################################################################
    sub clearOtherData {
	my $self = shift;
	$object{ $self }->{otherData} = [];
    }

    
    #################################################################
    ## Method to clear the data held in the linkedData attribute.
    #################################################################
    sub clearLinkedData {
	my $self = shift;
	$object{ $self }->{linkedData} = [];
    }


    #################################################################
    ## Method to return data held at index index of the otherData
    ## attribute array.
    #################################################################
    sub getOtherDataAt {
	my ($self,$index) = @_;
	return $object{ $self }->{otherData}->[$index];
    }


    #################################################################
    ## Method to push a new Requirement object to the list.
    #################################################################
    sub pushRequirement { 
	my ($self,$requirementObj) = @_;
	return push(@{$object{ $self }->{requirementList}}, $requirementObj);
    }


    #################################################################
    ## Method to pop a Requirement object off the list.
    #################################################################
    sub popRequirement {
	my $self = shift;
	return pop(@{$object{ $self }->{requirementList}});
    }


    #################################################################
    ## Method to shift a Requirement object off the list.
    #################################################################
    sub shiftRequirement {
	my $self = shift;
	return shift(@{$object{ $self }->{requirementList}});
    }
    
 
    #################################################################
    ## Method to unshift (opposite of shift) a Requirement object to
    ## the list.
    #################################################################
    sub unshiftRequirement {
	my ($self,$requirementObj) = @_;
	return unshift(@{$object{ $self }->{requirementList}},$requirementObj);
    }


    #################################################################
    ## Method to insert a new Requirement object at index.
    #################################################################
    sub insertRequirement {
	my ($self,$requirementObj,$index) = @_;
	return splice(@{$object{ $self }->{requirementList}},$index,$index,$requirementObj);
    }


    #################################################################
    ## Method to delete a Requirement object at index.
    #################################################################
    sub deleteRequirement {
	my ($self,$index) = @_;
	return delete($object{ $self }->{requirementList}->[$index]);
    }


    #################################################################
    ## Method to delete a Requirement object at index.
    #################################################################
    sub deleteCompletedRequirement {
	my ($self,$index) = @_;
	return delete($object{ $self }->{completedRequirementList}->[$index]);
    }


    #################################################################
    ## Method to return boolean true if the Requirements have been made.
    #################################################################
    sub hasRequirements {
	my $self = shift;
	if(defined $object{ $self }->{requirementList}) {
	    return scalar(@{$object{ $self }->{requirementList}}) ? TRUE : FALSE;
	} return FALSE;
    }


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

1;
