package EnsEMBL::Web::Component::Transcript::TranscriptSeq;

use strict;
use warnings;
no warnings "uninitialized";
use base qw(EnsEMBL::Web::Component::Transcript);

sub _init {
  my $self = shift;
  $self->cacheable(1);
  $self->ajaxable(1);
}

sub caption {
  return undef;
}

sub get_sequence_data {
  my $self = shift;
  my ($object, $config) = @_;
  my $trans = $object->Obj;
  my @exons = @{$trans->get_all_Exons};
  my $trans_strand = $exons[0]->strand;
  my $cd_start = $trans->cdna_coding_start;
  my $cd_end = $trans->cdna_coding_end;

  my $mk = {};
  my $length = 0;
  my $flip = 0;
  my $seq = '';
  my @sequence;
  my @markup;

  my $variation_seq = { name => 'variation', seq => [] };
  my $coding_seq = { name => 'coding_seq', seq => [] };
  my $protein_seq = { name => 'translation', seq => [] };
  my $rna_seq = { name => 'rna', seq => [ map {{ letter => $_ }} (split (//, $object->rna_notation)) ] };
  my @reference_seq = map { 
    $length += length $_->seq->seq;
    $seq .= $_->seq->seq;
    if ($config->{'exons'}) {
      $flip = 1 - $flip;
      push (@{$mk->{'exons'}->{$length}->{'type'}}, $mk->{'exons'}->{$length}->{'overlap'} ? 'exon2' : "exon$flip");
    }
    map {{ 'letter' => $_ }} split (//, uc($_->seq->seq)) 
  } @exons;
  delete $mk->{$length}; # We get a key which is too big, causing an empty span to be printed later 
  # Used to se the initial sequence colour
  if ($config->{'exons'}) {
    $mk->{'exons'}->{0}->{'type'} = [ 'exon0' ];
  }
  $config->{'length'} = $length;
  $config->{'numbering'} = [1];
  $config->{'seq_order'} = [ $config->{'species'} ];
  $config->{'slices'} = [{ slice => $seq, name => $config->{'species'} }];
  for (0..$length-1) {
    # Set default vaules
    $variation_seq->{'seq'}->[$_]->{'letter'} = ' ';
    $coding_seq->{'seq'}->[$_]->{'letter'} = $protein_seq->{'seq'}->[$_]->{'letter'} = '.';
    if ($_+1 >= $cd_start && $_+1 <= $cd_end) {         
      $coding_seq->{'seq'}->[$_]->{'letter'} = $reference_seq[$_]->{'letter'} if $config->{'coding_seq'};
    } elsif ($config->{'codons'}) {
      $mk->{'codons'}->{$_}->{'class'} = 'cu';
    }
  }
  my $can_translate = 0;
  eval {
    my $pep_obj = $trans->translate;
    my $peptide = $pep_obj->seq;
    my $flip = 0;
    my $startphase = $trans->translation->start_Exon->phase;
    my $s = 0;
    $can_translate = 1;
    if ($startphase > 0) {
      $s = 3 - $startphase;
      $peptide = substr($peptide, 1);
    }
    for (my $i = $cd_start + $s - 1; ($i+2) <= $cd_end; $i+=3) {
      if ($config->{'codons'}) {
        $mk->{'codons'}->{$i}->{'class'} = $mk->{'codons'}->{$i+1}->{'class'} = $mk->{'codons'}->{$i+2}->{'class'} = "c$flip";
        $flip = 1 - $flip;
      }
      if ($config->{'translation'}) {        
        $protein_seq->{'seq'}->[$i]->{'letter'} = $protein_seq->{'seq'}->[$i+2]->{'letter'} = '-';
        $protein_seq->{'seq'}->[$i+1]->{'letter'} = substr($peptide, int(($i+1-$cd_start)/3), 1) || ($i+1 < $cd_end ? '*' : '.');
      }
    }
  };

  if ($config->{'variation'}) {    
    $object->database('variation');
    my $source = '';
    if (exists($object->species_defs->databases->{'ENSEMBL_GLOVAR'})) {
      $source = 'glovar';
      $object->database('glovar');
    }
    $source = 'variation' if $object->database('variation');
    my %snps = %{$trans->get_all_cdna_SNPs($source)};
    my %protein_features = $can_translate == 0 ? () : %{$trans->get_all_peptide_variations($source)};

    foreach my $t (values %snps) {
      foreach my $snp (@$t) {
        # Due to some changes start of a variation can be greater than its end - insertion happened
        my ($st, $en);
        my $snpclass = $snp->var_class;
        my $source = $snp->source;
        my $variation_name = $snp->variation_name;
        my $strand = $snp->strand;
        my $alleles = $snp->allele_string;
        my $ambigcode = $snpclass eq 'in-del' ? '*' : $snp->ambig_code;
        my $insert = 0;
        if ($strand == -1 && $trans_strand == -1) {
          $ambigcode =~ tr/acgthvmrdbkynwsACGTDBKYHVMRNWS\//tgcadbkyhvmrnwsTGCAHVMRDBKYNWS\//;
          $alleles =~ tr/acgthvmrdbkynwsACGTDBKYHVMRNWS\//tgcadbkyhvmrnwsTGCAHVMRDBKYNWS\//;
        }
        if ($snp->start > $snp->end) {
          $st = $snp->end;
          $en = $snp->start;
          $insert = 1;
        } else {
          $en = $snp->end;
          $st = $snp->start;
        }
        foreach my $r ($st..$en) {               
          $mk->{'variations'}->{$r-1}->{'alleles'} .= $alleles;
          $mk->{'variations'}->{$r-1}->{'url_params'} .= "source=" . $source . ";snp=" . $variation_name;
          $mk->{'variations'}->{$r-1}->{'transcript'} = 1;
          my $url_params = $mk->{'variations'}->{$r-1}->{'url_params'};
          if ($snpclass eq 'snp' || $snpclass eq 'SNP - substitution') {
            my $aa = int(($r - $cd_start + 3)/3);
            my $aa_bp = $aa * 3 + $cd_start - 3;
            my @feat = @{$protein_features{$aa}||[]};
            my $f = scalar @feat;
            my $title = join (', ', @feat);
            $mk->{'variations'}->{$r-1}->{'type'} = ($mk->{'variations'}->{$r-1}->{'type'} eq 'snp' || $f != 1) ? 'snp' : 'syn';
            if ($config->{'translation'} && $f > 1) {
              $protein_seq->{'seq'}->[$aa_bp-1]->{'letter'} = $protein_seq->{'seq'}->[$aa_bp+1]->{'letter'} = '=';
              for ($aa_bp-1..$aa_bp+1) {
                $protein_seq->{'seq'}->[$_]->{'class'} = 'aa';
                $protein_seq->{'seq'}->[$_]->{'title'} = $title;
              }
            }
          } else {
            $mk->{'variations'}->{$r-1}->{'type'} = $insert ? 'insert' : 'delete';
          }
          $mk->{'variations'}->{$r-1}->{'type'} .= 'utr' if $config->{'codons'} && $mk->{'codons'}->{$r-1} && $mk->{'codons'}->{$r-1}->{'class'} eq 'cu';
          $variation_seq->{'seq'}->[$r-1]->{'letter'} = $url_params ? qq{<a href="../snpview?$url_params">$ambigcode</a>} : $ambigcode;
        }
      }
    }
  }
  push (@sequence, \@reference_seq);
  push (@markup, $mk);
  for ($variation_seq, $coding_seq, $protein_seq, $rna_seq) {
    if ($config->{$_->{'name'}}) {
      if ($_->{'name'} eq 'variation') {
        unshift (@sequence, $_->{'seq'});
        unshift (@markup, {});
        unshift (@{$config->{'numbering'}}, 0);
        unshift (@{$config->{'seq_order'}}, $_->{'name'});
        unshift (@{$config->{'slices'}}, { slice => join ('', map { $_->{'letter'} } @{$_->{seq}}), name => $_->{'name'} });
      } else {
        push (@sequence, $_->{'seq'});
        push (@markup, {});
        push (@{$config->{'numbering'}}, 1);
        push (@{$config->{'seq_order'}}, $_->{'name'});
        push (@{$config->{'slices'}}, { slice => join ('', map { $_->{'letter'} } @{$_->{seq}}), name => $_->{'name'} });
      }
    }
  }
  return (\@sequence, \@markup);
}

sub content {
  my $self   = shift;
  my $object = $self->object;
  my $config = { 
    display_width => $object->param('display_width') || 60,
    species => $object->species,
    maintain_colour => 1
  };
  for ('exons', 'codons', 'coding_seq', 'translation', 'rna', 'variation', 'number') {
    $config->{$_} = ($object->param($_) eq "yes") ? 1 : 0;
  }
  $config->{'codons'} = $config->{'coding_seq'} = $config->{'translation'} = 0 unless $object->Obj->translation;
  $config->{'variation'} = 0 unless $object->species_defs->databases->{'DATABASE_VARIATION'};
  $config->{'rna'} = 0 unless $object->rna_notation;
  my ($sequence, $markup) = $self->get_sequence_data($object, $config);
  $self->markup_exons($sequence, $markup, $config) if $config->{'exons'};
  $self->markup_codons($sequence, $markup, $config) if $config->{'codons'};
  $self->markup_variation($sequence, $markup, $config) if $config->{'variation'};  
  $self->markup_line_numbers($sequence, $config) if $config->{'number'};
  $config->{'v_space'} = "\n" if ($config->{'coding_seq'} || $config->{'translation'} || $config->{'rna'});
  my $html = $self->build_sequence($sequence, $config);
  if ($config->{'codons'} || $config->{'variation'} || $config->{'translation'}  || $config->{'coding_seq'}) {
    $html .= qq(<img src="/i/help/transview_key3.gif" alt="[Key]" border="0" />);
  }
  return $html;
}

1;