/**
 * Title:        ProAlign<p>
 * Description:  sequence alignment comparison program<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.JPanel;
import javax.swing.JScrollPane;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.RenderingHints;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;


//  Prints alignment characters with nice colors, shows quality as color.
//
public class PrintData extends JPanel {

    final Color bg = Color.white;
    final Color fg = Color.black;
    final Color purple = new Color(250,90,240);
    final Color lblue = new Color(90,210,250);
    final Color brown = new Color(200,160,30);
    final Color blue = new Color(205,205,255);
    
    int verticalCharSpace;
    int horizontalCharSpace;
    
    Font printFont;
    FontMetrics currentMetrics;
    
    int columnWidth, rowHeight; 
    int totalWidth, totalHeight;
    int charWidth, charHeight;
    int maxLength;
    
    int startSite = -1;

    boolean isDataGiven = false;
    boolean isDataAligned = false;
    boolean hasMouseListener = false;

    boolean[] taxaRemoved = new boolean[0];
    boolean[] sitesRemoved = new boolean[0];
    
    String textArray[];
    double[][] postProb;

    PrintData pd;
    ResultWindow parent;
    JScrollPane sPane = null;
    
    MiceListener mListener;

    public PrintData() {
	this.setParameters();
    }

    public PrintData(String[] textArray, int maxLength, ResultWindow rw) {

	pd = this;
	parent = rw;
	this.maxLength = maxLength;
	this.textArray = textArray;
	
	verticalCharSpace = ResultWindow.verticalCharSpace;
	horizontalCharSpace = ResultWindow.horizontalCharSpace;

	this.setParameters();

	isDataGiven = true;
	isDataAligned = false;

	totalWidth = maxLength*columnWidth+horizontalCharSpace;
	totalHeight = textArray.length*rowHeight+verticalCharSpace;
	setPreferredSize(new Dimension(totalWidth,totalHeight));
	
	if(hasMouseListener) {
	    removeMouseListener(mListener);
	    hasMouseListener = false;
	}
    }

    public PrintData(String[] textArray, double[][] postProb, ResultWindow rw) {

	pd = this;
	parent = rw;

	this.textArray = textArray;
	this.postProb =  postProb;

	verticalCharSpace = ResultWindow.verticalCharSpace;
	horizontalCharSpace = ResultWindow.horizontalCharSpace;

	this.setParameters();

	isDataGiven = true;
	isDataAligned = true;

	sitesRemoved = new boolean[textArray[0].length()];
	maxLength = textArray[0].length();

	totalWidth = maxLength*columnWidth+horizontalCharSpace;
	totalHeight = textArray.length*rowHeight+verticalCharSpace;
	setPreferredSize(new Dimension(totalWidth,totalHeight));
	
	mListener = new MiceListener();
	addMouseListener(mListener);
	hasMouseListener = true;
	
    }
    
    // Some parameters need to be set even without data.
    //
    void setParameters() {

	printFont = new Font("monospaced", Font.BOLD, ResultWindow.rwFontSize);
	
	setBackground(bg);
	setForeground(fg);
	
	currentMetrics = this.getFontMetrics(printFont);
	charWidth = currentMetrics.charWidth(' ');
	charHeight = currentMetrics.getHeight();

	columnWidth = charWidth + horizontalCharSpace;      
	rowHeight = charHeight + verticalCharSpace;
    }


    public void paint(Graphics g) {

	if(printFont.getSize()!=ResultWindow.rwFontSize || 
	   horizontalCharSpace!=ResultWindow.horizontalCharSpace ||
	   verticalCharSpace!=ResultWindow.verticalCharSpace){

	    printFont = new Font("monospaced", Font.BOLD, ResultWindow.rwFontSize);
	    horizontalCharSpace=ResultWindow.horizontalCharSpace;
	    verticalCharSpace=ResultWindow.verticalCharSpace;

	    this.setParameters();
	}
	
	if(isDataGiven) { // if no data, no update.
	    totalWidth = Math.max(maxLength*columnWidth+
				  horizontalCharSpace,sPane.getWidth());
	    totalHeight = Math.max(textArray.length*rowHeight+
				   verticalCharSpace,sPane.getHeight());
	    setPreferredSize(new Dimension(totalWidth,totalHeight));
	    
	    int startX = sPane.getHorizontalScrollBar().getValue();
	    int endX = startX + sPane.getWidth();
	    int startY = sPane.getVerticalScrollBar().getValue();
	    int endY = startY + sPane.getHeight();
	    
	    int startColumn = (int) (startX/columnWidth);
	    int endColumn = Math.min((int)(endX/columnWidth)+1,maxLength);
	    int startRow = (int) (startY/rowHeight);
	    int endRow = Math.min((int)(endY/rowHeight)+1,textArray.length);
	    
	    int charX = startColumn * columnWidth;
	    int charY = rowHeight + startRow * rowHeight;
	    
	    Graphics2D  g2 = (Graphics2D) g;
	    g2.setColor(bg);
	    g2.setFont(printFont);
	    g2.fillRect(Math.max(startX-10,0),Math.max(startY-10,0),
			Math.min(endX+10,totalWidth),Math.min(endY+10,totalHeight));
	    
	    for (int i = startRow; i < endRow; i++) {
		String str = textArray[i];
		for (int j = startColumn; j < endColumn; j++) {

		    if(j>=str.length()) { break; } // unaligned seqs can be of different length.

		    if(isDataAligned) { // if aligned, draw "selected/not-selected".
			// Do only once...
			if (i == startRow) {
			    if (j < sitesRemoved.length) {
				if (sitesRemoved[j]) {
				    g2.setColor(blue);
				    g2.fillRect(charX,0,(charX+columnWidth),totalHeight);
				    
				} else {
				    g2.setColor(bg);
				    g2.fillRect(charX,0,(charX+columnWidth),totalHeight);
				}
			    }
			}	  
			// ..until here.
		    }

		    g2.setColor(getColor(str.charAt(j)));
		    g2.drawString(""+str.charAt(j), charX, charY);
		    charX += columnWidth;
		}

		if(isDataAligned) { // if aligned, draw color box.
		    if(i<postProb[0].length) {
			charX = startColumn * columnWidth ;
			for (int j = startColumn; j < endColumn; j++) {
			    g2.setColor(getProbColor(postProb[j][i]));
			    g2.fillRect(charX+1,charY+1,columnWidth-2,verticalCharSpace-2);
			    g2.setColor(bg);
			    charX += columnWidth;
			}
		    } 
		}

		charY += rowHeight;
		charX = startColumn * columnWidth ;
	    }
	}
    }
    
    // A click on "quality box" updates messageText on the scree.
    //
    class MiceListener extends MouseAdapter {
	public void mouseClicked(MouseEvent e) {
	    int x = e.getX();
	    int y = e.getY();	    
	    int col = (int) x/columnWidth;
	    y = y - rowHeight/2;
	    if(y<0) { y = 0; }
	    int row = (int) y/rowHeight;
	    if(col >= postProb.length) { col=postProb.length-1; }
	    if(row >= postProb[0].length) { row = postProb[0].length-1; }
	    
	    parent.writeNodeInfo(col,row);

	}
    } 

    // Called from Rule.
    //
    void updateStableSites(int x, int y, boolean shiftDown) {
	if(pd.isDataAligned) {

	    if(shiftDown) {  // shift down -> range selection
		if(startSite > 0) {
		    int site = (int) x/columnWidth;
		    int start, end;
		    if(startSite>site) {
			start = site;
			end = startSite;
		    } else {
			start = startSite+1;
			end = site+1;
		    }
		    for(int sx=start; sx<end; sx++) {
			if (sitesRemoved[sx])
			    sitesRemoved[sx] = false;
			else 
			    sitesRemoved[sx] = true;
		    }
		    pd.repaint();

		    startSite = -1;
		} else {
		    int site = (int) x/columnWidth;
		
		    if (sitesRemoved[site])
			sitesRemoved[site] = false;
		    else 
			sitesRemoved[site] = true;
		    pd.repaint();

		    startSite = site;
		}

	    } else {  // not shift down -> single selection
		int site = (int) x/columnWidth;
		
		if (sitesRemoved[site])
		    sitesRemoved[site] = false;
		else 
		    sitesRemoved[site] = true;
		pd.repaint();

		startSite = -1;
	    }
	}
    }

    public boolean[] getRemovedSites() {
	return sitesRemoved;
    }  

    Color getColor(char x) {
	if(ProAlign.isDna) {
	    if (x == 'a' || x == 'A')
		return Color.red;
	    if (x == 'c' || x == 'C')
		return Color.blue;
	    if (x == 'g' || x == 'G')
		return Color.green;
	    if (x == 't' || x == 'T' || x == 'u' || x == 'U')
		return brown;
	    else
		return Color.gray;
	} else {
	    if (x == 'g' || x == 'G' || x == 'a' || x == 'A' || x == 't' || x == 'T')
		return purple;
	    if (x == 'p' || x == 'P' || x == 's' || x == 'S')
		return purple;
	    if (x == 'l' || x == 'L' || x == 'i' || x == 'I')
		return Color.green;
	    if (x == 'v' || x == 'V' || x == 'm' || x == 'M')
		return Color.green;
	    if (x == 'k' || x == 'K' || x == 'r' || x == 'R' || x == 'h' || x == 'H')
		return lblue;
	    if (x == 'f' || x == 'F' || x == 'w' || x == 'W' || x == 'y' || x == 'Y')
		return Color.blue;
	    if (x == 'e' || x == 'E' || x == 'q' || x == 'Q')
		return Color.black;
	    if (x == 'd' || x == 'D' || x == 'n' || x == 'N')
		return Color.black;
	    if (x == 'c' || x == 'C')
		return Color.red;
	    else
		return Color.gray;
	}
    }
    
    Color getProbColor(double x) {
	x = Math.exp(x);

 	if(x<=0d) {
	    return Color.white;
	} else if(x>0d && x<=0.1d) {
	    return new Color(255,0,0);
	} else if(x>0.1d && x<=0.2d) {
	    return new Color(255,26,0);
	} else if(x>0.2d && x<=0.3d) {
	    return new Color(255,51,0);
	} else if(x>0.3d && x<=0.4d) {
	    return new Color(255,78,0);
	} else if(x>0.4d && x<=0.5d) {
	    return new Color(255,102,0);
	} else if(x>0.5d && x<=0.6d) {
	    return new Color(255,128,0);
	} else if(x>0.6d && x<=0.7d) {
	    return new Color(255,153,0);
	} else if(x>0.7d && x<=0.8d) {
	    return new Color(255,179,0);
	} else if(x>0.8d && x<=0.9d) {
	    return new Color(255,204,0);
	} else if(x>0.9d) {
	    return new Color(255,230,0);
	} else {
	    return Color.gray;
	}
    }
}













