/**
 * Title:        ProAlign<p>
 * Description:  <p>
 * Copyright:    Copyright (c) Ari Loytynoja<p>
 * License:      GNU GENERAL PUBLIC LICENSE<p>
 * @see          http://www.gnu.org/copyleft/gpl.html
 * Company:      ULB<p>
 * @author Ari Loytynoja
 * @version 1.0
 */
package proalign;

import java.util.HashMap;
import java.util.Iterator; 

class PwAlignmentLoop extends Thread {

    
    PwAlignment pa;
    ResultWindow rw;
    PwAlignmentLoop pal;

    double[][] distance;
    String[] names;
    String tree;

    PwAlignmentLoop(PwAlignment pa,HashMap seqs) {
	this.pa = pa;

	ProAlign.log("PwAlignmentLoop");

	align(seqs);

	if(ProAlign.DEBUG) {
	    for(int i=0; i<names.length; i++) {
		ProAlign.log.print(names[i]+"      ");
		for(int j=0; j<names.length; j++) {
		    ProAlign.log.print((""+distance[i][j]+"    ").substring(0,5)+"  ");
		}
		ProAlign.log("");
	    }
	}

	NeighborJoining nj = new NeighborJoining(distance,names);

	tree = nj.getTree();
	ProAlign.log("PwAlignmentLoop unrooted tree:\n"+tree);

	TreeNode tn = new TreeNode(tree);
	tree = tn.findMiddlePoint();
	
    }

    PwAlignmentLoop(ResultWindow rw,PwAlignment pa) {
	this.rw = rw;
	this.pa = pa;
	pal = this;

	start();
    }

    public void run(){

	ProAlign.log("PwAlignmentLoop");

	if(rw.isAligned) {
	    pal.computeDistances();
	} else {
	    pal.align(rw.seqs);
	}

	if(ProAlign.DEBUG) {
	    for(int i=0; i<names.length; i++) {
		ProAlign.log.print(names[i]+"      ");
		for(int j=0; j<names.length; j++) {
		    ProAlign.log.print((""+distance[i][j]+"    ").substring(0,5)+"  ");
		}
		ProAlign.log("");
	    }
	}

	NeighborJoining nj = new NeighborJoining(distance,names);

	tree = nj.getTree();
	ProAlign.log("PwAlignmentLoop unrooted tree:\n"+tree);

	TreeNode tn = new TreeNode(tree);
	tree = tn.findMiddlePoint();

	if(rw.isAligned) {
	    rw.removeGaps(tree);
	} else {
	    rw.setRawDataAndTree(tree);
	}
	rw.setScrollPanes();

    }
    
    void computeDistances() {

	names = rw.root.getTerminalNames();
	String[] seqData = new String[rw.root.getNumChild()];

	for(int i=0; i<seqData.length; i++) { seqData[i] = "";}
	for(int i=0; i<rw.root.cellPath.length; i++) {
	    
	    int h = 0;
	    char[] c0 = rw.root.child[0].getCharacterAt(rw.root.cellPath[i][0]-2);
	    char[] c1 = rw.root.child[1].getCharacterAt(rw.root.cellPath[i][1]-2);
	    
	    for(int j=0; j<c0.length; j++) {
		seqData[h] += c0[j];
		h++;
	    }
	    for(int j=0; j<c1.length; j++) {
		seqData[h] += c1[j];
		h++;
	    } 
	}

	int ns = names.length;
	distance = new double[ns][ns];

	for(int i=0; i<ns; i++) {
	    for(int j=i+1; j<ns; j++) { 


		// Remove terminal gaps
		//
		int k=0;
		int first = 0; int last = 0;
		for(k=0; k<seqData[i].length(); k++) {
		    if(seqData[i].charAt(k)=='-' || seqData[j].charAt(k)=='-') {
			continue;
		    } else {
			first = k;
			break;
		    }
		}
		for(k=seqData[i].length()-1; k>=0; k--) {
		    if(seqData[i].charAt(k)=='-' || seqData[j].charAt(k)=='-') {
			continue;
		    } else {
			last = k;
			break;
		    }
		}
	

		int all = 0;
		int same = 0;
		for(k=first; k<=last; k++) {
		    if(seqData[i].charAt(k)==seqData[j].charAt(k)) {
			same++;
		    }
		    all++;
		}
 
		if(ProAlign.isDna) {
		    double p = 1d-(double)same/(double)all; 
		    double jcK;
		    if(ProAlign.correctMultiple) {
			jcK= -0.75d*Math.log(1d-4d/3d*p);
		    } else {
			jcK = p;
		    }
		    if(jcK>5d) {
			jcK=5d;
		    }
		    
		    distance[i][j] = distance[j][i] = jcK;
		    
		} else {
		    double p = 1d-(double)same/(double)all; 
		    double kD;
		    if(ProAlign.correctMultiple) {
			kD = -1d*Math.log(1-p-0.2d*p*p);
		    } else {
			kD = p;
		    }
		    if(kD>5d) {
			kD=5d;
		    }
		    distance[i][j] = distance[j][i] = kD;
		    
		}
	    }
	}
    }

    void align(HashMap seqs) {


	Iterator seqKeys = seqs.keySet().iterator();
	int ns = seqs.size();

	names = new String[ns];
	int h=0;
	while(seqKeys.hasNext()) {
	    names[h++] = (String)seqKeys.next();
	}

	distance = new double[ns][ns];

	for(int i=0; i<ns; i++) {
	    for(int j=i+1; j<ns; j++) {   

		if(ProAlign.isResultWindow) {
		    ResultWindow.updateInfo(" Computing distance matrix:"+
					    " aligning '"+names[i]+"' and '"+names[j]+"'.");
		} 
		String seq1 = (String)seqs.get(names[i]);
		String seq2 = (String)seqs.get(names[j]);
		distance[i][j] = pa.align(seq1,seq2);
		distance[j][i] = distance[i][j];
		distance[i][i] = 0d;

	    }
	}
    }
    double[][] getDistance() {
	return distance;
    }
    String[] getNames() {
	return names;
    }

    String getTree() {
	return tree;
    }

/*
    public static void main(String[] args) {

	SequenceReader2 sr = new SequenceReader2();
	boolean works = sr.fromFile(args[0]);
	if(!works) {
	    System.exit(0);
	}
	HashMap seqs = sr.getSequences(); 

	String alphabet;
	int[][] subst;
	int gOpen;
	int gExt;
	boolean isDna;

	PwSubstitutionMatrix psm = new PwSubstitutionMatrix();
	CheckSequence2 cs = new CheckSequence2();
	if(cs.isDna(seqs)){
	    alphabet = psm.dnaAlphabet;
	    subst = psm.swdna;
	    gOpen = -15;
	    gExt = -7;
	    isDna = true;
	} else {
	    alphabet = psm.protAlphabet;
	    subst = psm.pam60;
	    gOpen = -10;
	    gExt = -1;
	    isDna = false;
	}

	PwAlignment pa = new PwAlignment(subst,gOpen,gExt,alphabet,isDna);

	PwAlignmentLoop pal = new PwAlignmentLoop(pa);
	pal.align(seqs);

	NeighborJoining nj = new NeighborJoining(pal.getDistance(),pal.getNames());
	System.out.println(nj.getTree());
    }
*/
}
