=head1 NAME

iPE::Globals - Package containing global variables and routines for iPE.

=head1 DESCRIPTION

This package contains the functions to access and set global variables used in iPE.  In using iPE as a set of modules, you will need to create a front end to parse the commandline to create the iPE::Estimator, and to parse options such as verbosity level, debug level, and output files.

=head1 GLOBALS

=over 8

=cut

package iPE::Globals;
use iPE;
use Carp;
use IO::Handle;
use iPE::Options;
use base("Exporter");
use strict;

our @EXPORT = qw(msg Msg Warn);

our $instance = bless({
      gHMM_        => undef,
      bntree_      => undef,
      ssSeqNames_  => undef,
      states_      => undef,
      options_     => undef,
      levels_      => undef,

      warn_fh_     => _null_fh(),
      msg_fh_      => _null_fh(),
      dbg_fh_      => _null_fh(),

      default_warn_fh_     => *STDERR,
      default_msg_fh_      => *STDERR,
      default_dbg_fh_      => *STDERR,

      greeting_ => <<EOF

iParameterEstimation v$iPE::VERSION  $iPE::VERSION_DATE
Copyright (C) 2005-2007 Washington University in Saint Louis
by Bob Zimmermann and Brent Lab
http://mblab.wustl.edu

EOF
,

      seqtypes_    => ["dna", "cons", "cola", "tile", "array", "est", "asest", "rep", "align", "malign"],


      zoeIrregularSeqtypes_ => 
        { dna          => "DNA",
          cons          => "CONSEQ",
          cola          => "CONSEQ",
          asest         => "ESTSEQ",
           # BNTREE models display no sequence type in the header, however, 
           # the tag displays PHYLOGENETIC.  this is why we put this here
          malign        => "PHYLOGENETIC" 
          },

      irregularSeqclasses_ => { },

      sr_prefixes_ => { load         => "iPE::SequenceReader::Load::",
                             noload       => "iPE::SequenceReader::NoLoad::" },

      seqreaders_ =>  { fa           => "FASTA",
                             conseq       => "Raw",
                             align        => "Align" }

     
      
      }, __PACKAGE__);
sub new { $instance }

sub _null_fh { open(FH, ">/dev/null"); return *FH }

=item greeting ()

Returns the standard greeting for launching iPE.

=cut
sub greeting { shift->{greeting_} }

=item seqtypes ()

Return a reference to an array of sequence types.  These are all in iPE format.  To translate to zoe or class format see zoe_seqtype () and seqclass ()

=cut
sub seqtypes { shift()->{seqtypes_} }

=item seqtype_exists ()

Return 1 if the sequence type is in the seqtypes array.

=cut
sub seqtype_exists {
  my ($this, $name) = @_;

  for my $type (@{$this->seqtypes}) {
    return 1 if($type eq $name);
  }
  return 0;
}

=item zoe_seqtype (iPE_seqtype)

Translates the iPE seqtype into the corresponding zoe sequence type.  First looks to see if any zoeIrregularSeqtype has been defined for the passed iPE seqtype (e.g. dna => DNA, not DNASEQ), and if none is present, returns the uppercased sequence type followed by SEQ.  For example, if you passed "bob", you would get zoe_seqtype would return BOBSEQ.

=cut
sub zoe_seqtype 
  { $_[0]->{zoeIrregularSeqtypes_}->{$_[1]} || uc($_[1])."SEQ" }

=item seqclass (iPE_seqtype)

Returns the sequence class that defines the sequence type passed.  This is usually just the capitalized name preceeded by iPE::Sequence:: (e.g. cons => iPE::Sequence::Cons).  Looks for irregular sequence classes in the global irregularSeqclasses hash, and if none is there, translates as described.

=cut
sub seqclass    
  { $_[0]->{irregularSeqclasses_}->{$_[1]} || "iPE::Sequence::".ucfirst($_[1]) }

=item sr_prefix (type)

Return the sequence reader prefix for a type of sequence reader.  At time of writing this is only 'load' or 'noload'

=cut
sub sr_prefix { $_[0]->{sr_prefixes_}->{$_[1]} }

=item seqreader (filename-extension)

Return the sequence reader that handles the filename-extension given.

=cut
sub seqreader { $_[0]->{seqreaders_}->{$_[1]} }

=item gHMM ()

Returns the global iPE::gHMM object if it has been defined.  If it has not, any access to this function will cause the script to die.  The iPE::Estimator object constructs this object as its first object.

=cut
sub gHMM {
    my $this = shift();
    if(scalar(@_)) {
        $this->{gHMM_} = shift;
    }
    confess "Attempt to reference global gHMM object before defined.\n"
        unless(defined $this->{gHMM_});
    confess "Global gHMM object not an iPE::gHMM.\n" 
        if(ref($this->{gHMM_}) ne "iPE::gHMM");

    return $this->{gHMM_};
}

=item bntree ()

Returns the global iPE::Model::BNTree object.  This is only defined if the nscanModel and nscanTopology options are defined, and someone instantiates it (usually the iPE::Estimator object).

=cut
sub bntree        { 
    my $this = shift();
    if(scalar(@_)){ $this->{bntree_} = shift();           }
    return $this->{bntree_};
}


=item state (stateName)

Return the state in the gHMM object whose name is stateName.

=cut
sub state { 
    my ($this, $name) = @_;
    confess "No such state $name\n" unless(defined ($this->states()->{$name}));
    return $this->states()->{$name} 
}

=item states 

Return a hash reference to the states in the gHMM, keyed by state name.

=cut
sub states {
    my ($this) = @_;
    confess "Attempt to reference global states hash before defined.\n"
        unless(defined($this->gHMM()->states()));

    return $this->gHMM()->states();
}

=item ssSeqNames

Return a hash reference to the sequence names for the significant statistics files (for n-scan estimation).

=cut
sub ssSeqNames {
    my $this = shift();
    if(scalar(@_)) { $this->{ssSeqNames_} = shift; }
    return $this->{ssSeqNames_};
}

=item options

Return the iPE::Options object.  You may use this to check for any defined option by calling the name of the option, for example,
    $g->options->allowPartialTranscripts
returns 1 if partial transcripts are allowed, 0 otherwise.

=cut
sub options       { 
    my $this = shift();
    if(scalar(@_))                    { $this->{options_} = shift();           }
    elsif(!defined($this->{options_})){ $this->{options_} = new iPE::Options() }
    return $this->{options_};
}

=item levels

Return the global iPE::Levels object.  This includes the levels of the current sequence (which may be a bad idea to use) and the cumulative levels of all sequences input.

=cut
sub levels    {
    my $this = shift();
    if(scalar(@_)) { $this->{levels_} = shift(); }
    return $this->{levels_};
}

sub msg_fh  { shift()->{msg_fh_}  }
sub warn_fh { shift()->{warn_fh_} }
sub dbg_fh  { shift()->{dbg_fh_}  }

sub init_fhs {
    my ($this) = @_;
    return unless(defined($this->options));
    $this->{msg_fh_} = 
        _get_fh($this->options->messageOutputFile) || $this->{default_msg_fh_}
        if($this->options->verbose);
    $this->{warn_fh_} = 
        _get_fh($this->options->warningOutputFile) || $this->{default_warn_fh_}
        unless($this->options->supressWarnings);
    $this->{dbg_fh_} = 
        _get_fh($this->options->debugOutputFile) || $this->{default_dbg_fh_}
        if($this->options->debugOutput);
}

sub _get_fh {
    my $name = shift;
    return undef if ($name eq "");
    $name = $instance->options->outputBaseDir."/".$name 
        if(defined($instance) && defined($instance->options));
    my $fh;
    open($fh, ">$name") 
        or die (__PACKAGE__.":Could not open $name for writing.\n");
    $fh->autoflush();
    return $fh;
}

=back

=head1 FUNCTIONS

=over 8

=item msg (message), Msg (message), Warn (message)

Alert the user of an event or warn about an error.  Warnings can be plentiful, and are optionally left in a file.  The Msg function should be reserved for progress events and not used for errors.  All Msg calls are broadcast to all filehandles.

=cut
sub Msg  { $instance->{msg_fh_}->print(@_); 
           $instance->{dbg_fh_}->print(@_) if($instance->{dbg_fh_} ne *STDERR); }
sub msg  { $instance->{dbg_fh_}->print(@_)  }
sub Warn { $instance->{warn_fh_}->print(@_) }

=back

=head1 SEE ALSO

L<iPE::gHMM>, L<iPE::Options>, L<iPE::Estimator>

=head1 AUTHOR

Bob Zimmermann (rpz@cse.wustl.edu).

=cut

1;
