/*
 * BasicLabelPainter.java
 *
 * Copyright (C) 2006-2014 Andrew Rambaut
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package figtree.treeviewer.painters;

import figtree.treeviewer.TimeScale;
import figtree.treeviewer.TreePane;
import figtree.treeviewer.decorators.*;
import jebl.evolution.graphs.Node;
import jebl.evolution.taxa.Taxon;
import jebl.evolution.trees.RootedTree;
import jebl.evolution.trees.Tree;
import jebl.util.Attributable;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.List;

/**
 * A simple implementation of LabelPainter that can be used to display
 * tip, node or branch labels. It can display, taxon names, branch lengths,
 * node heights or other attributeNames of nodes.
 *
 * @author Andrew Rambaut
 * @version $Id$
 *
 * $HeadURL$
 *
 * $LastChangedBy$
 * $LastChangedDate$
 * $LastChangedRevision$
 */
public class BasicLabelPainter extends LabelPainter<Node> {

    public BasicLabelPainter(PainterIntent intent) {
        super(intent);

        this.displayAttribute = "";
    }

    public void setTreePane(TreePane treePane) {
        this.treePane = treePane;
    }

    public Decorator getBorderDecorator() {
        return borderDecorator;
    }

    public void setBorderDecorator(Decorator borderDecorator) {
        this.borderDecorator = borderDecorator;
        firePainterSettingsChanged();
    }

    public Decorator getTextDecorator() {
        return textDecorator;
    }

    public void setTextDecorator(Decorator textDecorator) {
        this.textDecorator = textDecorator;
        firePainterSettingsChanged();
    }

    public Tree getTree() {
        return treePane.getTree();
    }

    protected String getLabel(Tree tree, Node node) {
        if (displayAttribute.equalsIgnoreCase(NAMES)) {
            if (getIntent() == PainterIntent.TIP) {
                Taxon taxon = tree.getTaxon(node);
                if (taxon != null) {
                    if (textDecorator != null) {
                        textDecorator.setItem(taxon);
                    }
                    String name = (String)taxon.getAttribute("!name");
                    if (name != null) {
                        return name;
                    }
                    return taxon.getName();
                } else {
                    String name = (String)node.getAttribute("!name");
                    if (name != null) {
                        return name;
                    }
                    return null;
                }

            } else {
                String name = (String)node.getAttribute("!name");
                if (name != null) {
                    return name;
                }
                return null;
            }
        }

        if ( tree instanceof RootedTree) {
            final RootedTree rtree = (RootedTree) tree;

            if (textDecorator != null) {
                textDecorator.setItem(node);
            }

            if (displayAttribute.equalsIgnoreCase(NODE_AGES) ) {
                TimeScale timeScale = treePane.getTimeScale();
                double age = timeScale.getAge(rtree.getHeight(node), rtree);
                return getNumberFormat().format(age);
            } else if (displayAttribute.equalsIgnoreCase(NODE_HEIGHTS) ) {
                return getNumberFormat().format(rtree.getHeight(node));
            } else if (displayAttribute.equalsIgnoreCase(BRANCH_TIMES) ) {
                TimeScale timeScale = treePane.getTimeScale();
                double time = timeScale.getTime(rtree.getLength(node), rtree);
                return getNumberFormat().format(time);
            } else if (displayAttribute.equalsIgnoreCase(BRANCH_LENGTHS) ) {
                return getNumberFormat().format(rtree.getLength(node));
            }
        }

        Object value = null;

        if (getIntent() == PainterIntent.TIP) {
            Taxon taxon = tree.getTaxon(node);
            if (taxon != null) {
                value = taxon.getAttribute(displayAttribute);
            } else {
                value = node.getAttribute(displayAttribute);
            }
        }

        if (value == null) {
            value = node.getAttribute(displayAttribute);
        }

        return formatValue(value);
    }

    private String formatValue(Object value) {
        if (value != null) {
            if (value instanceof Double) {
                return getNumberFormat().format(value);
            } else if (value instanceof Object[]) {
                Object[] values = (Object[])value;

                if (values.length == 0) return null;
                if (values.length == 1) return formatValue(values[0]);

                StringBuilder builder = new StringBuilder("[");
                builder.append(formatValue(values[0]));
                for (int i = 1; i < values.length; i++) {
                    builder.append(",");
                    builder.append(formatValue(values[i]));
                }
                builder.append("]");
                return builder.toString();
            }
            return value.toString();
        }
        return null;
    }

    public Rectangle2D calibrate(Graphics2D g2, Node item) {
        Tree tree = treePane.getTree();

        String label = getLabel(tree, item);

        final Font oldFont = g2.getFont();
        if (textDecorator != null) {
            g2.setFont(textDecorator.getFont(getFont()));
        } else {
            g2.setFont(getFont());
        }

        FontMetrics fm = g2.getFontMetrics();
        preferredHeight = fm.getHeight();
        preferredWidth = 0;

        if (label != null) {
            Rectangle2D rect = fm.getStringBounds(label, g2);
            preferredWidth = rect.getWidth();
        }

        yOffset = (float)fm.getAscent();

        g2.setFont(oldFont);

        return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
    }

    public double getPreferredWidth() {
        return preferredWidth;
    }

    public double getPreferredHeight() {
        return preferredHeight;
    }

    public double getHeightBound() {
        return preferredHeight + yOffset;
    }

    public void paint(Graphics2D g2, Node item, Justification justification, Rectangle2D bounds) {
        Tree tree = treePane.getTree();

        if (TreePane.DEBUG_OUTLINE) {
            g2.setPaint(Color.red);
            g2.draw(bounds);
        }

        String label = getLabel(tree, item);

        Font oldFont = g2.getFont();

        Paint backgroundPaint = getBackground();
        Paint borderPaint = getBorderPaint();
        Stroke borderStroke = getBorderStroke();

        if (borderDecorator != null) {
            backgroundPaint = borderDecorator.getPaint(backgroundPaint);
            borderPaint = borderDecorator.getPaint(borderPaint);
            borderStroke = borderDecorator.getStroke(borderStroke);
        }

        if (backgroundPaint != null) {
            g2.setPaint(backgroundPaint);
            g2.fill(bounds);
        }

        if (borderPaint != null && borderStroke != null) {
            g2.setPaint(borderPaint);
            g2.setStroke(borderStroke);
            g2.draw(bounds);
        }

        if (textDecorator != null) {
            // this is a bit of a hack to detect whether we should be getting the
            // colour attribute from the taxon, or some other attribute from the
            // node:
            if (getIntent() == PainterIntent.TIP) {
                textDecorator.setItem(tree.getTaxon(item), (Node)item);
            } else {
                textDecorator.setItem(item);
            }

            g2.setPaint(textDecorator.getPaint(getForeground()));
            g2.setFont(textDecorator.getFont(getFont()));
        } else {
            g2.setPaint(getForeground());
            g2.setFont(getFont());
        }

        if (label != null) {

            Rectangle2D rect = g2.getFontMetrics().getStringBounds(label, g2);

            float xOffset;
            float y = yOffset + (float) bounds.getY();
            switch (justification) {
                case CENTER:
                    xOffset = (float)(-rect.getWidth()/2.0);
                    y = yOffset + (float) rect.getY();
//xOffset = (float) (bounds.getX() + (bounds.getWidth() - rect.getWidth()) / 2.0);
                    break;
                case FLUSH:
                case LEFT:
                    xOffset = (float) bounds.getX();
                    break;
                case RIGHT:
                    xOffset = (float) (bounds.getX() + bounds.getWidth() - rect.getWidth());
                    break;
                default:
                    throw new IllegalArgumentException("Unrecognized alignment enum option");
            }

            g2.drawString(label, xOffset, y);
        }

        g2.setFont(oldFont);
    }

    public String getDisplayAttribute() {
        if (displayAttribute.equalsIgnoreCase(NAMES) ||
                displayAttribute.equalsIgnoreCase(NODE_AGES) ||
                displayAttribute.equalsIgnoreCase(NODE_HEIGHTS) ||
                displayAttribute.equalsIgnoreCase(BRANCH_TIMES) ||
                displayAttribute.equalsIgnoreCase(BRANCH_LENGTHS)) {
            return null;
        }

        return displayAttribute;
    }

    public void setDisplayAttribute(String displayAttribute) {
        this.displayAttribute = displayAttribute;
        firePainterChanged();
    }

    private double preferredWidth;
    private double preferredHeight;
    private float yOffset;

    protected String displayAttribute;

    protected TreePane treePane;

    private Decorator textDecorator = null;
    private Decorator borderDecorator = null;


}
