/*
 * AlignmentPane.java
 *
 * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 *  BEAST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.app.beauti.alignmentviewer;

import javax.swing.*;
import java.awt.*;

/**
 * @author Andrew Rambaut
 * @version $Id: AlignmentPane.java,v 1.2 2005/11/11 16:40:41 rambaut Exp $
 */
public class AlignmentPane extends JComponent implements Scrollable {
	private final TaxonPane taxonPane;
    private final RulerPane rulerPane;

    private AlignmentBuffer alignment = null;
    private int rowCount = 0;
    private int columnCount = 0;
    private int rowHeight = 0;
    private int columnWidth = 0;
    private int ascent = 0;

	private int col1 = 0;
    private int col2 = 0;
    private int row1 = 0;
    private int row2 = 0;

    private RowDecorator rowDecorator = null;
    private ColumnDecorator columnDecorator = null;
    private CellDecorator cellDecorator = null;

    public AlignmentPane(TaxonPane taxonPane, RulerPane rulerPane) {
        this.taxonPane = taxonPane;
        this.rulerPane = rulerPane;
        setFont( new Font("monospaced", Font.PLAIN, 12));
    }

    public void setRowDecorator(RowDecorator rowDecorator) {
        this.rowDecorator = rowDecorator;
    }

    public void setColumnDecorator(ColumnDecorator columnDecorator) {
        this.columnDecorator = columnDecorator;
    }

    public void setCellDecorator(CellDecorator cellDecorator) {
        this.cellDecorator = cellDecorator;
    }

    public void setAlignmentBuffer(AlignmentBuffer alignment) {
        this.alignment = alignment;

        if (alignment != null) {
            rowCount = alignment.getSequenceCount();
            columnCount = alignment.getSiteCount();
        } else {
            rowCount = 0;
            columnCount = 0;

        }

        FontMetrics fm = getFontMetrics(getFont());
        rowHeight = fm.getHeight();
        columnWidth = fm.charWidth('M');
        ascent = fm.getAscent();

        if (taxonPane != null) {
            taxonPane.setRowHeight(rowHeight);
        }
        rulerPane.setColumnWidth(columnWidth);

        setPreferredSize(new Dimension(columnWidth * columnCount, rowHeight * rowCount));
        invalidate();
    }

	public Rectangle getVisibleArea() {
		return new Rectangle(col1, row1, col2 - col1 + 1, row2 - row1 + 1);
	}

	public void setTopRow(int row) {
		Rectangle rect = getVisibleRect();
		rect.y = getTop(row);
		scrollRectToVisible(rect);
	}

	public void setLeftColumn(int col) {
		Rectangle rect = getVisibleRect();
		rect.x = getLeft(col);
		scrollRectToVisible(rect);
	}

	public void setCentreColumn(int col) {
		Rectangle rect = getVisibleRect();
		rect.x = Math.max(0, (getLeft(col) + getLeft(col) + columnWidth - rect.width) / 2);
		scrollRectToVisible(rect);
	}

	public void setRightColumn(int col) {
		Rectangle rect = getVisibleRect();
		rect.x = Math.max(0, getLeft(col) + columnWidth - rect.width);
		scrollRectToVisible(rect);
	}

    public void paint(Graphics graphics) {
        if (alignment == null) return;

        Graphics2D g2 = (Graphics2D)graphics;

        String[] stateTable = alignment.getStateTable();

        Rectangle bounds = g2.getClipBounds();
        col1 = Math.min(getColumn(bounds.x), columnCount - 1);
        col2 = Math.min(getColumn(bounds.x + bounds.width), columnCount - 1);
        row1 = Math.min(getRow(bounds.y), rowCount - 1);
        row2 = Math.min(getRow(bounds.y + bounds.height), rowCount - 1);

        int x1 = getLeft(col1);
        int x2 = getLeft(col2 + 1);
        int y1 = getTop(row1);
        int y2 = getTop(row2 + 1);

        byte[] seq = new byte[columnCount];

        int y = y1;
        if (rowDecorator != null) {
            for (int row = row1; row <= row2; row++) {
                Paint rowBackground = rowDecorator.getRowBackground(row);
                if (rowBackground != null) {
                    g2.setPaint(rowBackground);
                    Rectangle rect = new Rectangle(x1, y, x2 - x1, rowHeight);
                    g2.fill(rect);
                }
                y += rowHeight;
            }
        }

        int x = x1;
//        if (columnDecorator != null) {
//            for (int col = col1; col <= col2; col++) {
//                Paint columnBackground = columnDecorator.getColumnBackground(col);
//                if (columnBackground != null) {
//                    g2.setPaint(columnBackground);
//                    Rectangle rect = new Rectangle(x, y1, columnWidth, y2 - y1);
//                    g2.fill(rect);
//                }
//                x += columnWidth;
//            }
//        }

        y = y1;
        for (int row = row1; row <= row2; row++) {
            alignment.getStates(row, col1, col2, seq);

            if (cellDecorator != null) {
                x = x1;
                int i = 0;
                for (int col = col1; col <= col2; col++) {

                    g2.setPaint(cellDecorator.getCellBackground(row, col, seq[i]));
                    Rectangle rect = new Rectangle(x, y, x + columnWidth, y + rowHeight);
                    g2.fill(rect);

                    g2.setPaint(cellDecorator.getCellForeground(row, col, seq[i]));
                    g2.drawString(stateTable[seq[i]], x, y + ascent);

                    x += columnWidth;
                    i++;
                }
            } else {
                g2.setPaint(Color.BLACK);
                x = x1;
                int i = 0;
                for (int col = col1; col <= col2; col++) {
                    g2.drawString(stateTable[seq[i]], x, y + ascent);

                    x += columnWidth;
                    i++;
                }
            }
            y += rowHeight;
        }

	    x = x1;
	    if (columnDecorator != null) {
	        for (int col = col1; col <= col2; col++) {
	            Paint columnBackground = columnDecorator.getColumnBackground(col);
	            if (columnBackground != null) {
	                g2.setPaint(columnBackground);
	                Rectangle rect = new Rectangle(x, y1, columnWidth, y2 - y1);
	                g2.fill(rect);
	            }
	            x += columnWidth;
	        }
	    }

    }

    public int getColumn(int x) {
        return x / columnWidth;
    }

    public int getLeft(int column) {
        return column * columnWidth;
    }

    public int getColumnWidth() {
        return columnWidth;
    }

    public int getColumnCount() {
        return columnCount;
    }

    public int getRow(int y) {
        return y / rowHeight;
    }

    public int getTop(int row) {
        return row * rowHeight;
    }

    public int getRowHeight() {
        return rowHeight;
    }

    public int getRowCount() {
        return rowCount;
    }

    public Dimension getPreferredSize() {
        return new Dimension(columnWidth * columnCount, rowHeight * rowCount);
    }

   public Dimension getPreferredScrollableViewportSize() {
        return getPreferredSize();
    }

    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        if (orientation == SwingConstants.HORIZONTAL) {
            return columnWidth;
        } else {
            return rowHeight;
        }
    }

    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        if (orientation == SwingConstants.HORIZONTAL) {
            return columnWidth * 10;
        } else {
            return rowHeight * 10;
        }
    }

    public boolean getScrollableTracksViewportWidth() {
        return false;
    }

    public boolean getScrollableTracksViewportHeight() {
        return false;
    }
}
