#! /usr/bin/perl -w

use strict;

print "Content-type: text/html\n\n";

########################################################################################
##########################PROGRAMA D'ALINIAMENT DE SEQUENCIES###########################
########################################################################################


# definim variables

my $seq1;
my $seq2;
my @seq1_v;
my @seq2_v;
my $i;
my $j;
my $m;
my $n;
my %msubs;
my $matriu_substitucio;
my @amin_v;
my $amin;
my @punt_parcials;
my @alinea;
my @ali1;
my @ali2;
my @ali3;
my @ali4;
my @rali1;
my @rali2;
my @rali3;
my $x = 0;
my $puntuacio = 0;
my $penalitzacio_salt;


######################################################################################
#############################TRACTAMENT DE L'INPUT####################################
###################################################################################### 

  
my $query_string = $ENV{'QUERY_STRING'};
 
my %hash = &entrada($query_string);


if ($hash{seq1} eq "") {
    print "<html><body><br><blockquote><b>There is no sequence 1.
           <p>Please, paste your sequence and try again.</b></body></html>";
    exit(1);
}
if ($hash{seq2} eq "") {
    print "<html><body><br><blockquote><b>There is no sequence 2.
           <p>Please, paste your sequence and try again.</b></body></html>";
    exit(1);
}


if ($hash{penalitzacio} ne "") {
    $penalitzacio_salt = $hash{penalitzacio};
}
else {
    $penalitzacio_salt = $hash{penalitzacio_salt};
}

#########################################################################################
#######################LLEGIM LES SEQUENCIES EN FORMAT FASTA#############################
#########################################################################################

#guardem les sequencies en un vector, i emmagatzemem el nom de la sequencia 
#($a[0] i $b[0]) en una variable ($primeralinia) i la resta de la sequencia en una altra 
#variable ($seq).

my @a = split (/\n/, $hash{seq1});
my @b = split (/\n/, $hash{seq2});

my $primeralinia1 = $a[0];
my $primeralinia2 = $b[0];


$i = 1;
$seq1 = "";
while ($i < scalar(@a)) {
     $seq1 = $seq1 . $a[$i];
     $i = $i + 1;
 }

$i = 1;
$seq2 = "";
while ($i < scalar(@b)) {
    $seq2 = $seq2 . $b[$i];
    $i = $i + 1;
}

#cridem la funcio llegir_sequencia. Explicacio al final del programa 

@seq1_v = &llegir_sequencia ($seq1);
@seq2_v = &llegir_sequencia ($seq2);



#######################################################################################
################################HASH DE HASHOS##########################################
########################################################################################


#obrim el fitxer de la matriu depenent de l'opcio que ha triat l'ususari:

if ($hash{matriu} eq "PAM30") {
    open (MATRIU1, "<pam30.txt");
}
else {
    if ($hash{matriu} eq "PAM70") {
	open (MATRIU1, "<pam70.txt");
    }
    else {
	if ($hash{matriu} eq "BLOSUM62") {
	    open (MATRIU1, "<blosum62.txt");
	}
	else {
	    open (MATRIU1, "<blosum80.txt");
	}
    }
}


#guardem la primera linia de la matriu (simbols) a la variable $amin

$amin = <MATRIU1>;


#cridem la funcio llegir_matriu_substitucio. Explicacio al final del programa

%msubs = llegir_matriu_substitucio($amin);


##########################################################################################
###############################MATRIU D'ALINIAMENT########################################
##########################################################################################

#aquesta matriu consta de dues matrius: una, la @punt_parcials, que emmagatzema els valors 
#de les puntuacions, i l'altra, la @alinea, on enregistrem la procedencia de l'alineament 
#parcial `optim amb tres etiquetes:'D' (diagonal), 'U' (dalt) i 'L' (esquerra)


$m = scalar(@seq1_v);
$n = scalar(@seq2_v);

#fem un 'unshift' a les sequencies per desplac,ar una posicio a la dreta tots
# els simbols per poder inserir un simbol de gap '-' a la posicio 0 

unshift(@seq1_v,"-");
unshift(@seq2_v,"-");

#enregistrem la matriu al fitxer matali.txt

open (MATALI, ">matali.txt");

#mostrem els simbols del vector de les columnes (@seq2_v)

print MATALI "  ";
$i = 0;
while ($i < $n+1) {

  print MATALI "$seq2_v[$i]\t";
  $i = $i + 1;
}
print MATALI "\n";


#mostrem el primer simbol del vector de les files (@seq1_v), que
#es un gap '-'

print MATALI "$seq1_v[0] ";

#a la posicio 0,0 la puntuacio sera 0

$punt_parcials[0][0]=0;
$alinea[0][0] = "final";
print MATALI "$punt_parcials[0][0]\t";

#per omplir la primera fila hem d'utilitzar obligatoriament el valor de l'esquerra ($alinea[0][$i] = 'L')
#i la puntuacio en cada posicio s'obte de la suma de la puntuacio anterior i el valor de la 
#penalitzacio_salt ($ARGV[3])

$i = 1;
while ($i < $n+1) {
  $punt_parcials[0][$i]=$punt_parcials[0][$i-1] + $penalitzacio_salt;
  $alinea[0][$i] = 'L';
  print MATALI "$punt_parcials[0][$i]$alinea[0][$i]\t";
  $i = $i + 1;
}
print MATALI "\n\n";
  

#ara, a partir de la segona fila i segona columna, anem construint
#totes les puntuacions parcials recorrent totes les files i per
#cada fila totes les columnes

$i = 1;
while ($i < $m+1) {

  #mostrem el simbol de la fila

  print MATALI "$seq1_v[$i] ";

  #per omplir la segona columna hem d'utilitzar obligatoriament el valor de dalt ($alinea[$i][0] = 'U')
  #i la puntuacio en cada posicio s'obte de la suma de la puntuacio anterior i el valor de la 
  #penalitzacio_salt


  $punt_parcials[$i][0] = $punt_parcials[$i-1][0] + $penalitzacio_salt;
  $alinea[$i][0] = 'U';
  print MATALI "$punt_parcials[$i][0]$alinea[$i][0]\t";

  $j = 1;
  while ($j < $n+1) {

    #per omplir la resta de la matriu hi ha 3 opcions de puntuacio possibles: alinear dos simbols
    #($puntuacio_diagonal), alinear un simbol de les files (@seq1_v) amb un gap ($puntuacio_dalt) 
    #i alinear un simbol de les columnes (@seq2_v) amb un gap ($puntuacio_esquerra). Ens quedarem
    #amb l'aliniament parcial que tingui la maxima puntuacio

    my $puntuacio_diagonal = $punt_parcials[$i-1][$j-1] + $msubs{$seq1_v[$i]}{$seq2_v[$j]};    
    my $puntuacio_dalt     = $punt_parcials[$i-1][$j] + $penalitzacio_salt;
    my $puntuacio_esquerra = $punt_parcials[$i][$j-1] + $penalitzacio_salt;

    if ($puntuacio_diagonal >= $puntuacio_dalt &&
        $puntuacio_diagonal >= $puntuacio_esquerra) {

      $punt_parcials[$i][$j] = $puntuacio_diagonal;
      $alinea[$i][$j] = 'D';

    }
    else {

      if ($puntuacio_dalt >= $puntuacio_diagonal &&
          $puntuacio_dalt >= $puntuacio_esquerra) {

        $punt_parcials[$i][$j] = $puntuacio_dalt;
        $alinea[$i][$j] = 'U';

      }
      else { 

        $punt_parcials[$i][$j] = $puntuacio_esquerra;
        $alinea[$i][$j] = 'L';

      }

    }

    #imprimim a cada posicio de la matriu el valor de la puntuacio parcial i d'on prove
    #aquest valor

    print MATALI "$punt_parcials[$i][$j]$alinea[$i][$j]\t";

    $j = $j + 1;

  }

  $i = $i + 1;

  print MATALI "\n\n";
}

#tanquem el fitxer

close (MATALI);


############################################################################################
###################################ALINIAMENT OPTIM#########################################
############################################################################################

#un cop tenim la matriu d'aliniament, obtindrem l'aliniament optim comenc,ant
# per l'ultima fila i columna, i seguint les anotacions de 'D' (diagonal)
# 'U' (dalt) i 'L' (esquerra) que tenim a la matriu @alinea.
#Fem servir 3 vectors (@ali1,@ali2 i @ali3): @ali1 contindra els valors de la seq1, @ali2
#contindra els valors de la seq2 i @ali3 contindra els simbols '*','.' o ' '. Posarem un '*'
#si els 2 aa son iguals, un '.' si el valor corresponent de la matriu d'identitats es >= 0, i 
#' ' si aquest valor es < 0 o aliniem l'aa amb un gap.
#Paral.lelament anem calculant la puntuacio de l'aliniament optim pas per pas ($puntuacio)

#declarem variables

$i = $m;
$j = $n;

while ($i > 0 || $j > 0) {

  #si la puntuacio maxima venia de la diagonal alineem el simbol
  #$i de seq1_v amb el simbol $j de seq2_v i posem '*', '.' o ' ' 
  #segons correspongui

  if ($alinea[$i][$j] eq 'D') {

    $ali1[$x] = $seq1_v[$i];
    $ali2[$x] = $seq2_v[$j];
    if ($seq1_v[$i] eq $seq2_v[$j]) {
	$ali3[$x] = "*";
    }
    else {
	if ($seq1_v[$i] ne $seq2_v[$j] && $msubs{$seq1_v[$i]}{$seq2_v[$j]} >= 0) {   
	    $ali3[$x] = ":";
	}
	if ($seq1_v[$i] ne $seq2_v[$j] && $msubs{$seq1_v[$i]}{$seq2_v[$j]} < 0 ) {   
	    $ali3[$x] = " ";
	}
    }
    #en aquest cas el valor de la puntuacio sera el valor anterior mes el valor corresponent a
    #la matriu d'identitats

    $puntuacio = $puntuacio + $msubs{$seq1_v[$i]}{$seq2_v[$j]};                     
    $i = $i - 1;
    $j = $j - 1;
    $x = $x + 1;

  }
  else {

    # si la puntuacio maxima venia de dalt, alineem el simbol
    # $i de seq1_v amb un gap i per tant el valor de $ali3[$i] sera ' '

    if ($alinea[$i][$j] eq 'U') {

      $ali1[$x] = $seq1_v[$i];
      $ali2[$x] = "-";
      $ali3[$x] = " ";
      #aqui, el valor de la puntuacio sera la suma del seu valor anterior mes el de la 
      #penalitzacio per gap

      $puntuacio = $puntuacio + $penalitzacio_salt;
      $i = $i - 1;
      $x = $x + 1;

    }

    else {

      # si la puntuacio maxima venia de l'esquerra, alineem un
      # gap amb el simbol $j de seq2_v. El valor de $ali3[$i] sera tambe ' '

      $ali1[$x] = "-";
      $ali2[$x] = $seq2_v[$j];
      $ali3[$x] = " ";
      #aqui, el valor de la puntuacio sera la suma del seu valor anterior mes el de la 
      #penalitzacio per gap

      $puntuacio = $puntuacio + $penalitzacio_salt;
      $j = $j - 1;
      $x = $x + 1;

    }

  }

}

#fem un reverse dels 3 vectors per tenir l'aliniament en el mateix ordre que el de les 
#sequencies inicials

@rali1 = reverse(@ali1);
@rali3 = reverse(@ali3);
@rali2 = reverse(@ali2);

#imprimim l'aliniament de 60 en 60 aa com el format fasta. Per fer-ho creem una matriu (@ali4) de
#tres files on la primera sera el vector rali1, la segona el vector rali3 i la tercera el vector rali2. 
#El numero de columnes sera la llargada de l'aliniament. 

my $volta = 1;
$j = 0;
$i = 0;
$x = 0;
my $l = 0;

#enviarem l'aliniament al fitxer resultat.txt o a resultat.html



print "<HTML><BODY>  <BODY background = ../yellow_sand.gif><pre><font size = +1>\n\n<font color =gray>";
print "$primeralinia1\n</font><font color =black>$primeralinia2</font>\n\n";

while ($i < scalar(@rali1)) {
    $x = $i;
    $j = 0;
    $l = $l + 1;
   
    #mostrem la posicio del primer aa de la fila

    print "$l\t\t";

    while ($j < 3) {
	$i = $x;
	if ($j > 0) {
	    print "\t\t"; 
	}

        #anem mostrant cada element de la matriu separats en blocs de 60 columnes  
	
	while ($i < 60*$volta && $i < scalar(@rali1)) {
	
	    if ($j == 0) {
		print "<font color= gray>";
		$ali4[$j][$i] = $rali1[$i];
	    }
	    if ($j == 1) {
		if ($rali3[$i] eq "*") {
		    print  "<font color = #8B0000>";
		    }
	        if ($rali3[$i] eq ":") { 
		    print "<font color = #008080>";
		    }
	        $ali4[$j][$i] = $rali3[$i];
	    }

	    if ($j == 2) {
		print "<font color= black>";
		$ali4[$j][$i] = $rali2[$i];
	    }
	    print  $ali4[$j][$i];
	    $i = $i + 1;
	}

	#imprimim la posicio l'ultim aa del bloc

	if ($j == 0) {
	    
	    print "<font color = black>";
	    if ($i < (scalar(@rali1)-1)) {
		$l = $l + 59;
	        print "\t\t$l";
	    }
	    else {
		$l = scalar(@rali1);
		print "\t\t$l";
	    }
	}

	print "\n";
	$j = $j + 1;
	       
    }
    print "\n\n";
    $volta = $volta + 1;
}

print "\n";

#mostrem la puntuacio total de l'aliniament

print "<b>score: $puntuacio\n\n</b></pre></BODY></HTML>";



##########################################################################################
################################FUNCIONS##################################################
##########################################################################################

#FUNCIO: entrada

#OBJECTIU: decodificar i transformar les dades que arriben al programa des de la pagina 
#html en un hash per poder-les utilitzar com a variables.
#Per aconseguir-ho, fem un split de & de manera que les dades ens queden separades en 
#quatre blocs (seq1, seq2, matriu i penalitzacio_salt) i a continuacio fem un altre split
# de = de forma que queda separat l'identificador del contingut. Decodifiquem el contingut
#(@value) transformant el '+' en un espai i els codis dels simbols (per exemple, %0D%0A)
#en els corresponents simbols. Fem un hash on les claus son els identificadors (@key) i on 
#els valors (@value) son els continguts.

#PARAMETRE: les dades que arriben de la pagina html, totes seguides i codificades.

#RETURN: retornem el hash.

sub entrada {
    my $i = 0;
    my $entrada = $_[0];
    my @key;
    my @value;
    my @vector = split (/&/, $entrada);
    while ($i < scalar(@vector)) {   
	($key[$i],$value[$i]) = split (/=/, $vector[$i]);
	$value[$i] =~ tr/+/ /;
	$value[$i] =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack("C", hex($1))/eg;
        $hash{$key[$i]} = $value[$i];
	$i=$i+1;
    }
    return %hash;
}

#FUNCIO: llegir_sequencia

#OBJECTIU: transformar la sequencia en format fasta en un vector on a cada posicio hi hagi
#un aminoacid. 
#En primer lloc, transferim el contingut de la variable $seq a un vector @v de forma que cada 
#element del vector sera una fila de la sequencia. En segon lloc, ajuntem tots els elements 
#del vector @v en una nova variable $seq_v i transferim el contingut de $seq_v al vector 
#@seq_v, on cada posicio sera un aminoacid de la sequencia

#PARAMETRE: la sequencia en format fasta ($seq)

#RETURN: retornem el vector on cada posicio es un aa

sub llegir_sequencia {
   
   my $x=$_[0];
   my @v = split(/\n/,$x);
   
  my $seq_v = "";
   $i = 0;
  while ($i < scalar(@v)) {
     $seq_v = $seq_v . $v[$i];
        $i = $i + 1;
     } 
  $seq_v =~ s/\W//g; 
  my @seq_v = split (//, $seq_v);
  return @seq_v;
	  
}


#FUNCIO: llegir_matriu_substitucio

#OBJECTIU: construim un hash a partir de la matriu de substitucio.
#En primer lloc, agafem la primera linia de la matriu i la passem a un vector
#(@amin_v) on cada posicio hi haura un aa. 
#En segon lloc, construim el hash:
#anem recorrent totes les columnes per cada fila.
#guardem cada fila al vector @v
#assignem el valor de la matriu ($v[$i]) a la parella d'aminoacids corresponents ($v[0] i $amin_v[$i-1]) 
 
#PARAMETRE: la primera linia de la matriu ($amin)

#RETURN: el hash (%msubs)

sub llegir_matriu_substitucio {
    
    $amin = $_[0];
    chomp($amin);
    @amin_v = split(/ +/,$amin);
    shift(@amin_v);
    my $i = 0;
    
    while (<MATRIU1>) {
 
      chomp;
      my @v = split(/ +/);
      my $j = 0;
      while ($j < scalar(@amin_v)) {
        
	 $msubs{$amin_v[$i]}{$amin_v[$j]} = $v[$j+1];
         $j = $j + 1;
	    
	}
       $i = $i + 1;
	       
    }
    return %msubs;

}

