#!@@_perl_root_@@/bin/perl.exe
package Feature;

##########################################################
#                                                        #
# dictyBase Extension of Feature                         #
#                                                        #
##########################################################

use Feature_base;
use dictyBaseid;
use Display_seq;
use External_id;
use Locus;
use Chromosome;

use Bio::Seq;
use Bio::SeqIO;
use IO::String;
use Bio::Root::Root;

#
# will eventually properly put this as a globally configurable variable
#
my $schema = $ENV{'DBUSER'};

#
# keep a hash of chromosome objects globally as an implementation of singleton
#
my $chro_obj_hash = {};


BEGIN { %Feature:: = %Feature_base:: }

use Data::Dumper;

sub new {
   my ($type, %args) = @_;
   my $self;

   if ($args{'dictyBaseid'}) {
      my $dictyBaseid = dictyBaseid::->new( %args );

      if ( $dictyBaseid->tab_name eq 'FEATURE' ) {
         $self = $type->SUPER::new( feature_no => $dictyBaseid->primary_key, dbh => $args{'dbh'} );
         $self->dictyBaseid( $dictyBaseid->dictyBaseid );
      }
      else {
         die "did not pass real feature dictyBaseid";
      }
   }
   else {
      $self = $type->SUPER::new( %args );
      return if !$self;
   }
   $self->feature_types( $self->get_feature_types );
   $self->display_seqs( [] );
  #
  # initialize with original funciton of chromosome
  #
   $self->chromosome( $self->SUPER::chromosome );
   $self->external_ids( [] );

   return $self;
}


sub feature_types {
   my ($self, $obj) = @_;
   if($obj) {
   $self->{feature_types} = $obj;
   }
   return $self->{feature_types};
}


sub chr_bioperl {
   my ($self) = @_;

   return Bio::Seq->new('-seq'=>$self->chromosome_obj->chr_seq) if $self->chromosome;

}

sub display_seqs {
   my ($self, $obj) = @_;
   if($obj) {
      $self->{display_seqs} = $obj;
   }
   return $self->{display_seqs};
}

sub external_ids {
   my ($self, $obj) = @_;
   if($obj) {
      $self->{external_ids} = $obj;
   }
   return $self->{external_ids};
}

sub locus {
   my ($self, $obj) = @_;

  #
  # fetches locus from database (get_locus) if locus is not yet defined
  #
   defined $self->{locus} || defined $obj || $self->get_locus();

   if($obj) {
      $self->{locus} = $obj;
   }
   return $self->{locus};
}

sub chromosome_obj {
   my ($self, $obj) = @_;

  #
  # fetches chromosome from database (get_chromosome) if chromosome is not yet defined
  #
   defined $self->{chromosome_obj} || defined $obj || $self->get_chromosome();

   if($obj) {
      $self->{chromosome_obj} = $obj;
   }
   return $self->{chromosome_obj};
}

sub get_display_seq_nos{
   my ($self) = @_;

   my $sth = $self->dbh->prepare("
        SELECT DISPLAY_SEQ_NO
          FROM $schema.DISPLAY_SEQ
         WHERE FEATURE_NO = ?
   ");

   $sth->execute( $self->feature_no );
   my $records = $sth->fetchall_arrayref();
   $sth->finish();
   return $self->_first_column_arrayref( $records );
}

sub dictyBaseid {
   my ($self, $obj) = @_;

  #
  # fetches dictyBaseid from database (get_dictyBaseid) if dictyBaseid is not yet defined
  #
   defined $self->{dictyBaseid} || defined $obj || $self->get_dictyBaseid();

   if($obj) {
      $self->{dictyBaseid} = $obj;
   }
   return $self->{dictyBaseid};
}



sub get_display_seqs{
   my ($self) = @_;

die "Don't use 'get_display_seq' this method, not optimized-- use get_display_seq_by_type instead";

   my $featno = $self->feature_no;

   my $getSequences = $self->dbh->selectcol_arrayref("
        SELECT DISPLAY_SEQ_NO
          FROM $schema.DISPLAY_SEQ
         WHERE FEATURE_NO = $featno
   ");

   foreach $display_seq_no ( @{ $getSequences } ) {
      push @{ $self->display_seqs }, Display_seq->new( display_seq_no => $display_seq_no, dbh => $self->dbh );
   }
}

sub get_external_ids{
   my ($self) = @_;

   my $sth = $self->dbh->prepare("
        SELECT EXTERNAL_ID_NO
          FROM $schema.EXTERNAL_ID
         WHERE TAB_NAME    = 'FEATURE'
           AND PRIMARY_KEY = ?
   ");

   $sth->execute( $self->feature_no );
   my $records = $sth->fetchall_arrayref();

   my $getExternalId = $self->_first_column_arrayref( $records );

   foreach $external_id_no ( @{ $getExternalIds } ) {
      push @{ $self->external_ids }, External_id->new( external_id_no => $external_id_no, dbh => $self->dbh );
   }
      $sth->finish();
}

sub get_dictyBaseid{
   my ($self) = @_;

   my $featno = $self->feature_no;

   my $sth = $self->dbh->prepare("
        SELECT DICTYBASEID
          FROM $schema.DICTYBASEID
         WHERE TAB_NAME         = 'FEATURE'
           AND DICTYBASEID_TYPE = 'Primary'
           AND PRIMARY_KEY      = ?
   ");

   $sth->execute( $self->feature_no );
   my $records = $sth->fetchall_arrayref();

   my $getdictyBaseid = $self->_first_column_arrayref( $records );

   $self->dictyBaseid($getdictyBaseid->[0]);
      $sth->finish();

}


sub get_external_id_by_source {
   my ($self, $source) = @_;

   my $return_external_id;
   foreach my $external_id_obj ( @{ $self->external_ids } ) {
      if ( uc( $external_id_obj->source ) eq uc($source) ) {
         $return_external_id = $external_id_obj;
         last;
      }
   }
   return $return_external_id;
}


sub is_type {
   my ($self, $type) = @_;
   foreach my $feature_type ( @{ $self->feature_types } ) {
      return 1 if uc( $feature_type ) eq uc( $type );
   }
   return 0;
}

sub get_display_seq_by_type {
   my ($self, $sequence_type) = @_;

   my $sth = $self->dbh->prepare("
        SELECT DISPLAY_SEQ_NO
          FROM $schema.DISPLAY_SEQ
         WHERE FEATURE_NO = ?
           AND UPPER(DISPLAY_SEQ_TYPE) = ?
   ");

   $sth->execute( $self->feature_no, uc($sequence_type) );
   my $records = $sth->fetchall_arrayref();

   my $getSequences = $self->_first_column_arrayref($records);

   foreach $display_seq_no ( @{ $getSequences } ) {
      push @{ $self->display_seqs }, Display_seq->new( display_seq_no => $display_seq_no, dbh => $self->dbh );
   }
   my $return_seq;
   foreach my $display_seq_obj ( @{ $self->display_seqs } ) {
      if ( uc($display_seq_obj->display_seq_type) eq uc($sequence_type) ) {
         $return_seq = $display_seq_obj;
         last;
      }
   }
   $sth->finish();
   return $return_seq;
}

sub get_feature_types{
   my ($self, $type) = @_;

   my $sth = $self->dbh->prepare("
        SELECT FEATURE_TYPE
          FROM $schema.FEATURE_TYPE
         WHERE FEATURE_NO = ?
   ");

   $sth->execute( $self->feature_no );
   my $records = $sth->fetchall_arrayref();

   my $getTypes = $self->_first_column_arrayref( $records );
      $sth->finish();
   return $getTypes;

}


sub get_genomic_sequence_from_chromosome{
   my ($self,) = @_;

   return $self->chr_bioperl->trunc( $self->start_coord, $self->stop_coord )->seq;

}


sub get_locus{
   my ($self) = @_;

   $self->locus( Locus->new( locus_no => $self->locus_no, dbh => $self->dbh ) ) if $self->locus_no;
}

sub get_chromosome{
   my ($self) = @_;

   if( $self->chromosome && !$chro_obj_hash->{$self->chromosome} ) {
      $chro_obj_hash->{$self->chromosome} = Chromosome->new( chromosome => $self->chromosome, dbh => $self->dbh );
   }
   $self->chromosome_obj( $chro_obj_hash->{$self->chromosome} ) if $chro_obj_hash->{$self->chromosome};
}

sub fasta{
   my ($self,$sequence_type) = @_;

   my $str = IO::String->new;

  #
  #   this got a little complicated in getting sequence.  Get by
  #   splicing out chromosomal sequence if it is contig or chromsome (these have no entry
  #   in display_seq.  otherwise use display_seq obj to get the sequence
  #
   my $display_seq_obj  = $self->get_display_seq_by_type( $sequence_type );

   my $display_seq      = $display_seq_obj ?
                             $display_seq_obj->display_seq :
                             grep( /(contig)|(chromosome)/i, @{ $self->get_feature_types } ) ?
                                $self->get_genomic_sequence_from_chromosome()                :
                                undef;

   if ( ! defined $display_seq ) {
      return undef;
   }
   else {

      my $display_seq_type = $display_seq_obj ? $display_seq_obj->display_seq_type : $sequence_type;



      my $header .= "|".$display_seq_type."|";
         $header .= " locus: ".$self->locus->locus_name if $self->locus;
         $header .= " on chromosome: ".$self->chromosome_obj->chromosome_name if $self->chromosome_obj;
         $header .= " position ".$self->start_coord." to ".$self->stop_coord if $self->start_coord;
         $header .= " plus ".$display_seq_obj->flanking_up. " upstream and ".$display_seq_obj->flanking_down." downstream basepairs" if $display_seq_obj && $display_seq_type eq "Genomic DNA";
         $header .= ", reverse complement" if ($self->strand eq 'C' and $display_seq_type eq "Genomic DNA");

      my $seqobj;

      eval{
         $seqobj      = Bio::Seq->new(
                                     -display_id => $self->dictyBaseid,
                                     -desc       => $header,
                                     -seq        => $display_seq
                                   );
      };
      if ($@) {
        die $@."\n".Dumper($self);
      }

      my $out = Bio::SeqIO->new( '-format' => 'fasta', -fh =>$str  );

      $out->write_seq( $seqobj );

      return  ${ $str->string_ref };
   }
}



sub _first_column_arrayref {
   my ($self, $array)  = @_;

   my @retunlist = map { $_->[0] } @{ $array };

   return \@retunlist;

}




1;