/**
 * 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 javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JScrollBar;
import javax.swing.BorderFactory;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JMenu;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Container;
import java.awt.Point;
import java.awt.Graphics;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.beans.PropertyChangeEvent ;
import java.beans.PropertyChangeListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Iterator;
import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ResultWindow extends JFrame {

    JMenuBar mb = new JMenuBar();
    JMenu 
        fm = new JMenu("File   "),
	dm = new JMenu("Import"),
	em = new JMenu("Export"),
	am = new JMenu("Align  "),
	tm = new JMenu("Font   "),
	tm1 = new JMenu("Font size"),
	tm2 = new JMenu("Horizontal space"),
	tm3 = new JMenu("Vertical space"),
	pm = new JMenu("Result "),
	lm = new JMenu("Filter sites");

    JMenuItem[] file = {
	new JMenuItem("About ProAlign"),
	new JMenuItem("Open alignment"),
	new JMenuItem("Save alignment"),
	new JMenuItem("Save guide tree"),
	new JMenuItem("Exit")
	    };
    JMenuItem[] data = {
	new JMenuItem("data"),
	new JMenuItem("guide tree")
	    };   
    JMenuItem[] export = {
	new JMenuItem("PIR"),
	new JMenuItem("Phylip"),
	new JMenuItem("MSF"),
	new JMenuItem("Nexus")
	    };
    JMenuItem align[] = {
	new JMenuItem("Do ClustalW guide tree"),
	new JMenuItem("Do ProAlign guide tree"),
	new JMenuItem("Do multiple alignment"),
	new JMenuItem("Sample alignments"),
	new JMenuItem("Remove all gaps"),
	new JMenuItem("Set parameters"),
	    };

    JMenuItem proba[] = {
	new JMenuItem("Plot min. probability"),
	new JMenuItem("min 90%"),
	new JMenuItem("min 80%"),
	new JMenuItem("min 70%"),
	new JMenuItem("min 60%"),
	new JMenuItem("min 50%"),
	new JMenuItem("min 40%"),
	new JMenuItem("min 30%"),
	new JMenuItem("min 20%"),
	new JMenuItem("min 10%"),
	new JMenuItem("0% (reset)"),
    };
    
    JMenuItem fom1[] = {
	new JMenuItem("8"),
	new JMenuItem("9"),
	new JMenuItem("10"),
	new JMenuItem("11"),
	new JMenuItem("12"),
	new JMenuItem("13"),
	new JMenuItem("14")
    };

    JMenuItem fom2[] = {
	new JMenuItem("2"),
	new JMenuItem("3"),
	new JMenuItem("4"),
	new JMenuItem("5"),
	new JMenuItem("6"),
	new JMenuItem("7"),
	new JMenuItem("8"),
	new JMenuItem("9")
    };

    JMenuItem fom3[] = {
	new JMenuItem("2"),
	new JMenuItem("3"),
	new JMenuItem("4"),
	new JMenuItem("5"),
	new JMenuItem("6"),
	new JMenuItem("7"),
	new JMenuItem("8"),
	new JMenuItem("9")
    };


    JScrollPane sPane1;
    JScrollPane sPane2;
    JScrollPane sPane3;

    JScrollBar sButt1;
    JScrollBar sButt3;

    //   ScrollListener scro;
    
    JSplitPane splitp1;
    JSplitPane splitp2;

    JTextField messageText;
    Container cp;
    
    Font font = new Font(ProAlign.paFontName,Font.PLAIN, ProAlign.paFontSize);
    Font font1 = new Font("monospaced", Font.PLAIN, ProAlign.paFontSize);
    
    PrintTree guidetree;
    PrintNames seqname;
    PrintData seqcont;

    Rule charRule;
    JPanel columnRule1;
    JPanel columnRule2;
    
    int splitWidth = 100;
    boolean isAligned = false;
    boolean hasData = false;
    boolean hasTree = false;

    String[] nodeNames;
    double[][] postProb;

    int wHeight;
    int wWidth;
    int maxLength;

    static int verticalCharSpace = 5;
    static int horizontalCharSpace = 5;
    static int rwFontSize = 11;

    HashMap seqs;

    static ResultWindow rw;
    AlignmentNode root;
    ProAlign pa;

    ResultWindow(ProAlign pa) {
	
	rw = this;
	this.pa = pa;
	setTitle("ProAlign "+ProAlign.version);   

	// Create the JMenuBar - it's needed anyway.
	//
	FileMenuListener fle = new FileMenuListener();
	AlignmentMenuListener alg = new AlignmentMenuListener();
	ProbabilityMenuListener prb = new ProbabilityMenuListener();
	FontMenuListener fon = new FontMenuListener();

        file[0].setActionCommand("about");
        file[0].addActionListener(fle);
        file[0].setFont(font);
        file[1].setActionCommand("open");
        file[1].addActionListener(fle);
        file[1].setFont(font);
        file[2].setActionCommand("save");
        file[2].addActionListener(fle);
        file[2].setFont(font);
        file[3].setActionCommand("savepag");
        file[3].addActionListener(fle);
        file[3].setFont(font);
        file[4].setActionCommand("exit");
        file[4].addActionListener(fle);
        file[4].setFont(font);

	data[0].setActionCommand("data");
	data[0].addActionListener(fle);
	data[0].setFont(font);
	data[1].setActionCommand("tree");
	data[1].addActionListener(fle);
	data[1].setEnabled(false);
	data[1].setFont(font);

	export[0].setActionCommand("fasta");
	export[0].addActionListener(fle);
	export[0].setFont(font);
	export[1].setActionCommand("phylip");
	export[1].addActionListener(fle);
	export[1].setFont(font);
	export[2].setActionCommand("msf");
	export[2].addActionListener(fle);
	export[2].setFont(font);
	export[3].setActionCommand("nexus");
	export[3].addActionListener(fle);
	export[3].setFont(font);

	align[0].setActionCommand("guide");
	align[0].addActionListener(alg);
	align[0].setFont(font);
	align[0].setEnabled(false);
	align[1].setActionCommand("paguide");
	align[1].addActionListener(alg);
	align[1].setFont(font);
	align[1].setEnabled(false);
	align[2].setActionCommand("multiple");
	align[2].addActionListener(alg);
	align[2].setFont(font);
	align[2].setEnabled(false);
	align[3].setActionCommand("sample");
	align[3].addActionListener(alg);
	align[3].setFont(font);
	align[3].setEnabled(false);
	align[4].setActionCommand("remove");
	align[4].addActionListener(alg);
	align[4].setFont(font);
	align[4].setEnabled(false);
	align[5].setActionCommand("setting");
	align[5].addActionListener(alg);
	align[5].setFont(font);
	align[5].setEnabled(true);

	for(int i=0; i<proba.length; i++) {
	    proba[i].addActionListener(prb);
	    proba[i].setFont(font);
	}
	proba[0].setActionCommand("plot");
	proba[0].setEnabled(false);
	proba[1].setActionCommand("90percent");
	proba[2].setActionCommand("80percent");
	proba[3].setActionCommand("70percent");
	proba[4].setActionCommand("60percent");
	proba[5].setActionCommand("50percent");
	proba[6].setActionCommand("40percent");
	proba[7].setActionCommand("30percent");
	proba[8].setActionCommand("20percent");
	proba[9].setActionCommand("10percent");
	proba[10].setActionCommand("0percent");


	for(int i=0; i<fom1.length; i++) {
	    fom1[i].addActionListener(fon);
	    fom1[i].setFont(font);
	    fom1[i].setActionCommand("size");
	    tm1.add(fom1[i]);
	}
	for(int i=0; i<fom2.length; i++) {
	    fom2[i].addActionListener(fon);
	    fom2[i].setFont(font);
	    fom2[i].setActionCommand("hor");
	    tm2.add(fom2[i]);
	}
	for(int i=0; i<fom3.length; i++) {
	    fom3[i].addActionListener(fon);
	    fom3[i].setFont(font);
	    fom3[i].setActionCommand("ver");
	    tm3.add(fom3[i]);
	}

	dm.add(data[0]);
	dm.add(data[1]);
	dm.setFont(font);
	em.add(export[0]);
	em.add(export[1]);
	em.add(export[2]);
	em.add(export[3]);
	em.setFont(font);
	em.setEnabled(false);
        fm.add(file[0]);
        fm.addSeparator();
        fm.add(file[1]);
        fm.add(file[2]);
	file[2].setEnabled(false);
        fm.addSeparator();
        fm.add(dm);
        fm.add(em);
	file[3].setEnabled(false);
        fm.add(file[3]);
        fm.addSeparator();
        fm.add(file[4]);
        fm.setFont(font);
	am.add(align[0]);
	am.add(align[1]);
	am.add(align[2]);
	am.add(align[3]);
        am.addSeparator();
	am.add(align[4]);
	am.add(align[5]);
	am.setFont(font);

	for(int i=1; i<proba.length; i++) {
	    lm.add(proba[i]);
	}
	pm.add(proba[0]);
	pm.add(lm);
	lm.setFont(font);
	lm.setEnabled(false);
	pm.setFont(font);
	
        mb.add(fm);

	tm.setFont(font);
	tm1.setFont(font);
	tm2.setFont(font);
	tm3.setFont(font);
	tm.add(tm1);
	tm.add(tm2);
	tm.add(tm3);
	mb.add(tm);

        mb.add(am);
	mb.add(pm);
        setJMenuBar(mb);


	// Message field to show some info.
	//
	messageText = new JTextField();
	messageText.setFont(font);
	messageText.setBackground(Color.white);
	messageText.setEditable(false);


	//Create empty panels and import data later.
	//
	seqname = new PrintNames();
	seqname.setPreferredSize(new Dimension(50,100));
	seqcont = new PrintData();
	seqcont.setPreferredSize(new Dimension(500,100));
	guidetree = new PrintTree();
	guidetree.setPreferredSize(new Dimension(50,100));

	// Create a split pane for the two scroll panes at time.
	//
	splitp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
	splitp1.setOneTouchExpandable(true);
	splitp1.setDividerLocation(splitWidth);
	
	// Listener needed to update the tree.
	splitp1.addPropertyChangeListener(new SplitListener());

	// Second splitpane will keep the first as a left-side.
	//
	splitp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
	splitp2.setOneTouchExpandable(true);
	splitp2.setDividerLocation(splitWidth*2);

	// Set scrollPanes.
	//	
	setScrollPanes();


	// Get the initial size from the parent - needs updating.
	//
	wHeight = ProAlign.wHeight;
	wWidth = ProAlign.wWidth;

	// Do not use layout but set it manually.
	//
	cp = getContentPane();
	cp.setLayout(null);
	cp.add(splitp2);
	cp.add(messageText);

	if(System.getProperty("os.name").startsWith("Windows")){
	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
	    messageText.setBounds(0,wHeight-75,wWidth-10,20);
	} else if(System.getProperty("os.name").startsWith("Mac")){
	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
	    messageText.setBounds(0,wHeight-75,wWidth-20,25);	    
	} else {
	    splitp2.setBounds(0,0, wWidth-10,wHeight-66);
	    messageText.setBounds(0,wHeight-65,wWidth-10,25);
	}
	addWindowListener(new WindowAdapter() {
		public void windowClosing(WindowEvent e){System.exit(0);}
	    });
	addMouseMotionListener(new MiceListener());
    }

    void setAlignedData(AlignmentNode root) {
	    
	// Get the sequence names..
	//
	String[] seqNames = root.getTerminalNames();
	for(int i=0; i<seqNames.length; i++) {
	    seqNames[i] = (seqNames[i]+"                    ").substring(0,20);
	}
	
	// and get the sequence data.
	//
	String[] seqData = new String[root.getNumChild()];
	for(int i=0; i<seqData.length; i++) { seqData[i] = ""; }
	for(int i=0; i<root.cellPath.length; i++) {
	    
	    int h = 0;
	    char[] c0 = root.child[0].getCharacterAt(root.cellPath[i][0]-2);
	    char[] c1 = root.child[1].getCharacterAt(root.cellPath[i][1]-2);
	    
	    for(int j=0; j<c0.length; j++) {
		seqData[h++] += c0[j];
	    }
	    for(int j=0; j<c1.length; j++) {
		seqData[h++] += c1[j];
	    }	
	} 

	// Get the posterior probabilities.
	//
	nodeNames = root.getInternalNames();
	postProb = new double[root.cellPath.length][nodeNames.length];
	for(int i=0; i<root.cellPath.length; i++) {
	    postProb[i] = root.getInternalPostProbAt(i);
	}
	
	// Update info & create new JPanels.
	//
	rw.messageText.setText(" Alignment ready: "+seqData.length+" sequences, "+
			       seqData[0].length() +" characters. viterbi:"+root.sumViterbiEnd()+
				       "; forward: "+root.sumForwardEnd());
	seqname = new PrintNames(seqNames, true);
	seqcont = new PrintData(seqData, postProb, rw);
	guidetree = new PrintTree(root, rw, true);

	rw.file[1].setEnabled(true); // open alignment
	rw.file[2].setEnabled(true); // save alignment
	rw.file[3].setEnabled(true); // save paguide

	rw.data[1].setEnabled(true); // import guide tree

	rw.align[0].setEnabled(false); // do guide tree
	rw.align[1].setEnabled(true); // do paguide tree
	rw.align[2].setEnabled(false); // do multiple 
	rw.align[3].setEnabled(false); // sample
	rw.align[4].setEnabled(true); // remove gaps

	rw.dm.setEnabled(true); // import
	rw.em.setEnabled(true); // export 

	rw.proba[0].setEnabled(true); // plot prob's
	rw.lm.setEnabled(true); // filter sites

	isAligned = true;
	hasData = true;
	hasTree = true;

    }

    void setRawData(HashMap seqs) {

	rw.seqs = seqs;

	// Get the sequence names and the data.
	//
	Iterator seqKeys = seqs.keySet().iterator();	
	String[] seqNames = new String[seqs.keySet().size()];
	String[] seqData = new String[seqs.keySet().size()];

	int i = 0;
	maxLength = 0;
	while(seqKeys.hasNext()) {
	    String name = (String) seqKeys.next();
	    seqData[i] = (String) seqs.get(name);
	    seqNames[i] = (name+"                    ").substring(0,20);

	    if(seqData[i].length()>maxLength) {
		maxLength = seqData[i].length();
	    }
	    i++;
	}
	
	// Update info & create new JPanels.
	//
	rw.messageText.setText(" Data ready: "+seqData.length+" sequences. ");
	seqname = new PrintNames(seqNames, false);
	seqcont = new PrintData(seqData, maxLength, rw);
	guidetree = new PrintTree();
	guidetree.setPreferredSize(new Dimension(50,seqname.getHeight()));

	rw.file[1].setEnabled(true); // open alignment
	rw.file[2].setEnabled(false); // save alignment
	rw.file[3].setEnabled(false); // save paguide

	rw.data[1].setEnabled(true); // import guide tree

	rw.align[0].setEnabled(true); // do clustalw guide tree
	rw.align[1].setEnabled(true); // do proalign guide tree
	rw.align[2].setEnabled(false); // do multiple
	rw.align[3].setEnabled(true); // sample
	rw.align[4].setEnabled(false); // remove gaps

	rw.dm.setEnabled(true); // import
	rw.em.setEnabled(false); // export 

	rw.proba[0].setEnabled(false); // plot prob's
	rw.lm.setEnabled(false); // filter sites

	isAligned = false;
	hasData = true;
	hasTree = false;

    }

    void setRawDataAndTree(String tree) {
	
	rw.root = new AlignmentNode(pa,tree,0f);

	String[] seqNames = root.getTerminalNames();
	String[] seqData = new String[seqNames.length];

	for(int j=0; j<seqNames.length; j++) {
	    seqData[j] = (String) pa.seqs.get(seqNames[j]);
	    seqNames[j] = (seqNames[j]+"                    ").substring(0,20);
	}

	// Update info & create new JPanels.
	//
	rw.messageText.setText(" Data and tree ready: "+seqData.length+" sequences. ");
	seqname = new PrintNames(seqNames, false);
	seqcont = new PrintData(seqData, maxLength, rw);
	guidetree = new PrintTree(root, rw, false);

	rw.file[1].setEnabled(true); // open alignment
	rw.file[2].setEnabled(false); // save alignment
	rw.file[3].setEnabled(true); // save paguide

	rw.data[0].setEnabled(true); // import data
	rw.data[1].setEnabled(true); // import guide tree

	rw.align[0].setEnabled(true); // do guide tree
	rw.align[1].setEnabled(true); // do paguide tree
	rw.align[2].setEnabled(true); // do multiple
	rw.align[3].setEnabled(true); // sample
	rw.align[4].setEnabled(false); // remove gaps
	
	rw.dm.setEnabled(true); // import
	rw.em.setEnabled(false); // export 

	isAligned = false;
	hasData = true;
	hasTree = true;
    }

    void removeGaps(String tree) {

	pa.setNodeNumber(-1);
	rw.root = new AlignmentNode(pa,tree,0f);
	
	String[] seqNames = rw.root.getTerminalNames();
	String[] seqData = new String[rw.root.getNumChild()];
	
	maxLength = 0;
	for(int i=0; i<seqNames.length; i++) {
	    seqData[i] = (String) seqs.get(seqNames[i]);
	    seqNames[i] = (seqNames[i]+"                    ").substring(0,20);

	    if(seqData[i].length()>maxLength) {
		maxLength = seqData[i].length();
	    }
	}

	// Update info & create new JPanels.
	//
	rw.messageText.setText(" Data and tree ready: "+seqData.length+" sequences. ");
	seqname = new PrintNames(seqNames, false);
	seqcont = new PrintData(seqData, maxLength, rw);
	guidetree = new PrintTree(root, rw, false);

	rw.file[1].setEnabled(true); // open alignment
	rw.file[2].setEnabled(false); // save alignment
	rw.file[3].setEnabled(true); // save paguide

	rw.data[1].setEnabled(true); // import guide tree

	rw.align[0].setEnabled(true); // do guide tree 
	rw.align[1].setEnabled(true); // do pa guide tree
	rw.align[2].setEnabled(true); // do multiple
	rw.align[3].setEnabled(true); // sample
	rw.align[4].setEnabled(false); // remove gaps

	rw.dm.setEnabled(true); // import
	rw.em.setEnabled(false); // export

	rw.proba[0].setEnabled(false); // plot prob's
	rw.lm.setEnabled(false); // filter sites

	isAligned = false;
	hasData = true;

    }

    // Update if the frame has been resized.
    //
    public void update(Graphics g) {
	
	wHeight = rw.getHeight();
        wWidth = rw.getWidth();

	if(System.getProperty("os.name").startsWith("Windows")){
	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
	    messageText.setBounds(0,wHeight-75,wWidth-10,20);
	} else if(System.getProperty("os.name").startsWith("Mac")){
	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
	    messageText.setBounds(0,wHeight-75,wWidth-20,25);	    
	} else {
	    splitp2.setBounds(0,0, wWidth-10,wHeight-66);
	    messageText.setBounds(0,wHeight-65,wWidth-10,25);
	}
	splitp2.updateUI();
	
	messageText.updateUI();

    }
    public void macUpdate() { // things don't work on OSX

	if(wHeight!=rw.getHeight() || wWidth!=rw.getWidth()){

	    wHeight = rw.getHeight();
	    wWidth = rw.getWidth();

	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
	    messageText.setBounds(0,wHeight-75,wWidth-20,25);	    

	    splitp2.updateUI();
	    
	    messageText.updateUI();
	}
    }

    void setScrollPanes() {

	sPane1 = new JScrollPane(guidetree,
				 JScrollPane.VERTICAL_SCROLLBAR_NEVER,
				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
	sPane2 = new JScrollPane(seqname,
				 JScrollPane.VERTICAL_SCROLLBAR_NEVER,
				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
	sPane3 = new JScrollPane(seqcont,
				 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
	
	sButt1 = new JScrollBar();
	sButt3 = new JScrollBar();

	rw.setRulers();

	sPane1.setViewportBorder(BorderFactory.createLineBorder(Color.black));
	sPane1.setColumnHeaderView(columnRule1);
	sPane1.setBackground(Color.white);

	sPane2.setViewportBorder(BorderFactory.createLineBorder(Color.black));
	sPane2.setColumnHeaderView(columnRule2);
	sPane2.setBackground(Color.white);
	seqname.sPane = sPane2;
	    
	sPane3.setViewportBorder(BorderFactory.createLineBorder(Color.black));
	sPane3.setColumnHeaderView(charRule);
	sPane3.setBackground(Color.white);
	seqcont.sPane = sPane3;
	
	sButt3.setUnitIncrement(30);
	sButt3.setBlockIncrement(100);
	sButt3.addAdjustmentListener(new ScrollListener());
	sPane3.setVerticalScrollBar(sButt3);

	sPane1.getHorizontalScrollBar().setBackground(Color.white);
	sPane2.getHorizontalScrollBar().setBackground(Color.white);
	sPane3.getHorizontalScrollBar().setBackground(Color.white);
	sPane1.getVerticalScrollBar().setBackground(Color.white);
	sPane3.getVerticalScrollBar().setBackground(Color.white);
	sPane3.getHorizontalScrollBar().setUnitIncrement(50);
	sPane3.getHorizontalScrollBar().setBlockIncrement(200);
	
	splitp1.setLeftComponent(sPane1);
	splitp1.setRightComponent(sPane2);
	splitp2.setLeftComponent(splitp1);
	splitp2.setRightComponent(sPane3);

	splitp1.setOneTouchExpandable(true);
	splitp1.setDividerLocation(splitWidth);
	splitp2.setOneTouchExpandable(true);
	splitp2.setDividerLocation(splitWidth*2);

	splitp1.updateUI();
	splitp2.updateUI();

    }    

    void setRulers() {

	// Create a ruler for sequence data..
	//
	charRule = new Rule(rw);
	charRule.setPreferredWidth(seqcont.totalWidth);
	charRule.setIncrement(seqcont.columnWidth, seqcont.horizontalCharSpace);
	charRule.setBackground(Color.white);	
	// ..and two empty panels to fill the same space in other windows.
	//
	columnRule1 = new JPanel();
	columnRule1.setPreferredSize(new Dimension(guidetree.getWidth(),charRule.rulerHeight));
	columnRule1.setBackground(Color.white);
	//
	columnRule2 = new JPanel();
	columnRule2.setPreferredSize(new Dimension(seqname.totalWidth,charRule.rulerHeight));
	columnRule2.setBackground(Color.white);

    }
    
    // Focus the alignment view if clicked on the curve.
    //
    void focusAlignment(int x) {
	int y3 = (int) sPane3.getViewport().getViewPosition().getY();
	int x3 = x*seqcont.columnWidth-sPane3.getWidth()/2;
	if(x3<0) { 
	    x3 = 0; 
	}
	if(x3>(seqcont.totalWidth-sPane3.getWidth())) { 
	    x3 = seqcont.totalWidth-sPane3.getWidth();
	}
	sPane3.getViewport().setViewPosition(new Point(x3,y3));
	sPane3.getViewport().updateUI();
    }


    // Write info at messageText field.
    // 
    void writeNodeInfo(int col, int row) {
	String nn = nodeNames[row];
	AlignmentNode an = root.getNodeNamed(nn);
	String alphabet = root.getAlphabet();
	int site = root.getSiteAt(col,nn);

	String txt = "";

	if(site>-1) {
	    for(int i=0; i<alphabet.length(); i++) {
		txt += ", ";
		txt += alphabet.charAt(i)+": ";
		if(ProAlign.isDna) {
		    if(an.charProb[site][i]<0.001) {
			txt += ("0.000"); 
		    } else {
			txt += (""+an.charProb[site][i]+"     ").substring(0,5);
		    }
		} else {
		    if(an.charProb[site][i]<0.001) {
			txt += ("0.00"); 
		    } else {
			txt += (""+an.charProb[site][i]+"     ").substring(0,4);
		    }
		}
	    }
	} else {
	    txt = ", no info";
	}

	String pp = (""+Math.exp(postProb[col][row])+"     ").substring(0,5);
	messageText.setText(col+", "+nn+": "+pp+" (char "+site+txt+")");
       
    }

    void convertNodeInfoX(int col, String name) {

	if(col >= postProb.length) { col=postProb.length-1; }
	int row = -1;
	for(int i=0; i<nodeNames.length; i++) {
	    if(nodeNames[i].equals(name)) { row = i; }
	}
	if(row > -1) {
	    rw.writeNodeInfo(col,row);
	}
    }

    void writeTempFasta() {

	try {
	    OutFile out = new OutFile(ProAlign.tempFolder+File.separator+"proalign.seq");

	    Iterator seqKeys = seqs.keySet().iterator();	

	    while(seqKeys.hasNext()) {
		String name = (String) seqKeys.next();
		String data = (String) seqs.get(name);

		if(ProAlign.isDna) {
		    out.println(">DL;" +name);
		} else {
		    out.println(">P1;" +name);
		}
		out.println();
		int count = 0;
		for(int j = 0; j<data.length(); j++) {
		    count++;
		    out.print(data.charAt(j));
		    if(count == 50) {
			out.println("");
			count = 0;
		    }
		}
		out.println("*");
	    }
	    out.close();
	} catch (IOException e) {
	}
    }

    static void updateInfo(String text) {
	
	rw.messageText.setText(text);

    }

    // Vertical scrolling - requires some synchronizing.
    //
    class ScrollListener implements AdjustmentListener {
	public void adjustmentValueChanged(AdjustmentEvent e) {

	    int y3 = (int) sPane3.getViewport().getViewPosition().getY();
	    int x3 = (int) sPane3.getViewport().getViewPosition().getX();
	    int x2 =  (int) sPane2.getViewport().getViewPosition().getX();

	    sPane1.getViewport().setViewPosition(new Point(0,y3));
	    sPane2.getViewport().setViewPosition(new Point(x2,y3));
	    sPane3.getViewport().setViewPosition(new Point(x3,y3));

	    sPane2.getViewport().updateUI();
	    sPane3.getViewport().updateUI();
	    
	}
    }   

    // Tree split pane - tree dimesions need updating.
    //
    class SplitListener implements PropertyChangeListener {
	public void propertyChange(PropertyChangeEvent e) {

	    if(guidetree.isTreeGiven) {
		guidetree.setPreferredSize(new Dimension(sPane1.getWidth(), guidetree.yMax));
		guidetree.updateUI();
	    }

	}
    }

     
    // ActionListener for 'File' menu
    //
    class FileMenuListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    
	    JMenuItem target = (JMenuItem)e.getSource();
	    String actionCommand = target.getActionCommand();
	    
	    if(actionCommand.equals("about")) {
		
		String text = new String(
		    "\n            ProAlign v. "+ProAlign.version+"\n"+
		    "  a program for probabilistic sequence alignment \n"+
		    "  (c) 2002 Ari Loytynoja & Michel C. Milinkovitch \n");
		OpenDialog od = new OpenDialog(ResultWindow.this);
		od.showDialog("About ProAlign", text);
		
	    } else if(actionCommand.equals("open")) {

		OpenFileChooser opf = 
		    new OpenFileChooser(ResultWindow.this,"Open",true);
		String filepath = opf.openFile();

		if(!filepath.equals("")) {

		    UserSettings user = new UserSettings(); 
		    String[] userdata = user.readSettings();
		    ProAlign.folderPath = new File(filepath).getParent();
		    userdata[0] = new File(filepath).getParent();
                    user.writeSettings(userdata);

		    boolean validFile = true;

		    try {
			ObjectInputStream is = 
			    new ObjectInputStream(new FileInputStream(filepath));
			rw.root = (AlignmentNode) is.readObject();
		    }
		    catch(InvalidClassException ice) {
			String text = new String("\n  Error! File doesn't look\n"+
						 "  like a ProAlign-file!\n");
			OpenDialog od = new OpenDialog(ResultWindow.this);
			od.showDialog("Error!", text);

			ProAlign.log.println("Open exception1: "+filepath+"\n"+ice.toString());
			ProAlign.log.flush();
			validFile = false;
		    } 
		    catch(Exception oe) {
			ProAlign.log.println("Open exception2: "+filepath+"\n"+oe.toString());
			ProAlign.log.flush();
			//oe.printStackTrace();	
			validFile = false;
		    }

		    if(validFile) {

			rw.setAlignedData(rw.root);
			rw.setScrollPanes();
			PrintTree.numOpenWindows = 0;
		    }

		    // Set substitution model
		    if(ProAlign.isDna){
			rw.pa.sm.jcDnaModel();	    
		    } else {
			rw.pa.sm.wagProteinModel();
		    }

		    // Set ProAlign 'seqs' hashmap
		    //
		    String[] names = rw.root.getTerminalNames();
		    String[] data = new String[rw.root.getNumChild()];

		    for(int i=0; i<data.length; i++) { data[i] = ""; }
		    for(int i=0; i<rw.root.cellPath.length; i++) {
			
			int h = 0;
			char[] c0 = root.child[0].getCharacterAt(root.cellPath[i][0]-2);
			char[] c1 = root.child[1].getCharacterAt(root.cellPath[i][1]-2);
			
			for(int j=0; j<c0.length; j++) {
			    if(c0[j]!='-') {
				data[h] += c0[j];
			    }
			    h++;
			}
			for(int j=0; j<c1.length; j++) {
			    if(c1[j]!='-') {
				data[h] += c1[j];
			    }	
			    h++;
			} 
		    }
		    		 
		    rw.seqs = new HashMap();
		    for(int i=0; i<names.length; i++) {
			rw.seqs.put(names[i], data[i]);
		    }
		    pa.seqs = seqs;
		    
		}
		
	    } else if(actionCommand.equals("save")) {
		
		if(rw.root != null) {
		    OpenFileChooser opf = 
			new OpenFileChooser(ResultWindow.this,"Save",true);
		    String filepath = opf.openFile();
		
		    if(!filepath.equals("")) {
			if(!filepath.endsWith(ProAlign.fileExt)) {
			    filepath += "."+ProAlign.fileExt;

			    UserSettings user = new UserSettings(); 
			    String[] userdata = user.readSettings();
			    ProAlign.folderPath = new File(filepath).getParent();
			    userdata[0] = new File(filepath).getParent();
			    user.writeSettings(userdata);
			}
			try {
			    ObjectOutputStream os = 
				new ObjectOutputStream(new FileOutputStream(filepath));
			    os.writeObject((AlignmentNode) rw.root);
			    os.flush();
			    os.close();
			} catch(IOException ioe) {
			    ProAlign.log.println("Save exception1: "+filepath+"\n"+ioe.toString());
			    ProAlign.log.flush();
			    //ioe.printStackTrace();
			}
			catch(Exception oe) {
			    ProAlign.log.println("Save exception2: "+filepath+"\n"+oe.toString());
			    ProAlign.log.flush();
			    //oe.printStackTrace();	
			}
		    }
		}

	    } else if(actionCommand.equals("savepag")) {
		
		OpenFileChooser opf = 
		    new OpenFileChooser(ResultWindow.this,"Save guide tree",false);
		String filepath = opf.openFile();
		if(!filepath.equals("")) {
		    try {
			OutFile paguide = new OutFile(filepath);
			paguide.println(rw.root.tree);
			paguide.flush();
			paguide.close();
		    } catch(Exception ne) { }
		}
		//System.out.println("pag: "+rw.root.tree);

	    } else if(actionCommand.equals("data")) {

		OpenFileChooser opf = 
		    new OpenFileChooser(ResultWindow.this,"Import",false);
		String filepath = opf.openFile();

		SequenceReader sr = new SequenceReader();
		if(!filepath.equals("")) {
		    if(sr.fromFile(filepath)) {
			HashMap seqs = sr.getSequences();
			pa.seqs = seqs;

			CheckSequence cs = new CheckSequence();
			if(cs.isDna(seqs)){
			    pa.sm.jcDnaModel();
			    ProAlign.isDna = true;
			} else {
			    pa.sm.wagProteinModel();
			    ProAlign.isDna = false;
			}

			if(!cs.isFromAlphabet(seqs,pa.sm.equateAlphabet)) {
			    OpenDialog od = new OpenDialog(ResultWindow.this);
			    od.showDialog("Error!", cs.getError());
			} else {
			    rw.setRawData(seqs);
			    rw.setScrollPanes();

			    rw.root = null;
			    pa.setNodeNumber(-1);
			    
			    UserSettings user = new UserSettings(); 
			    String[] userdata = user.readSettings();
			    ProAlign.folderPath = new File(filepath).getParent();
			    userdata[0] = new File(filepath).getParent();
			    user.writeSettings(userdata);
			}
		    } else {
			OpenDialog od = new OpenDialog(ResultWindow.this);
			od.showDialog("Error!", sr.getErrors());	
		    }
		}

	    } else if(actionCommand.equals("tree")) {

		OpenFileChooser opf = 
		    new OpenFileChooser(ResultWindow.this,"Import",false);
		String filepath = opf.openFile();

		if(!filepath.equals("")) {

		    UserSettings user = new UserSettings(); 
		    String[] userdata = user.readSettings();
		    ProAlign.folderPath = new File(filepath).getParent();
		    userdata[0] = new File(filepath).getParent();
                    user.writeSettings(userdata);

		    TreeReader tr = new TreeReader();
		    
		    String[] treeNodes = tr.getAllNodes(filepath);
		    CheckTreeAndData chk = 
			new CheckTreeAndData(treeNodes, rw.seqs);
		    if(chk.nodesAreSame()) {
			pa.setNodeNumber(-1);
			String guide = tr.readFile(filepath);
			if(tr.isUnRooted) {
			    TreeNode tn = new TreeNode(guide);
			    guide = tn.findMiddlePoint();
 			}
			if(rw.isAligned) {
			    rw.removeGaps(guide);
			} else {
			    rw.setRawDataAndTree(guide);
			}
			rw.setScrollPanes();

		    }
		}
	    } else if(actionCommand.equals("fasta")) {

		SaveData sd = new SaveData("Fasta", ResultWindow.this);

	    } else if(actionCommand.equals("phylip")) {

		SaveData sd = new SaveData("Phylip", ResultWindow.this);

	    } else if(actionCommand.equals("msf")) {

		SaveData sd = new SaveData("MSF", ResultWindow.this);

	    } else if(actionCommand.equals("nexus")) {

		SaveData sd = new SaveData("Nexus", ResultWindow.this);

	    } else if(actionCommand.equals("exit")) {
		dispatchEvent(new WindowEvent(ResultWindow.this,
					      WindowEvent.WINDOW_CLOSING));
	    }
	}
    }

    // ActionListener for 'Alignment' menu
    //
    class AlignmentMenuListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    
	    JMenuItem target = (JMenuItem)e.getSource();
	    String actionCommand = target.getActionCommand();
	    
	    if(actionCommand.equals("guide")) {

		pa.setNodeNumber(-1);
		rw.writeTempFasta();

		try {
		    RunClustalw rc = new RunClustalw(ResultWindow.this);
		    rw.messageText.setText(" Creating ClustalW guide tree.");

		    rw.file[1].setEnabled(false); // open alignment
		    rw.file[2].setEnabled(false); // save alignment
		    rw.dm.setEnabled(false); // import
		    rw.align[0].setEnabled(false); // do guide tree  
		    rw.align[1].setEnabled(false); // do paguide tree
		    rw.align[2].setEnabled(false); // do multiple
		    rw.align[3].setEnabled(false); // sample
		    rw.align[4].setEnabled(false); // remove gaps

		} catch(Exception ex) {

		    rw.file[1].setEnabled(true); // open alignment
		    rw.file[2].setEnabled(false); // save alignment
		    rw.file[3].setEnabled(false); // save paguide
		    
		    rw.data[1].setEnabled(true); // import guide tree
		    
		    rw.align[0].setEnabled(true); // do clustalw guide tree
		    rw.align[1].setEnabled(true); // do proalign guide tree
		    rw.align[2].setEnabled(false); // do multiple
		    rw.align[3].setEnabled(true); // sample
		    rw.align[4].setEnabled(false); // remove gaps
		    
		    rw.dm.setEnabled(true); // import
		    rw.em.setEnabled(false); // export 
		    
		    rw.proba[0].setEnabled(false); // plot prob's
		    rw.lm.setEnabled(false); // filter sites

		}	

	    } else if(actionCommand.equals("paguide")) {

		pa.setNodeNumber(-1);
		PwSubstitutionMatrix psm = new PwSubstitutionMatrix();
		String pwAlphabet;
		int[][] pwSubst;
		int gOpen, gExt;

		if(ProAlign.isDna){
		    pwAlphabet = psm.dnaAlphabet;
		    pwSubst = psm.swdna;
		    gOpen = -1*pa.pwDnaOpen;
		    gExt = -1*pa.pwDnaExt;

		} else {

		    pwAlphabet = psm.protAlphabet;
		    if(pa.pwProtMatrix.equals("pam60")) {
			pwSubst = psm.pam60;
		    } else if(pa.pwProtMatrix.equals("pam160")) {
			pwSubst = psm.pam160;
		    } else if(pa.pwProtMatrix.equals("pam250")) {
			pwSubst = psm.pam250;
		    } else {
			pwSubst = psm.pam120;
		    }
		    gOpen = -1*pa.pwProtOpen;
		    gExt = -1*pa.pwProtExt;
		}

		PwAlignment pa = 
		    new PwAlignment(pwSubst,gOpen,gExt,pwAlphabet,ProAlign.isDna);
		PwAlignmentLoop pal = new PwAlignmentLoop(ResultWindow.this,pa);
		rw.messageText.setText(" Creating ProAlign guide tree.");

		rw.file[1].setEnabled(false); // open alignment
		rw.file[2].setEnabled(false); // save alignment
		rw.dm.setEnabled(false); // import
		rw.align[0].setEnabled(false); // do guide tree
		rw.align[1].setEnabled(false); // do paguide tree
		rw.align[2].setEnabled(false); // do multiple
		rw.align[3].setEnabled(false); // sample
		rw.align[4].setEnabled(false); // remove gaps

	    } else if(actionCommand.equals("multiple")) {

		RunMultiple rm = new RunMultiple(ResultWindow.this);
		rw.messageText.setText(" Creating multiple alignment.");

		rw.file[1].setEnabled(false); // open alignment
		rw.file[2].setEnabled(false); // save alignment
		rw.dm.setEnabled(false); // import
		rw.align[0].setEnabled(false); // do guide tree  
		rw.align[1].setEnabled(false); // do paguide tree
		rw.align[2].setEnabled(false); // do multiple
		rw.align[3].setEnabled(false); // sample
		rw.align[4].setEnabled(false); // remove gaps

	    } else if(actionCommand.equals("sample")) {

		SampleAlignments sa = new SampleAlignments(ResultWindow.this);
		sa.setSize(sa.width,sa.height);
		sa.setVisible(true);

	    } else if(actionCommand.equals("remove")) {

		rw.removeGaps(rw.root.tree);
		rw.setScrollPanes();

	    } else if(actionCommand.equals("setting")) {

		SetParameters sp = new SetParameters(ResultWindow.this);
		sp.setSize(sp.width,sp.height);
		sp.setVisible(true);

	    }
	}
    }  

    // ActionListener for 'Probability' menu
    //
    class ProbabilityMenuListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    
	    JMenuItem target = (JMenuItem)e.getSource();
	    String actionCommand = target.getActionCommand();
	    double limit = -1d;

	    if(actionCommand.equals("plot")) {
		//
		double[] minPost = new double[root.cellPath.length];
		String[] minNode = new String[root.cellPath.length];
		for(int i=0; i<root.cellPath.length; i++) {
 		    minPost[i] = root.getMinimumInternalPostProbAt(i);
		    minNode[i] = root. getMinimumInternalPostProbNode();
		}
		
		MinimumProbWindow mpw = new MinimumProbWindow(minPost,minNode,rw);
		mpw.setSize(600,100);
		mpw.setLocation(220,320);		
		mpw.setVisible(true);

	    } else if(actionCommand.equals("90percent")) {
		limit = 0.9d;
	    } else if(actionCommand.equals("80percent")) {
		limit = 0.8d;
	    } else if(actionCommand.equals("70percent")) {
		limit = 0.7d;
	    } else if(actionCommand.equals("60percent")) {
		limit = 0.6d;
	    } else if(actionCommand.equals("50percent")) {
		limit = 0.5d;
	    } else if(actionCommand.equals("40percent")) {
		limit = 0.4d;
	    } else if(actionCommand.equals("30percent")) {
		limit = 0.3d;
	    } else if(actionCommand.equals("20percent")) {
		limit = 0.2d;
	    } else if(actionCommand.equals("10percent")) {
		limit = 0.1d;
	    } else if(actionCommand.equals("0percent")) {
		limit = 0d;
	    }

	    if(limit >= 0d) {	

		boolean[] sitesRemoved = new boolean[root.cellPath.length];
		int count = 0;
		for(int i=0; i<root.cellPath.length; i++) {
 		    if(Math.exp(root.getMinimumInternalPostProbAt(i)) < limit) {
			sitesRemoved[i] = true;
			count++;
		    }
		}
		rw.seqcont.sitesRemoved = sitesRemoved;
		rw.messageText.setText(
		    " Filter: "+count+" of "+root.cellPath.length+" sites have posterior "+
		    "probability lower than "+(int)(limit*100)+" percent.");

	    }
	}
    }

    // ActionListener for 'Probability' menu
    //
    class FontMenuListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    JMenuItem target = (JMenuItem)e.getSource();
	    String actionCommand = target.getActionCommand();
	    // System.out.println(actionCommand+" "+target.getText()+"");

	    if(actionCommand.equals("size")) {
		rwFontSize = new Integer(target.getText()).intValue();
	    } else if(actionCommand.equals("hor")) {
		horizontalCharSpace = new Integer(target.getText()).intValue();
	    } else if(actionCommand.equals("ver")) {
		verticalCharSpace = new Integer(target.getText()).intValue();
	    }

	    FontMetrics currentMetrics = 
		rw.getFontMetrics(new Font("monospaced", Font.BOLD, ResultWindow.rwFontSize));
	    int columnWidth = currentMetrics.charWidth(' ')+horizontalCharSpace;
	    rw.charRule.setIncrement(columnWidth,horizontalCharSpace);
	    rw.charRule.setPreferredWidth(rw.seqcont.maxLength*columnWidth);
	}
    }

    class MiceListener extends MouseMotionAdapter {
	public void mouseMoved(MouseEvent e) {
	    if(System.getProperty("os.name").startsWith("Mac")){
		rw.macUpdate();
	    }
	}
    }
} 











