=head1 NAME

iPE::State - the state class, containing references all information about parameters for a given state

=head1 DESCRIPTION

This is a container class for the many things related to a state.

=item FUNCTIONS

=over 8

=cut

package iPE::State;
use iPE;
use iPE::Globals;
use iPE::State::PseudoState;
use iPE::State::Transitions;
use iPE::Model::EmissionChain;

use base("iPE::XML::Object");
use strict;

=item new (attributes)

States are not constructed outside of gHMM.  They should be constructed based on an XML file or a parameter file.

=cut

sub new 
{
    my $class = shift;
    my $this = $class->SUPER::new(@_);
	my($tag, $m) = @_;

# could check for all of these being valid, but we can trust the xml checking
# to determine before we get to this point that these values are determined.

	$this->{name_}              = $m->{name};

    $this->{initISOs_}          = undef;
    $this->{initials_}          = undef;
    $this->{fixedInitLen_}      = undef;
    $this->{freeInitial_}       = 1; 
        # if this is a free parameter in the inital probability model

	$this->{strand_}            = $m->{strand};
    die "State $this->{name_} has invalid strand $this->{strand_}\n".
        "All strand values must be +, -, or N.\n"
        if($this->{strand_} ne '+' &&
           $this->{strand_} ne '-' &&
           $this->{strand_} ne 'N');
	$this->{startFrame_}        = $m->{start_frame};
	$this->{endFrame_}          = $m->{end_frame};
    if(!defined($this->{startFrame_}))  { $this->{startFrame_} = "N" }
    if(!defined($this->{endFrame_}))    { $this->{endFrame_}   = "N" }

    $this->{frameName_}         = $m->{frame_name};
    if(!defined($this->{frameName_})) {
        { $this->{frameName_} = "0"; }
    }
	$this->{pseudoStates_}      = [];
    $this->{type_}              = $m->{type};
    $this->{initModelName_}     = $m->{init_model};
    $this->{seqRegionName_}     = $m->{seq_model};  # these should probably be deprecated.
    $this->{consRegionName_}    = $m->{cons_model}; # since any number of different kinds of
                                                    # sequences can be used, it doesn't make
                                                    # sence to have static names for them. hmm...
    $this->{durRegionName_}     = $m->{dur_model};

    $this->{canOverlap_}        = $m->{can_overlap};
    if(!defined($this->{canOverlap_})) {
        $this->{canOverlap_} = 1;
    }

    $this->{durModel_}          = undef;

    my $g = new iPE::Globals();
    for my $type (@{$g->seqtypes}) {
        $this->{$type."Emis_"} = new iPE::Model::EmissionChain($this->{name_});
    }

    $this->{transISOs_}         = undef;
    $this->{transitions_}       = new iPE::State::Transitions($m->{name},
                                                         $m->{transitions});

    return $this;
}

sub name { return shift->{name_}}

sub initials { return shift->{initials_} }
sub initISOs { return shift->{initISOs_} }

sub strand { return shift->{strand_}}
sub startFrame { return shift->{startFrame_}}
sub endFrame { return shift->{endFrame_}}
sub frameName { return shift->{frameName_} }

sub canOverlap { return shift->{canOverlap_} }

sub transitions { return shift->{transitions_} }

sub pseudoStates { return shift->{pseudoStates_} }
sub type { return shift->{type_} }
sub initModelName { return shift->{initModelName_} }
sub seqRegionName { return shift->{seqRegionName_} }
sub consRegionName { return shift->{consRegionName_} }
sub durRegionName {return shift->{durRegionName_} }
sub durModel {return shift->{durModel_} }

#XXX this is deprecated.
sub seqEmis    { shift->{dnaEmis_}    }
#XXX these could be autoloaded.
sub dnaEmis    { shift->{dnaEmis_}    }
sub consEmis   { shift->{consEmis_}   }
sub tileEmis   { shift->{tileEmis_}   }
sub estEmis    { shift->{estEmis_}    }
sub repEmis    { shift->{repEmis_}    }
sub colaEmis   { shift->{colaEmis_}    }
sub alignEmis  { shift->{alignEmis_}  }
sub malignEmis { shift->{malignEmis_} }

#sub entryPoint { return shift->{entryPoint_} }
#sub exitPoint  { return shift->{exitPoint_} }

=item getModels (type)

Get the EmissionChain object for the type of sequence passed.

=cut
sub getModels {
    my ($this, $type) = @_;

    my $ret = $this->{$type."Emis_"};

    die "Unknown sequence type $type\n" if(!defined($ret));
    return $ret;
}

sub init {
    my ($this) = @_;

    my $g = new iPE::Globals();
    for my $chainName (@{$g->seqtypes}) {
        $this->{$chainName."Emis_"}->init;
    }
}

sub outputPrepare {
    my ($this, $out, $mode) = @_;

    $out->addWord("State::name", $this->name);
    $out->addWord("State::type", $this->type);
    $out->addWord("State::frame", $this->frameName);
    $out->addWord("State::seqRegionName", $this->seqRegionName);
    $out->addWord("State::durRegionName", $this->durRegionName);

    for my $pseudostate (@{$this->pseudoStates}) {

        my $pseudoframe = $pseudostate->frameName;
        $pseudoframe = 0 if $pseudoframe eq "N";

        $out->addWord("State::name", $pseudostate->name);
        $out->addWord("State::frame", $pseudoframe);
    }

    # set the initials in the XML document.
    my $initString = "";
    if(defined($this->initISOs)) {
        for my $level (@{$this->initISOs->levels}) {
            if(defined($this->initials)) {
                $initString .= $out->floatf($this->{initials_}->{$level})." ";
            }
            else { 
                $initString .= $out->floatf(0)." ";
            }
        }
    }
    $this->setAttribute("init_probs", $initString);
}

sub outputZoe {
    my ($this, $out) = @_;

    my $strand = $this->strand;
    $strand = '+' if ($strand eq 'N');
    $out->print($out->wordf("State::name", $this->name),
                $out->wordf("State::type", $this->type),
                $strand." ",
                $out->wordf("State::frame", $this->frameName),
                $out->wordf("State::durRegionName", $this->durRegionName),
                $out->wordf("State::seqRegionName", $this->seqRegionName));


    if(defined $this->initISOs) {
        for my $initISO (@{$this->initISOs->levels}) {
            if(defined ($this->initials)) {
                $out->print ($out->floatf($this->initials->{$initISO}));
            }
            else { $out->print ($out->floatf(0)); }
        }
    }
    $out->print ("\n");

    for my $pseudostate (@{$this->pseudoStates}) {

        my $pseudoframe = $pseudostate->frameName;
        $pseudoframe = 0 if ($pseudoframe eq "N");

        $out->print ($out->wordf("State::name", $pseudostate->name).
                     $out->wordf("State::type", $this->type).
                     $this->strand." ".
                     $out->wordf("State::frame", $pseudoframe),
                     $out->wordf("State::durRegionName", $this->durRegionName).
                     $out->wordf("State::seqRegionName", $this->seqRegionName));

        for my $initISO (@{$this->initISOs->levels}) {
            if(defined ($this->initials)) {
                $out->print ($out->floatf($this->initials->{$initISO}));
            }
            else { $out->print ($out->floatf(0)); }
        }
        $out->print ("\n");
    }
}

sub addPseudoState 
{
	my ($this, $pseudoState) = @_;

    for my $ps (@{$this->pseudoStates}) {
        die __PACKAGE__.": Pseudostate ".$ps->name." defined twice for state "
            .$this->name."\n" if($ps->name eq $pseudoState->name);
    }

	push @{$this->pseudoStates}, $pseudoState;
}

# -- Initial probabilities

sub initInits {
    my ($this, $ISO) = @_;

    die "Reuse of state for initial probability in state ".$this->{name_}.
        " in parameter template\n" if defined $this->{initials_};
    $this->{initISOs_} = $ISO;
}

sub setInitials {
    my ($this, @vals) = @_;

    die "Wrong number of initial probabilities for state $this->{name_}.\n"
        if scalar(@vals) != scalar(@{$this->initISOs->levels});
    for(@{$this->initISOs->levels}) {
        $this->{initials_}->{$_} = shift @vals;
    }
}

sub setDurModel    { $_[0]->{durModel_} = $_[1] }

sub addModel {
    my ($this, $emis) = @_;
    my $chain = $this->getModels($emis->source);
    $chain->addEmission($emis);
}

sub countRegion {
    my ($this, $region) = @_;
    my $chain = $this->getModels($region->seq->type);
    $chain->countRegion($region);
}

=back

=head1 SEE ALSO

L<iPE::gHMM>

=head1 AUTHOR

Bob Zimmermann (rpz@cse.wustl.edu)

=cut

1;
