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

import figtree.treeviewer.decorators.*;
import jam.controlpalettes.AbstractController;
import jebl.evolution.graphs.Node;
import jebl.evolution.trees.Tree;
import jebl.util.Attributable;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.*;

/**
 * @author Andrew Rambaut
 * @version $Id$
 *
 * $HeadURL$
 *
 * $LastChangedBy$
 * $LastChangedDate$
 * $LastChangedRevision$
 */
public class AttributeColourController extends AbstractController {

    public static final String CONTROLLER_KEY = "colour";
    public static final String SCHEME_KEY = "scheme";
    public static final String ORDER_KEY = "order";

    public AttributeColourController(final TreeViewer treeViewer, final JFrame frame) {
        this.treeViewer = treeViewer;
        this.frame = frame;
    }

    public void setupControls(
            final JComboBox colourAttributeCombo,
            final JButton colourSetupButton) {

        if (colourSetupButton != null) {
            colourSetupButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent actionEvent) {
                    ColourDecorator decorator = null;
                    if (colourAttributeCombo.getSelectedIndex() > 0) {
                        String attribute = (String) colourAttributeCombo.getSelectedItem();
                        decorator = getDecoratorForAttribute(attribute);
                    }

                    if (decorator == null) {
                        return;
                    }

                    boolean update = false;
                    if (decorator instanceof DiscreteColourDecorator) {
                        if (discreteColourScaleDialog == null) {
                            discreteColourScaleDialog = new DiscreteColourScaleDialog(frame);
                        }
                        discreteColourScaleDialog.setDecorator((DiscreteColourDecorator)decorator);
                        int result = discreteColourScaleDialog.showDialog();
                        if (result != JOptionPane.CANCEL_OPTION && result != JOptionPane.CLOSED_OPTION) {
                            decorator = discreteColourScaleDialog.getDecorator();

                            String attribute = (String) colourAttributeCombo.getSelectedItem();
                            setDecoratorForAttribute(attribute, decorator);

                            update = true;
                        }
                    } else if (decorator instanceof ContinuousColourDecorator) {
                        if (continuousColourScaleDialog == null) {
                            continuousColourScaleDialog = new ContinuousColourScaleDialog(frame);
                        }
                        continuousColourScaleDialog.setDecorator((ContinuousColourDecorator)decorator);
                        int result = continuousColourScaleDialog.showDialog();
                        if (result != JOptionPane.CANCEL_OPTION && result != JOptionPane.CLOSED_OPTION) {

                            decorator = continuousColourScaleDialog.getDecorator();
                            String attribute = (String) colourAttributeCombo.getSelectedItem();
                            setDecoratorForAttribute(attribute, decorator);

                            update = true;
                        }
                    } else {
                        throw new IllegalArgumentException("Unsupported decorator type");
                    }

                    if (update) {
                        if (colourAttributeCombo.getSelectedIndex() > 0) {
                            String attribute = (String) colourAttributeCombo.getSelectedItem();
                            setDecoratorForAttribute(attribute, decorator);
                        }
                        fireControllerChanged();
                    }
                }
            });

            colourAttributeCombo.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent itemEvent) {
                    colourSetupButton.setEnabled(colourAttributeCombo.getSelectedIndex() > 0);
                }
            });

            colourSetupButton.setEnabled(colourAttributeCombo.getSelectedIndex() > 0);
        }

        // I don't think this is required and it throws up many, many events (i.e., every time
        // an attribute is added to the combo.
//        colourAttributeCombo.addActionListener(new ActionListener() {
//            public void actionPerformed(ActionEvent event) {
//                fireControllerChanged();
//            }
//        });
    }

    public Decorator getColourDecorator(JComboBox colourAttributeCombo, Decorator defaultDecorator) {
        Decorator decorator = defaultDecorator;
        if (defaultDecorator == null || colourAttributeCombo.getSelectedIndex() > 0) {
            String attribute = (String) colourAttributeCombo.getSelectedItem();
            if (attribute != null && attribute.length() > 0) {
                decorator = getDecoratorForAttribute(attribute);
            }
        }
        return decorator;
    }

    public ColourDecorator getDecoratorForAttribute(String attribute) {
        ColourDecorator colourDecorator = attributeDecoratorMap.get(attribute);

        Set<Attributable> items = new HashSet<Attributable>();
        for (Tree tree : treeViewer.getTrees()) {
            for (Node node : tree.getNodes()) {
                if (node.getAttribute(attribute) != null) {
                    items.add(node);
                }
                if (tree.getTaxon(node) != null) {
                    items.add(tree.getTaxon(node));
                }
            }
        }

        //

        if (colourDecorator == null) {

            if (attribute.endsWith("*")) {
                // todo reinstate branch colouring
                return null;
            } else if (DiscreteColourDecorator.isDiscrete(attribute, items)) {
                colourDecorator = new HSBDiscreteColourDecorator(attribute, items);
            } else {
                ContinuousScale scale = attributeScaleMap.get(attribute);
                if (scale == null) {
                    scale = new ContinuousScale();
                    attributeScaleMap.put(attribute, scale);
                }
                scale.setAttributes(attribute, items);

                colourDecorator = new HSBContinuousColourDecorator(scale);

            }
        } else if (colourDecorator instanceof DiscreteColourDecorator) {
            ((DiscreteColourDecorator)colourDecorator).setAttributes(attribute, items);
        } else if (colourDecorator instanceof ContinuousColourDecorator) {
            ((ContinuousColourDecorator)colourDecorator).setAttributes(attribute, items);
        }

        return colourDecorator;
    }

    private void setDecoratorForAttribute(String attribute, ColourDecorator decorator) {
        attributeDecoratorMap.put(attribute, decorator);
    }

    @Override
    public JComponent getTitleComponent() {
        return null;
    }

    @Override
    public JPanel getPanel() {
        return null;
    }

    @Override
    public boolean isInitiallyVisible() {
        return false;
    }

    @Override
    public void initialize() {
    }

    @Override
    public void setSettings(Map<String, Object> settings) {
        for (String key : settings.keySet()) {
            if (key.trim().startsWith(CONTROLLER_KEY + "." + SCHEME_KEY)) {

                String value = (String)settings.get(key);
                if (value != null) {
                    String[] parts = value.split(":");
                    if (parts.length == 2) {
                        String attribute = parts[0];
                        ColourDecorator decorator = getDecoratorForAttribute(attribute);

                        String colourSettings = parts[1];
                        if (colourSettings.startsWith("HSBDiscrete")) {
                            String settingsString = colourSettings.substring("HSBDiscrete".length());
                            if (decorator == null || !(decorator instanceof HSBDiscreteColourDecorator)) {
                                decorator = new HSBDiscreteColourDecorator(attribute, settingsString);
                            } else {
                                decorator.setup(settingsString);
                            }
                        } else if (colourSettings.startsWith("FixedDiscrete")) {
                            String settingsString = colourSettings.substring("FixedDiscrete".length());
                            if (decorator == null || !(decorator instanceof FixedDiscreteColourDecorator)) {
                                decorator = new FixedDiscreteColourDecorator(attribute, settingsString);
                            } else {
                                decorator.setup(settingsString);
                            }
                        } else if (colourSettings.startsWith("HSBContinuous")) {
                            String settingsString = colourSettings.substring("HSBContinuous".length());
                            if (decorator == null || !(decorator instanceof HSBDiscreteColourDecorator)) {
                                decorator = new HSBContinuousColourDecorator(attribute, settingsString);
                            } else {
                                decorator.setup(settingsString);
                            }
                        } else if (colourSettings.startsWith("InterpolatingContinuous")) {
                            String settingsString = colourSettings.substring("InterpolatingContinuous".length());
                            if (decorator == null || !(decorator instanceof InterpolatingColourDecorator)) {
                                decorator = new InterpolatingColourDecorator(attribute, settingsString);
                            } else {
                                decorator.setup(settingsString);
                            }
                        } else {
//                    throw new IllegalArgumentException("Unrecognized colour decorator type");
                        }
                        setDecoratorForAttribute(attribute, decorator);
                    }
                }
            } else if (key.trim().startsWith(CONTROLLER_KEY + "." + ORDER_KEY)) {
                String value = (String)settings.get(key);
                if (value != null) {
                    String[] parts = value.split(":");
                    if (parts.length == 2) {
                        String attribute = parts[0];
                        Object[] values = parts[1].split(",");
                        ColourDecorator decorator = getDecoratorForAttribute(attribute);
                        if (decorator != null && decorator instanceof DiscreteColourDecorator) {
                            ((DiscreteColourDecorator)decorator).setValuesOrder(Arrays.asList(values));
                            setDecoratorForAttribute(attribute, decorator);
                        }
                    }
                }
            }

        }
    }

    @Override
    public void getSettings(Map<String, Object> settings) {
        for (String attribute : attributeDecoratorMap.keySet()) {
            ColourDecorator decorator = attributeDecoratorMap.get(attribute);
            String colourSettings = decorator.toString();
            String name = "";
            if (decorator instanceof HSBDiscreteColourDecorator) {
                name = "HSBDiscrete";
            } else if (decorator instanceof FixedDiscreteColourDecorator) {
                name = "FixedDiscrete";
            } else if (decorator instanceof HSBContinuousColourDecorator) {
                name = "HSBContinuous";
            } else if (decorator instanceof InterpolatingColourDecorator) {
                name = "InterpolatingContinuous";
            } else {
                throw new IllegalArgumentException("Unrecognized colour decorator type");
            }
            settings.put(CONTROLLER_KEY + "." + SCHEME_KEY + "." + flattenName(attribute), attribute + ":" + name + colourSettings);
            if (decorator instanceof DiscreteColourDecorator) {
                if (((DiscreteColourDecorator)decorator).hasReorderedValues()) {
                    String orderString = ((DiscreteColourDecorator)decorator).getOrderString();
                    settings.put(CONTROLLER_KEY + "." + ORDER_KEY + "." + flattenName(attribute), attribute + ":" + orderString);

                }
            }
        }
    }

    private String flattenName(String name) {
        String flattened = name.trim().toLowerCase();
        flattened.replaceAll(" ", "_");
        flattened.replaceAll("\t", "_");
        flattened.replaceAll("\r", "_");
        return flattened;
    }

    private final TreeViewer treeViewer;
    private final JFrame frame;

    private Map<String, ColourDecorator> attributeDecoratorMap = new HashMap<String, ColourDecorator>();
    private Map<String, ContinuousScale> attributeScaleMap = new HashMap<String, ContinuousScale>();

//    private List<String> attributeNames = new ArrayList<String>();

//    private boolean editingComboBox = false;

    private ContinuousColourScaleDialog continuousColourScaleDialog = null;
    private DiscreteColourScaleDialog discreteColourScaleDialog = null;

//    // an internal listener interface
//    private interface Listener {
//        void attributesChanged();
//    }
//
//    private void addListener(Listener listener) {
//        listeners.add(listener);
//    }
//
//    private void fireAttributesChanged() {
//        for (Listener listener : listeners) {
//            listener.attributesChanged();
//        }
//
//    }
//    private final List<Listener> listeners = new ArrayList<Listener>();

}
