/*
 * ColourDecorator.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.decorators;

import jebl.evolution.graphs.Node;
import jebl.evolution.taxa.Taxon;
import jebl.util.Attributable;

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

/**
 * This decorator takes an attribute name and a set of attibutable Objects.
 * Colours are given to each individual value.
 *
 * If the data take more values than colors, then they will wrap around
 *
 * @author Andrew Rambaut
 * @version $Id$
 *
 * $HeadURL$
 *
 * $LastChangedBy$
 * $LastChangedDate$
 * $LastChangedRevision$
 */
public abstract class ColourDecorator implements Decorator {

    public ColourDecorator(String attributeName) {
        this.attributeName = attributeName;
    }

    public ColourDecorator(String attributeName, Set<? extends Attributable> items) {
        setAttributes(attributeName, items);
    }

    public abstract void setup(String settings);

    @Override
    public boolean allowsGradient() {
        return true;
    }

    public void setAttributes(String attributeName, Set<? extends Attributable> items) {
        this.attributeName = attributeName;
    }

    // Decorator INTERFACE
    public Paint getPaint(Paint paint) {
        if (this.paint == null) return paint;
        return this.paint;
    }

    public Paint getFillPaint(Paint paint) {
        if (this.fillPaint == null) return paint;
        return fillPaint;
    }

    public Paint getPaint(Paint paint, Point2D point1, Point2D point2) {
        if (colour1 != null && colour2 != null) {
            return new GradientPaint(point1, colour1, point2, colour2, false);
        } else {
            return getPaint(paint);
        }
    }

    public Paint getFillPaint(Paint paint, Point2D point1, Point2D point2) {
        if (fillColour1 != null && fillColour2 != null) {
            return new GradientPaint(point1, fillColour1, point2, fillColour2, false);
        } else {
            return getFillPaint(paint);
        }
    }

    public String getAttributeName() {
        return attributeName;
    }

    public Stroke getStroke(Stroke stroke) {
        return stroke;
    }

    public Font getFont(Font font) {
        return font;
    }

    public void setItem(Attributable item, Attributable fallbackItem) {
        setAttributableItem(item, fallbackItem);
    }

    public void setItem(Object item) {
        if (item instanceof Attributable) {
            setAttributableItem((Attributable)item, null);
        } else {
            paint = getColourForValue(item);
        }
    }

    public void setItems(Object item1, Object item2) {
        colour1 = null;
        colour2 = null;

        if (item2 == null) {
            setItem(item1);
            return;
        }

        if (item1 == null) {
            setItem(item2);
            return;
        }

        Object value1 = item1;
        if (item1 instanceof Attributable) {
            value1 = ((Attributable)item1).getAttribute(getAttributeName());
        }
        if (value1 != null) {
            colour1 = getColourForValue(value1);
            paint = colour1;
        }

        Object value2 = item2;
        if (item2 instanceof Attributable) {
            value2 = ((Attributable)item2).getAttribute(getAttributeName());
        }
        if (value2 != null) {
            colour2 = getColourForValue(value2);
            paint = colour2;
        }

        fillColour1 = null;
        if (colour1 != null) {
            fillColour1 = getLighterColour(colour1);

        }

        fillColour2 = null;
        if (colour2 != null) {
            fillColour2 = getLighterColour(colour2);
        }


    }

    public static boolean isDiscrete(String attributeName, Set<? extends Attributable> items) {
        // First collect the set of all attribute values
        Set<Object> values = new HashSet<Object>();
        for (Attributable item : items) {
            Object value = item.getAttribute(attributeName);
            if (value != null) {
                values.add(value);
            }
        }

        boolean isNumber = true;
        boolean isInteger = true;

        for (Object value : values) {
            if (value instanceof Number) {
                if (((Number)value).doubleValue() != ((Number)value).intValue()) {
                    isInteger = false;
                }
            } else {
                isNumber = false;
            }
        }

        if (isNumber && !isInteger) return false;

        return true;
    }

    public static boolean isNumerical(String attributeName, Set<? extends Attributable> items) {
        // First collect the set of all attribute values
        Set<Object> values = new HashSet<Object>();
        for (Attributable item : items) {
            Object value = item.getAttribute(attributeName);
            if (value != null) {
                values.add(value);
            }
        }

        boolean isNumber = true;

        for (Object value : values) {
            if (!(value instanceof Number)) {
                isNumber = false;
            }
        }

        return isNumber;
    }

    // Private methods
    private void setAttributableItem(Attributable item, Attributable fallbackItem) {
        Object value = item.getAttribute(attributeName);

        if (value == null && fallbackItem != null) {
            value = fallbackItem.getAttribute(attributeName);
        }

        paint = null;

        Color colour = getColourForValue(value);
        if (colour != null) {
            paint = colour;
            fillPaint = getLighterColour(colour);
        } else {
            paint = null;
            fillPaint = null;
        }
    }

    protected abstract Color getColourForValue(Object value);

    protected Color getLighterColour(Color color) {
        return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() / 2);
    }

    private String attributeName = null;

    protected Paint paint = null;
    protected Paint fillPaint = null;
    protected Color colour1 = null;
    protected Color colour2 = null;
    protected Color fillColour1 = null;
    protected Color fillColour2 = null;

}
