/*
 * #%L
 * gitools-ui-app
 * %%
 * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group
 * %%
 * 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 3 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, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
/*******************************************************************************
 * Copyright 2013 Lars Behnke
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package org.gitools.ui.app.analysis.clustering.visualization;

import org.gitools.heatmap.header.HierarchicalCluster;

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


public class ClusterComponent implements Paintable {

    private HierarchicalCluster cluster;
    private VCoord linkPoint;
    private VCoord initPoint;
    private boolean printName;
    private int dotRadius = 2;
    private int namePadding = 6;

    private List<ClusterComponent> children;

    public List<ClusterComponent> getChildren() {
        if (children == null) {
            children = new ArrayList<ClusterComponent>();
        }
        return children;
    }

    public int getNamePadding() {
        return namePadding;
    }

    public void setNamePadding(int namePadding) {
        this.namePadding = namePadding;
    }

    public int getDotRadius() {
        return dotRadius;
    }

    public void setDotRadius(int dotRadius) {
        this.dotRadius = dotRadius;
    }

    public void setChildren(List<ClusterComponent> children) {
        this.children = children;
    }

    public VCoord getLinkPoint() {
        return linkPoint;
    }

    public void setLinkPoint(VCoord linkPoint) {
        this.linkPoint = linkPoint;
    }

    public VCoord getInitPoint() {
        return initPoint;
    }

    public void setInitPoint(VCoord initPoint) {
        this.initPoint = initPoint;
    }

    public HierarchicalCluster getCluster() {
        return cluster;
    }

    public void setCluster(HierarchicalCluster cluster) {
        this.cluster = cluster;
    }

    public boolean isPrintName() {
        return printName;
    }

    public void setPrintName(boolean printName) {
        this.printName = printName;
    }

    public ClusterComponent(HierarchicalCluster cluster, boolean printName, VCoord initPoint) {
        this.printName = printName;
        this.cluster = cluster;
        this.initPoint = initPoint;
        this.linkPoint = initPoint;
    }

    @Override
    public void paint(Graphics2D g, int xDisplayOffset, int yDisplayOffset, double xDisplayFactor, double yDisplayFactor, boolean decorated) {
        int x1, y1, x2, y2;
        FontMetrics fontMetrics = g.getFontMetrics();
        x1 = (int) (initPoint.getX() * xDisplayFactor + xDisplayOffset);
        y1 = (int) (initPoint.getY() * yDisplayFactor + yDisplayOffset);
        x2 = (int) (linkPoint.getX() * xDisplayFactor + xDisplayOffset);
        y2 = y1;
        g.fillOval(x1 - dotRadius, y1 - dotRadius, dotRadius * 2, dotRadius * 2);
        g.drawLine(x1, y1, x2, y2);

        if (cluster.isLeaf()) {
            g.drawString(cluster.getName(), x1 + namePadding, y1 + (fontMetrics.getHeight() / 2) - 2);
        }
        if (decorated && cluster.getDistance() != null && !cluster.getDistance().isNaN() && cluster.getDistance() > 0) {
            String s = String.format("%.2f", cluster.getDistance());
            Rectangle2D rect = fontMetrics.getStringBounds(s, g);
            g.drawString(s, x1 - (int) rect.getWidth(), y1 - 2);
        }

        x1 = x2;
        y1 = y2;
        y2 = (int) (linkPoint.getY() * yDisplayFactor + yDisplayOffset);
        g.drawLine(x1, y1, x2, y2);


        for (ClusterComponent child : children) {
            child.paint(g, xDisplayOffset, yDisplayOffset, xDisplayFactor, yDisplayFactor, decorated);
        }
    }

    public double getRectMinX() {

        // TODO Better use closure / callback here
        assert initPoint != null && linkPoint != null;
        double val = Math.min(initPoint.getX(), linkPoint.getX());
        for (ClusterComponent child : getChildren()) {
            val = Math.min(val, child.getRectMinX());
        }
        return val;
    }

    public double getRectMinY() {

        // TODO Better use closure here
        assert initPoint != null && linkPoint != null;
        double val = Math.min(initPoint.getY(), linkPoint.getY());
        for (ClusterComponent child : getChildren()) {
            val = Math.min(val, child.getRectMinY());
        }
        return val;
    }

    public double getRectMaxX() {

        // TODO Better use closure here
        assert initPoint != null && linkPoint != null;
        double val = Math.max(initPoint.getX(), linkPoint.getX());
        for (ClusterComponent child : getChildren()) {
            val = Math.max(val, child.getRectMaxX());
        }
        return val;
    }

    public double getRectMaxY() {

        // TODO Better use closure here
        assert initPoint != null && linkPoint != null;
        double val = Math.max(initPoint.getY(), linkPoint.getY());
        for (ClusterComponent child : getChildren()) {
            val = Math.max(val, child.getRectMaxY());
        }
        return val;
    }

    public int getNameWidth(Graphics2D g, boolean includeNonLeafs) {
        int width = 0;
        if (includeNonLeafs || cluster.isLeaf()) {
            Rectangle2D rect = g.getFontMetrics().getStringBounds(cluster.getName(), g);
            width = (int) rect.getWidth();
        }
        return width;
    }

    public int getMaxNameWidth(Graphics2D g, boolean includeNonLeafs) {
        int width = getNameWidth(g, includeNonLeafs);
        for (ClusterComponent comp : getChildren()) {
            int childWidth = comp.getMaxNameWidth(g, includeNonLeafs);
            if (childWidth > width) {
                width = childWidth;
            }
        }
        return width;
    }
}
