=head1 NAME

iPE::Instance - an instance of parameter estimation, defined by the iPE_instance XML file.

=head1 DESCRIPTION

Each running instance of iPE defines a set of options, sequences, annotations, feature maps, null definitions, and a gHMM.  The most basic type of instance is the run instance which runs all the sequences and annotations on a single processor.

A thread or job instance defines a set of sequences and annotations which will be split up to be run on separate threads or nodes in a queue.  If a job instance is submitted to iPE, it will be divided up into files and output paths, and a jobfile is created.  The user will then have to manually submit the job to a queue.  A thread instance will run on separate threads on the same machine, splitting up the sequences and annotations as in the job instance.


Both thread and job instances will have a special running instance called a gather instance, which will gather the files from the subinstances and add all the counts together and convert all the numbers into scores.

These are defined in XML files.  This class inherits from the iPE::XML::DocObject class.

=head1 FUNCTIONS

=over 8

=cut

package iPE::Instance;
use iPE;
use iPE::Globals;
use iPE::Options;
use iPE::Estimator;
use base ("iPE::XML::DocObject");
use strict;

sub _set { chomp ($_[2]); $_[0]->{$_[1]} = $_[2]; }
sub handle_subelement {
    my $this = shift;
    my ($tag, $att, $data, $element) = @_;

    for($tag) {
        if(/^author$/)              { $this->_set("author_", $data)         }
        elsif(/^date$/)             { $this->_set("date_", $data)           }
        elsif(/^type$/)             { $this->_set("type_", $data)           }
        elsif(/^gHMM_file$/)        { $this->_set("gHMMFile_", $data)       }
        elsif(/^feature_map_files$/){ 
                               $this->_set_files("featureMap", $att, $data)   }
        elsif(/^annotation_files$/) { $this->_set_files("ann", $att, $data)   }
        elsif(/^dna_files$/)        { $this->_set_files("dna", $att, $data)   }
        elsif(/^conseq_files$/)     { $this->_set_files("cons", $att,$data)   }
        elsif(/^tileseq_files$/)    { $this->_set_files("tile", $att,$data)   }
        elsif(/^estseq_files$/)     { $this->_set_files("est", $att, $data)   }
        elsif(/^repseq_files$/)     { $this->_set_files("rep", $att, $data)   }
        elsif(/^asestseq_files$/)   { $this->_set_files("asest", $att, $data) }
        elsif(/^cola_files$/)       { $this->_set_files("cola", $att, $data)  }
        elsif(/^align_files$/)      { $this->_set_files("align",$att,$data)   }
        elsif(/^malign_files$/)     { $this->_set_files("malign",$att,$data)  }
        elsif(/^seq_files$/)        {$this->_set_files($att->{type},$att,$data)}
        elsif(/^options$/)          { $this->_set_options(@_)                 }
        else { Warn("Unrecognized tag: $tag\n");                              }
    }
}

sub _set_files {
    my ($this, $type, $att, $names) = @_;

    my $basedir;
    if(defined ($att->{basedir})) { 
        $basedir = $att->{basedir};
        die __PACKAGE__.": No such directory $att->{basedir} for type $type.\n" 
            if(! -d $att->{basedir});
    }
    my @names_arr = split ' ', $names;
    for my $name (@names_arr) {
        my $full_name = "";
        $full_name .= $basedir."/" if defined $basedir;
        $full_name .= $name;
        push @{$this->{$type."Files_"}}, $full_name;
    }
}

sub _set_options {
    my $this = shift;
    $this->{options_} = new iPE::Options(@_);
}

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

    my $g = new iPE::Globals();
    $g->options($this->{options_});
    $g->init_fhs();

    $this->validateOptions();
}

=item run ()

After new ()ing and parse_file ()ing an instance file, you can call this function to run the estimator based on the instance created.  At this point, all sanity checks have been performed on the instance.

=cut
sub run {
    my ($this) = @_;
    my $g = new iPE::Globals();

    my $member_hash = { gHMMFile          => $this->{gHMMFile_},
                        featureMapFiles   => $this->{featureMapFiles_},
                        instanceFilename  => $this->filename,
                        annotationFiles   => $this->{annFiles_} };
    for my $type (@{$g->seqtypes}) {
        $member_hash->{$type."Files"} = $this->{$type."Files_"}
            if(defined($this->{$type."Files_"}));
    }
    $this->{estimator_} = new iPE::Estimator( $member_hash );

    $this->{estimator_}->run;
}

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

    my $g = new iPE::Globals();
    die __PACKAGE__.": No such directory ".$g->options->outputBaseDir.".\n"
        if(! -d $g->options->outputBaseDir);
    my $numPerType = scalar(@{$this->{"annFiles_"}});
    for my $type (@{$g->seqtypes}) {
        next if (!defined ($this->{$type."Files_"}));
        if(scalar(@{$this->{$type."Files_"}}) != $numPerType) {
            die __PACKAGE__.": Number of $type files differs.\n";
        }
        for my $file (@{$this->{$type."Files_"}}) {
            die __PACKAGE__.": No such file $file.\n" if(! -f $file);
        }
    }

    die("N-SCAN requires defining the nscanModel and nscanTopology options.\n")
      if(defined($this->{malignFiles_}) && scalar(@{$this->{malignFiles_}}) && 
        (length($this->options->nscanModel) == 0 || 
         length($this->options->nscanTopology) == 0)) 
}

sub AUTOLOAD {
    my $this = shift;
    my $member = our $AUTOLOAD;
    return if $member =~ /::DESTROY$/;
    $member =~ s/.*:://;

    if(scalar(@_)) {
        my $value = shift;
        $this->{$member."_"} = $value;
    }
    else {
        return $this->{$member."_"} if defined $this->{$member."_"};
        return "0";
    }
}

=back

=head1 SEE ALSO

L<iPE::XML::DocObject>

=head1 AUTHOR

Bob Zimmermann (rpz@cse.wustl.edu)

=cut

1;
