#
# Cared for by EnsEMBL  <ensembl-dev@ebi.ac.uk>
#
# You may distribute this module under the same terms as perl itself
#
# POD documentation - main docs before the code

=pod 

=head1 NAME

Bio::EnsEMBL::Analysis::RunnableDB::IgSegBuilder

=cut

# Let the code begin...

package Bio::EnsEMBL::Analysis::RunnableDB::IgSegBuilder;

use vars qw(@ISA);
use strict;

# Object preamble

use Bio::EnsEMBL::Analysis::RunnableDB::BaseGeneBuild;
use Bio::EnsEMBL::Analysis::Config::GeneBuild::IgSegBuilder;

use Bio::SeqIO;
use Bio::EnsEMBL::Gene;
use Bio::EnsEMBL::Utils::Exception qw(throw warning);

@ISA = qw(Bio::EnsEMBL::Analysis::RunnableDB::BaseGeneBuild);

############################################################

sub new {
  my ($class,@args) = @_;
  my $self = $class->SUPER::new(@args);    
  
  $self->read_and_check_config($IGSEG_CONFIG_BY_LOGIC);
           
  # other stuff here
  
  return $self;
}


############################################################

sub fetch_input {
  my($self) = @_;
  
  my $trandb = $self->get_dbadaptor($self->TRANDB_DATABASES_NAME);

  my $slice = $trandb->get_SliceAdaptor->fetch_by_name($self->input_id);
  my $tlslice = $trandb->get_SliceAdaptor->fetch_by_region('toplevel',
                                                           $slice->seq_region_name);
  $self->query($tlslice);

  my (@lv, @d, @j, @c);

  my @groups = 
      (
       { logics => $self->LV_LOGICS, result => \@lv, },
       { logics => $self->D_LOGICS,  result => \@d,  },
       { logics => $self->J_LOGICS,  result => \@j,  },
       { logics => $self->C_LOGICS,  result => \@c,  },
       );

  foreach my $grp (@groups) {
    my ($logics, $result) = ($grp->{logics}, $grp->{result});

    if ($logics) {
      foreach my $logic (@$logics) {
        foreach my $t (@{$slice->get_all_Transcripts(1, $logic)}) {
          map { $_->get_all_supporting_features } ($t, @{$t->get_all_Exons});
          $t = $t->transfer($tlslice);


          if ($t->coding_region_start > $t->start or $t->coding_region_end < $t->end) {
            my @e = @{$t->get_all_translateable_Exons};
            my $tr = Bio::EnsEMBL::Translation->new(-start_Exon => $e[0],
                                                    -end_Exon => $e[-1],
                                                    -start => 1,
                                                    -end => $e[-1]->length);
            my @sfs = @{$t->get_all_supporting_features};
            $t = Bio::EnsEMBL::Transcript
                ->new(-analysis => $t->analysis,
                      -exons    => $t->get_all_translateable_Exons);
            $t->add_supporting_features(@sfs);
            $t->translation($tr);
            
            if ($t->get_all_Exons->[0]->phase < 0) {
              $t->get_all_Exons->[0]->phase(0);
            }
            if ($t->get_all_Exons->[-1]->end_phase < 0) {
              $t->get_all_Exons->[-1]->end_phase(0);
            }
          }
          push @{$result}, $t;
        }
      }                       
    }
  }

  $self->LV_transcripts(\@lv);
  $self->D_transcripts(\@d);
  $self->J_transcripts(\@j);
  $self->C_transcripts(\@c);
}

#############################################################

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

  my $out_db = $self->get_dbadaptor($self->OUTPUTDB_DATABASES_NAME);
  my $ga = $out_db->get_GeneAdaptor;

  my $sf_ana = $self->analysis;
  if ($self->SUPPORTING_FEATURE_OUTPUT_LOGIC) {
    $sf_ana = $out_db->get_AnalysisAdaptor
        ->fetch_by_logic_name($self->SUPPORTING_FEATURE_OUTPUT_LOGIC);
    if (not defined $sf_ana) {
      $sf_ana = $self->analysis;
    }
  }

  foreach my $g (@{$self->output}) {
    $g->analysis($self->analysis);
    foreach my $t (@{$g->get_all_Transcripts}) {
      $t->analysis($self->analysis);
      foreach my $sf (@{$t->get_all_supporting_features}) {
        $sf->analysis($sf_ana);
      }
      foreach my $e (@{$t->get_all_Exons}) {
        foreach my $sf (@{$e->get_all_supporting_features}) {
          $sf->analysis($sf_ana);
        }
      }
    }
  }

  foreach my $g (@{$self->output}) {
    $ga->store($g);
  }
}

#############################################################

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

  my @genes;

  # LV segments...

  my @lv_genes = @{$self->cluster_by_genomic_overlap($self->LV_transcripts)};
  @lv_genes = @{$self->transfer_to_local_slices(\@lv_genes)};
  foreach my $g (@lv_genes) {
    $self->adjust_gene_3($g, "CAC");
    $self->trim_gene_translation3($g);
    $g = $self->prune_LV_transcripts($g);
    if (@{$g->get_all_Transcripts}) {
      $g = $g->transfer($self->query);
      $g->biotype($self->LV_OUTPUT_BIOTYPE);
      map { $_->biotype($self->LV_OUTPUT_BIOTYPE) } @{$g->get_all_Transcripts};
      push @genes, $g;
    }
  }
  @lv_genes = @genes; @genes = ();


  # C segments...

  my @c_genes = @{$self->cluster_by_genomic_overlap($self->C_transcripts)};
  @c_genes = @{$self->transfer_to_local_slices(\@c_genes)};
  foreach my $g (@c_genes) {
    $self->adjust_gene_5($g, "AG");
    $self->set_gene_stop_codon($g);
    $g = $self->prune_C_transcripts($g);
    if (@{$g->get_all_Transcripts}) {
      $g = $g->transfer($self->query);
      $g->biotype($self->C_OUTPUT_BIOTYPE);
      map { $_->biotype($self->C_OUTPUT_BIOTYPE) } @{$g->get_all_Transcripts};
      push @genes, $g;
    }
  }
  @c_genes = @genes; @genes = ();


  # D segments...

  my @d_genes = @{$self->cluster_by_genomic_overlap($self->D_transcripts)};
  @d_genes = grep { $self->gene_close_to_others($_, \@lv_genes, \@c_genes) } @d_genes;
  @d_genes = @{$self->transfer_to_local_slices(\@d_genes)};

  foreach my $g (@d_genes) {
    $self->adjust_gene_5($g, "GTG");
    $self->adjust_gene_3($g, "CAC");;
    $g = $self->prune_D_J_transcripts($g);
    if (@{$g->get_all_Transcripts}) {
      $g = $g->transfer($self->query);
      $g->biotype($self->D_OUTPUT_BIOTYPE);
      map { $_->biotype($self->D_OUTPUT_BIOTYPE) } @{$g->get_all_Transcripts};
      push @genes, $g;
    }
  }
  @d_genes = @genes; @genes = ();


  # J segments...

  my @j_genes = @{$self->cluster_by_genomic_overlap($self->J_transcripts)};
  @j_genes = grep { $self->gene_close_to_others($_, \@lv_genes, \@c_genes) } @j_genes;
  @j_genes = @{$self->transfer_to_local_slices(\@j_genes)};
  foreach my $g (@j_genes) {
    $self->adjust_gene_5($g, "GTG");
    $self->adjust_gene_3($g, "GT");
    $g = $self->prune_D_J_transcripts($g);
    if (@{$g->get_all_Transcripts}) {
      $g = $g->transfer($self->query);
      $g->biotype($self->J_OUTPUT_BIOTYPE);
      map { $_->biotype($self->J_OUTPUT_BIOTYPE) } @{$g->get_all_Transcripts};
      push @genes, $g;
    }
  }
  @j_genes = @genes; @genes = ();
 
  foreach my $g (@lv_genes, @c_genes, @j_genes, @d_genes) {
    $self->prune_Exons($g);
    push @genes, $g;
  }
  @genes = sort { $a->start <=> $b->start } @genes;

  $self->output(\@genes);
}

############################################################
# helper methods
############################################################

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

  my %tran_groups;
  foreach my $t (@$tran) {
    my $sr = $t->slice->seq_region_name;

    push @{$tran_groups{$sr}}, $t;
  }

  my @genes;

  foreach my $grp (values %tran_groups) {
    my @c;
    foreach my $t (sort {$a->start <=> $b->start} @$grp) {
      if (not @c or $t->start > $c[-1]->{end} + 1) {
        push @c, {
          start => $t->start,
          end   => $t->end,
          trans => [$t],
        };
      } else {
        push @{$c[-1]->{trans}}, $t;
        if ($c[-1]->{end} < $t->end) {
          $c[-1]->{end} = $t->end;
        }
      }

    }
    foreach my $c (@c) {
      # all transcripts in cluster should be same strand; if
      # not, use voting to decide correct strand and remove
      # transcripts from other strand
      my @t = @{$c->{trans}};
      my @forward = grep { $_->strand > 0 } @t;
      my @reverse = grep { $_->strand < 0 } @t;

      my @all;
      if (scalar(@forward) > scalar(@reverse)) {
        @all = @forward;
      } elsif (scalar(@reverse) > scalar(@forward)) {
        @all = @reverse;
      } else {
        # equal number of forward and reverse transcripts in this locus; 
        # arbitrarily choose forward
        @all = @forward;
      }
      
      my $gene = Bio::EnsEMBL::Gene->new();
      map { $gene->add_Transcript($_) } @all;
      push @genes, $gene;
    }
  }

  return \@genes;
}


########################################################

# the following method transfers the genes to a set
# of smaller slices; some slices are reverse strand
# to ensure that all gene and sequence is forward
# strand (makes motif checking easier)
#
sub transfer_to_local_slices {
  my ($self, $genes) = @_;
  
  my @groups;
  foreach my $g (sort { $a->start <=> $b->start } @$genes) {
    if (not @groups or 
        $groups[-1]->{strand} != $g->strand or
        $g->start > $groups[-1]->{end} + 100000) {
      push @groups, {
        start => $g->start,
        end   => $g->end, 
        strand => $g->strand,
        genes => [],
      };
    } elsif ($g->end > $groups[-1]->{end}) {
      $groups[-1]->{end} = $g->end;
    }
    push @{$groups[-1]->{genes}}, $g;
  }

  my @retgenes;
  foreach my $grp (@groups) {
    my $gstart = $grp->{start} - 10; 
    $gstart = 1 if $gstart < 1;
    my $gend   = $grp->{end} + 10; 
    $gstart = $self->query->end if $gstart > $self->query->end;
    
    my $local_slice = $self->get_dbadaptor($self->TRANDB_DATABASES_NAME)->
        get_SliceAdaptor->fetch_by_region($self->query->coord_system->name,
                                          $self->query->seq_region_name,
                                          $gstart,
                                          $gend,
                                          $grp->{strand});
    push @retgenes, map { $_->transfer($local_slice) } @{$grp->{genes}}; 
  }

  return \@retgenes;
}


#############################################################
sub prune_Exons {
  my ($self, $gene) = @_;

  my %gene_exons;

  foreach my $tran (@{$gene->get_all_Transcripts}) {
    my @t_exons;
    foreach my $e (@{$tran->get_all_Exons}) {
      my $k = join(":",
                   $e->start,
                   $e->end,
                   $e->strand,
                   $e->phase,
                   $e->end_phase);
      if (not exists $gene_exons{$k}) {
        $gene_exons{$k} = $e;
      } else {
        my %sf;
        map { $sf{$_} = $_ } @{$gene_exons{$k}->get_all_supporting_features};
        foreach my $osf (@{$e->get_all_supporting_features}) {
          if (not exists $sf{$osf}) {
            $gene_exons{$k}->add_supporting_features($osf);
          }
        }
      }
      push @t_exons, $gene_exons{$k};

      if ($tran->translation) {
        if ($e == $tran->translation->start_Exon) {
          $tran->translation->start_Exon($gene_exons{$k});
        }
        if ($e == $tran->translation->end_Exon) {
          $tran->translation->end_Exon($gene_exons{$k});
        }
      }
    }
    
    $tran->flush_Exons;
    map { $tran->add_Exon($_) } @t_exons;
  }
}


#############################################################
sub prune_LV_transcripts {
  my ($self, $gene) = @_;

  my @ftran;  
  my @tran = @{$gene->get_all_Transcripts};
  @tran = grep { $_->translate->seq !~ /\*/ } @tran;

  #
  # remove transcripts with frameshift introns
  #
  TRAN: foreach my $t (@tran) {
    foreach my $i (@{$t->get_all_Introns}) {
      if ($i->length <= 10) {
        next TRAN;
      }
    }
    push @ftran, $t;
  }
  @tran = @ftran; @ftran = ();

  #
  # if there are transcripts with a single intron, remove others
  #
  foreach my $t (@tran) {
    if (scalar(@{$t->get_all_Introns}) == 1) {
      push @ftran, $t;
    }
  }
  if (@ftran) {
    @tran = @ftran; 
    @ftran = ();
  }
  
  #
  # if there are transcripts with all-consensus introns, remove others
  #
  my $seq = uc($gene->slice->seq);

  foreach my $t (@tran) {
    my $all_consensus = 1;
    foreach my $i (@{$t->get_all_Introns}) {
      my $left = substr($seq, $i->start - 1, 2);
      my $right = substr($seq, $i->end - 2, 2);
 
      if ($left ne 'GT' or $right ne 'AG') {
        $all_consensus = 0;
        last;
      }
    }
    push @ftran, $t if $all_consensus;
  }
  if (@ftran) {
    @tran = @ftran;
    @ftran = ();
  }

  #
  # if there are transcripts that begin with ATG, remove others
  #
  foreach my $t (@tran) {
    my $start = substr($seq, $t->start - 1, 3);
    push @ftran, $t if $start eq 'ATG';
  }
  if (@ftran) {
    @tran = @ftran;
    @ftran = ();
  }

  #
  # finally, remove redundant transcripts; only those with
  # unique introns are kept
  #
  @tran = sort _by_total_exon_length @tran;

  my @redun_tran;

  foreach my $t (@tran) {
    my @intr = @{$t->get_all_Introns};
    my $redundant = 0;
    foreach my $kt (@ftran) {
      my @kintr = @{$kt->get_all_Introns};
      my $matches = 0;
      foreach my $i (@intr) {
        foreach my $ki (@kintr) {
          if ($i->start == $ki->start and
              $i->end   == $ki->end) {
            $matches++;
            last;
          }
        }
      }
      if ($matches == scalar(@intr)) {
        $redundant = 1;
        last;
      }
    }
    if (not $redundant) {
      push @ftran, $t;
    } else {
      push @redun_tran, $t;
    }
  }

  #
  # finally, check that the results are not trivial variants
  # differing by only a small number of amino acids
  #
  @tran = @ftran;
  @ftran = ();

  @tran = sort _by_percent_id @tran;
  foreach my $t (@tran) {
    my $redundant = 0;
    foreach my $ft (@ftran) {
      my $all_covered = 1;
      foreach my $i (@{$t->get_all_Introns}) {
        # find correponding intron
        my $found_matching = 0;
        foreach my $fi (@{$ft->get_all_Introns}) {
          if ($i->overlaps($fi) and
              abs($i->start  - $fi->start <= 9) and
              abs($i->end - $fi->end <= 9)) {
            $found_matching = 1;
            last;
          }
        }
        if (not $found_matching) {
          $all_covered = 0;
          last;
        }
      }
      if ($all_covered) {
        $redundant = 1;
        last;
      }
    }
    if (not $redundant) {
      push @ftran, $t;
    } else {
      push @redun_tran, $t;
    }
  }

  $self->transfer_exon_supporting_features(\@redun_tran, \@ftran);

  return Bio::EnsEMBL::Gene->new(-transcripts => \@ftran);
}

########################################################
sub prune_C_transcripts {
  my ($self, $gene) = @_;

  my @tran = @{$gene->get_all_Transcripts};
  @tran = grep { $_->translate->seq !~ /\*/ } @tran;
  @tran = sort _by_total_exon_length @tran;

  my (@newtran, @redun_tran, @intron_lists, %all_exons);
  foreach my $tran (@tran) {
    my @exons = sort {$a->start <=> $b->start} @{$tran->get_all_Exons};
    my @introns = sort {$a->start <=> $b->start} @{$tran->get_all_Introns};
    my $is_redundant = 0;
    foreach my $il (@intron_lists) {
      my $all_found = 1;
      foreach my $ti (@introns) {
        my $found_in_other = 0;
        foreach my $oi (@$il) {
          if ($ti->start == $oi->start and
              $ti->end   == $oi->end) {
            $found_in_other = 1;
            last;
          }
        }
        if (not $found_in_other) {
          $all_found = 0;
        }        
      }
      if ($all_found) {
        $is_redundant = 1;
        last;
      }
    }
    if (not $is_redundant) {
      push @newtran, $tran;
      push @intron_lists, \@introns;
      map { $all_exons{$_->start . ":" . $_->end} = $_} @exons;
    } else {
      # all introns have been seen in another transcript,
      # but perhaps the terminal exons contribute something
      # unique?
      my $all_covered = 1;
      foreach my $tex ($exons[0], $exons[-1]) {
        my $covered = 0;
        foreach my $eid (keys %all_exons) {
          if ($all_exons{$eid}->start <= $tex->start and
              $all_exons{$eid}->end   >= $tex->end) {
            # the exon is completely covered by another
            $covered = 1;
            last;
          }
        }
        if (not $covered) {
          $all_covered = 0;
          last;
        }
      }
      if (not $all_covered) {
        push @newtran, $tran;
        push @intron_lists, \@introns;
        map { $all_exons{$_->start . ":" . $_->end} = $_} @exons;
      } else {
        push @redun_tran, $tran;
      }
    }
  }

  $self->transfer_exon_supporting_features(\@redun_tran, \@newtran);

  return Bio::EnsEMBL::Gene->new(-transcripts => \@newtran);
}

########################################################
sub prune_D_J_transcripts {
  my ($self, $gene) = @_;

  my ($best, @others) = sort _by_total_exon_length @{$gene->get_all_Transcripts};

  if (scalar(@{$best->get_all_Exons}) > 1) {
    my ($exon, $min, $max);
    foreach my $e (@{$best->get_all_Exons}) {
      if (not defined $exon) {
        $exon = $e;
        $min = $e->start;
        $max = $e->end;
      } else {
        $min = $e->start if $e->start < $min;
        $max = $e->end   if $e->end > $max;
      }
    }
    $exon->start($min);
    $exon->end($max);

    $best->flush_Exons;
    $best->add_Exon($exon);
  }
  
  # remove translation
  $best->translation(undef);
  $best->recalculate_coordinates;
  map { $_->phase(-1); $_->end_phase(-1) } @{$best->get_all_Exons};
    
  $self->transfer_exon_supporting_features(\@others, [$best]);

  return Bio::EnsEMBL::Gene->new(-transcripts => [$best]);
}


#########################################################
sub set_gene_stop_codon {
  my ($self, $gene) = @_;

  my $seq = uc($gene->slice->seq);
  
  my %stops = (TAA => 1,
               TGA => 1,
               TAG => 1);

  foreach my $t (@{$gene->get_all_Transcripts}) {
    my $reg_start = $t->end - 2;

    my $subseq = substr($seq, $t->end - 3, 3);

    if (not exists $stops{$subseq}) {
      my $next_seq = substr($seq, $t->end, 3);

      if (exists $stops{$next_seq}) {
        $self->adjust_transcript($t, 0, 3); 
      }
    }
  }
}

#########################################################
sub trim_gene_translation5 {
  my ($self, $gene) = @_;
  
  my $seq = uc($gene->slice->seq);
  
  my $codons_to_check = 2;
  
  foreach my $t (@{$gene->get_all_Transcripts}) {
    my $exon = $t->get_all_Exons->[0];
    my $spare = (3 - $exon->phase) % 3;
    my $adjust = 0;
    
    for(my $i=0; $i < $codons_to_check; $i++) {
      my $start = $exon->start + $spare + (3 * $i);
      my $codon = substr($seq, $start - 1, 3);
      if ($codon eq 'TAA' or 
          $codon eq 'TGA' or 
          $codon eq 'TAG') {
        $adjust = (3 * $i) + 3 + $spare;
      }
    }
    
    if ($adjust) {
      $t->translation->start($adjust);
    }
  }
}

#########################################################
sub trim_gene_translation3 {
  my ($self, $gene) = @_;
  
  my $seq = uc($gene->slice->seq);
  
  my $codons_to_check = 2;
  
  foreach my $t (@{$gene->get_all_Transcripts}) {
    my $exon = $t->get_all_Exons->[-1];
    my $spare = $exon->end_phase;
    my $adjust = 0;
    
    for(my $i=0; $i < $codons_to_check; $i++) {
      my $start = $exon->end - $spare - 2 - (3 * $i);
      my $codon = substr($seq, $start - 1, 3);
      if ($codon eq 'TAA' or 
          $codon eq 'TGA' or 
          $codon eq 'TAG') {
        $adjust = (3 * $i) + 3 + $spare;
      }
    }
    
    if ($adjust) {
      $t->translation->end($exon->length - $adjust);
    }
  }
}

#########################################################
sub adjust_gene_5 {
  my ($self, $gene, $motif) = @_;

  my $seq = uc($gene->slice->seq);

  # we will consider adding/removing a maximum of
  # 2 bps to the transcript start (to allow for
  # a partial codon at the start sometimes being
  # included in the translation, and sometimes not)

  my (@adjusted_trans);
  
  foreach my $t (@{$gene->get_all_Transcripts}) {
    my $reg_end = $t->start + 1;
    my $reg_start = $t->start - 2 - length($motif);
    my $subseq = substr($seq, $reg_start - 1, $reg_end - $reg_start + 1);

    # favour extension so take the first match
    if ((my $subseqoff = index($subseq, $motif)) >= 0) {
      my $newstart = $subseqoff + length($motif) + $reg_start;
      $self->adjust_transcript($t, $t->start - $newstart, 0);
    }
  }
}


#########################################################
sub adjust_gene_3 {
  my ($self, $gene, $motif) = @_;

  my $seq = uc($gene->slice->seq);

  # we will consider adding/removing a maximum of
  # 2 bps to the transcript end (to allow for
  # a partial codon at the end sometimes being
  # included in the translation, and sometimes not)

  foreach my $t (@{$gene->get_all_Transcripts}) {
    my $reg_start = $t->end - 1;
    my $reg_end = $t->end + 2 + length($motif);
    my $subseq = substr($seq, $reg_start - 1, $reg_end - $reg_start + 1);
    
    # favour extension so take the last match
    if ((my $subseqoff = index($subseq, $motif)) >= 0) {
      while((my $anotheroff = index($subseq, $motif, $subseqoff + 1)) >= 0) {
        $subseqoff = $anotheroff;
      }
      my $newend = $subseqoff + $reg_start - 1;
      $self->adjust_transcript($t, 0, $newend - $t->end);
    }
  }
}

#########################################################
sub adjust_transcript {
  my ($self, $tran, $offset5, $offset3) = @_;

  # offsets in the following are how much to EXTEND transcript
  # by at each end (-> negative offsets are truncations)

  my @exons = @{$tran->get_all_Exons};

  if ($offset5) {
    $exons[0]->start($exons[0]->start - $offset5);
    $exons[0]->phase( ($exons[0]->phase - $offset5) % 3 );
    if ($tran->translation) {
      $tran->translation->end($exons[-1]->length);
    }
    $tran->recalculate_coordinates;
  }

  if ($offset3) {
    $exons[-1]->end($exons[-1]->end + $offset3);    
    $exons[-1]->end_phase( ($exons[-1]->end_phase + $offset3) % 3 );
    if ($tran->translation) {
      $tran->translation->end($exons[-1]->length);
    }
    $tran->recalculate_coordinates;
  }
}


sub gene_close_to_others {
  my ($self, $gene, @glists) = @_;

  # return true if gene is within 2Mb of at least
  # one other gene in each of the given lists

  if (not $self->D_J_PROXIMITY_THRESHOLD) {
    return 1;
  } else {
    foreach my $list (@glists) {
      my $member_close = 0;
      foreach my $g (@$list) {
        if (abs($g->start - $gene->start) <= $self->D_J_PROXIMITY_THRESHOLD) {
          $member_close = 1;
          last;
        }
      }
      if (not $member_close) {
        return 0;
      }
    }
  }

  return 1;
}


sub transfer_exon_supporting_features {
  my ($self, $src_trans, $tgt_trans) = @_;

  my @src_sf;

  foreach my $t (@{$src_trans}) {
    foreach my $e (@{$t->get_all_Exons}) {
      push @src_sf, @{$e->get_all_supporting_features};
    }
  }

  @src_sf = sort { $a->start <=> $b->start } @src_sf;

  foreach my $t (@{$tgt_trans}) {
    my @e = sort { $a->start <=> $b->start } @{$t->get_all_Exons};

    foreach my $e (sort { $a->start <=> $b->start } @{$t->get_all_Exons}) {
      foreach my $sf (@src_sf) {
        if ($sf->start <= $e->end and $sf->end >= $e->start) {
          $e->add_supporting_features($sf);
        } elsif ($sf->start > $e->end) {
          last;
        }
      }
    }
  }
}


#######

sub _by_total_exon_length {
  my $alen = 0;
  my $blen = 0;
  foreach my $e (@{$a->get_all_Exons}) {
    $alen += $e->length;
  }
  foreach my $e (@{$b->get_all_Exons}) {
    $blen += $e->length;
  }
  
  my ($aevi) = @{$a->get_all_supporting_features};
  my ($bevi) = @{$b->get_all_supporting_features};

  return ($blen <=> $alen or $bevi->percent_id <=> $aevi->percent_id);
}


sub _by_percent_id {
  my $alen = 0;
  my $blen = 0;
  foreach my $e (@{$a->get_all_Exons}) {
    $alen += $e->length;
  }
  foreach my $e (@{$b->get_all_Exons}) {
    $blen += $e->length;
  }
  
  my ($aevi) = @{$a->get_all_supporting_features};
  my ($bevi) = @{$b->get_all_supporting_features};

  return ($bevi->percent_id <=> $aevi->percent_id or $blen <=> $alen);
}




############################################################
# containers
############################################################

sub LV_transcripts{
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_lv_transcripts} = $value;
  }
  return $self->{_lv_transcripts};
}


sub D_transcripts{
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_d_transcripts} = $value;
  }
  return $self->{_d_transcripts};
}


sub J_transcripts{
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_j_transcripts} = $value;
  }
  return $self->{_j_transcripts};
}


sub C_transcripts{
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_c_transcripts} = $value;
  }
  return $self->{_c_transcripts};
}


sub supporting_feature_analysis {
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_support_ana} = $value;
  }
  return $self->{_support_ana};
} 


############################################################
# config holders
############################################################

sub LV_LOGICS {
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_lv_logic} = $value;
  }
  return $self->{_lv_logic};
}


sub J_LOGICS {
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_j_logic} = $value;
  }
  return $self->{_j_logic};

}

sub D_LOGICS {
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_d_logic} = $value;
  }
  return $self->{_d_logic};

}

sub C_LOGICS {
  my ($self,$value) = @_;
  
  if (defined $value) {
    $self->{_c_logic} = $value;
  }
  return $self->{_c_logic};
}


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

  if (defined $val) {
    $self->{_output_lv_biotype} = $val;
  }

  return $self->{_output_lv_biotype};
}


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

  if (defined $val) {
    $self->{_output_d_biotype} = $val;
  }

  return $self->{_output_d_biotype};
}


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

  if (defined $val) {
    $self->{_output_j_biotype} = $val;
  }

  return $self->{_output_j_biotype};
}


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

  if (defined $val) {
    $self->{_output_c_biotype} = $val;
  }

  return $self->{_output_c_biotype};
}

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

  if (defined $val) {
    $self->{_supporting_logic} = $val;
  }

  return $self->{_supporting_logic};
}

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

  if (defined $val) {
    $self->{_transcriptdb_name} = $val;
  }

  return $self->{_transcriptdb_name};
}


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

  if (defined $val) {
    $self->{_outputdb_name} = $val;
  }

  return $self->{_outputdb_name};
}

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

  if (defined $val) {
    $self->{_dj_prox} = $val;
  }

  return $self->{_dj_prox};

}


1;
