=head1 NAME

iPE::State::AltSplice::OptExon - Optional exon altsplice state.

=head1 DESCRIPTION

This altsplice state is for optional exons, ones that exist in some isoforms but not in others.  They have to be clearly 

=head1 FUNCTIONS

=over 8

=cut

package iPE::State::AltSplice::OptExon;
use iPE;
use iPE::Globals;
use base("iPE::State::AltSplice");
use strict;

=item detect (transcripts, seqLen)

Detect optional exons in the passed array of transcripts annotating a sequence of underlying length seqLen.

 What we're looking for is this:

 ===|||||||||===||||||||======||||||||||====
 ==||||||||||=================||||||||||||==
             <--------------->
               supercassette
 
 Layer 1 and 2 can happen any number of times in the gene.  Fortunately for us, the annotation is divided up into same-annotated segments.

 Note that a longer intron is allowed in other isoforms.  That is, if another isoform skips this exon and other exons, we take the shortest version; the one with one segment per pre- and post- optional exon introns.

=cut


sub detect {
    my ($this, $transcripts, $seqLength) = @_;

    my $g = new iPE::Globals();
    my @coord_pairs;
    for(my $i = 0; $i < scalar(@{$transcripts})-1; $i++) {
        for(my $j = 1; $j < scalar(@{$transcripts}); $j++) {
            next if ($i == $j);
            my @features = sort { $a->start <=> $b->start || 
                                  $b->end   <=> $b->end }
                           (@{$transcripts->[$i]->features},
                            @{$transcripts->[$j]->features});
            for(my $k = 4; $k < scalar(@features); $k++) {
                if(    $features[$k-3]->start == $features[$k-2]->start &&
                       $features[$k]->end     == $features[$k-2]->end &&
                       $features[$k-1]->state->name eq $this->originState &&
                       $features[$k-1]->state->dnaEmis !=
                       $features[$k-2]->state->dnaEmis) {
                    my $feat = $features[$k-1];

                    # we have to leave the frame of the original feature alone,
                    # since not all opt exons will have length divisible by 3.
                    push(@coord_pairs, 
                        [$feat->start, $feat->end, 
                            $feat->startFrame, $feat->endFrame]);
                    msg($this->name." from ".$features[$k-1]->start." to ".
                        $features[$k-1]->end."\n");
                }
            }
        }
    }

    for my $coord_pair (@coord_pairs) {
        for my $tx (@{$transcripts}) {
            for my $feat (@{$tx->features}) {
                next unless($feat->strand eq $this->strand);
                if($feat->end == $coord_pair->[0]-1
                        && defined($this->inTransition)) {
                    $feat->state($g->state($this->inTransition));
                }
                elsif($feat->start == $coord_pair->[1]+1) {
                    #this feature is dumb, i'm keeping it here for 'compatibility'
                    $feat->state($g->state($this->transitions->dests->[0]));
                }
                elsif($feat->start == $coord_pair->[0] &&
                        $feat->end == $coord_pair->[1]) {
                    $feat->state($this);
                }
                elsif($feat->start < $coord_pair->[0] && 
                        $feat->end > $coord_pair->[1] && 
                       $feat->state->dnaEmis !=
                       $this->dnaEmis) {
                    $tx->addFeature($feat->state, $coord_pair->[1]+1,
                        $feat->end, $feat->startFrame, $feat->endFrame);
                    $tx->addFeature($this, $coord_pair->[0],
                        $coord_pair->[1], $coord_pair->[2], $coord_pair->[3]);
                    $feat->end($coord_pair->[0]-1);
                }
            }
        }
    }
}

=back

=head1 SEE ALSO

L<iPE::State::AltSplice> L<iPE::State> L<iPE::gHMM>

=head1 AUTHOR

Bob Zimmermann (rpz@cse.wustl.edu)

=cut

1;
