/*
 * DiscreteColourDecorator.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.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 class DiscreteColourDecorator extends ColourDecorator {

    public static Color[] DEFAULT_PAINTS = new Color[] {
            new Color(64,35,225),
            new Color(229,35,60),
            new Color(255,174,34),
            new Color(86,255,34),
            new Color(35,141,148),
            new Color(146,35,142),
            new Color(255,90,34),
            new Color(239,255,34),
            Color.DARK_GRAY,

            new Color(160,146,241),
            new Color(243,146,158),
            new Color(255,215,145),
            new Color(171,255,145),
            new Color(146,199,202),
            new Color(201,146,199),
            new Color(255,173,145),
            new Color(248,255,145),
            Color.LIGHT_GRAY,

            new Color(32,18,113),
            new Color(115,18,30),
            new Color(128,87,17),
            new Color(43,128,17),
            new Color(18,71,74),
            new Color(73,18,71),
            new Color(128,45,17),
            new Color(120,128,17)
    };

    public DiscreteColourDecorator() {
        super(null);
        colours = DEFAULT_PAINTS;
    }

    public DiscreteColourDecorator(String attributeName) {
        super(attributeName);
    }

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

    @Override
    public void setup(String settings) {
        throw new UnsupportedOperationException("setup from string not implemented");
    }

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

        // First collect the set of all attribute values
        Set<Object> sortedValues = new TreeSet<Object>();
        Set<Object> unsortedValues = new HashSet<Object>();

        for (Attributable item : items) {
            Object value = item.getAttribute(attributeName);
            if (value != null) {
                if (value instanceof Comparable) {
                    sortedValues.add(value);
                } else {
                    unsortedValues.add(value);
                }
            }
        }

        if (values == null) {
            values = new ArrayList<Object>(sortedValues);
            values.addAll(unsortedValues);
        } else {
            // if there is already a values array, only add the new values
            // to maintain the order that may have been edited by the user.

            for (Object value : sortedValues) {
                if (!values.contains(value)) {
                    values.add(value);
                }
            }
            for (Object value : unsortedValues) {
                if (!values.contains(value)) {
                    values.add(value);
                }
            }
        }

        setupColours();
    }

    public List<Object> getValues() {
        return values;
    }

    public void setValues(Collection<Object> values) {
        this.values = new ArrayList<Object>(values);
        setupColours();
    }

    protected void setupColours() {
        setColourMap(values, DEFAULT_PAINTS);
    }

    public Color getColor(Object value) {
        return (Color)colours[orderMap.get(value)];
    }

    protected void setColourMap(Collection<Object> values, Color[] colours) {
        if (values == null) {
            return;
        }

        orderMap = new TreeMap<Object, Integer>();

        this.colours = colours;

        this.values = new ArrayList<Object>(values);

        // now create a paint map for these values
        int i = 0;
        for (Object value : this.values) {
            orderMap.put(value, i);
            i = (i + 1) % colours.length;
        }

    }

    protected Color getColourForValue(Object value) {
        int index = -1;

        if (value != null) {
            if (orderMap != null && orderMap.size() > 0) {
                index = orderMap.get(value);
            } else if (value instanceof Number) {
                index = ((Number)value).intValue() % colours.length;
            }
        }
        if (index == -1) {
            return null;
        }
        return colours[index];
    }

    public void setValuesOrder(List<Object> discreteValues) {
        values = discreteValues;
        hasReorderedValues = true;
    }

    public boolean hasReorderedValues() {
        return hasReorderedValues;
    }

    public String getOrderString() {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Object value : values) {
            if (!first) {
                sb.append(",");
            } else {
                first = false;
            }
            sb.append(value);
        }
        return sb.toString();
    }

    private List<Object> values;
    private Map<Object, Integer> orderMap;
    private Color[] colours;

    private boolean hasReorderedValues = false;

}
