/*
 * TreeAppearanceController.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.painters.AttributeComboHelper;
import figtree.treeviewer.painters.AttributeComboHelperListener;
import figtree.treeviewer.painters.NodeShapePainter;
import jam.controlpalettes.ControllerListener;
import jam.controlpalettes.AbstractController;
import jam.panels.OptionsPanel;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.prefs.Preferences;

import figtree.treeviewer.decorators.*;
import jebl.evolution.trees.RootedTree;

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

    private static final String CONTROLLER_TITLE = "Appearance";

    public static Preferences PREFS = Preferences.userNodeForPackage(TreeViewer.class);

    public static final String CONTROLLER_KEY = "appearance";

    public static final String FOREGROUND_COLOUR_KEY = "foregroundColour";
    public static final String BACKGROUND_COLOUR_KEY = "backgroundColour";
    public static final String SELECTION_COLOUR_KEY = "selectionColour";
    public static final String BRANCH_COLOR_ATTRIBUTE_KEY = "branchColorAttribute";
    public static final String BRANCH_COLOR_GRADIENT_KEY = "branchColorGradient";
    public static final String HILIGHTING_GRADIENT_KEY = "hilightingGradient";
    public static final String BACKGROUND_COLOR_ATTRIBUTE_KEY = "backgroundColorAttribute";
    public static final String BRANCH_LINE_WIDTH_KEY = "branchLineWidth";
    public static final String BRANCH_MIN_LINE_WIDTH_KEY = "branchMinLineWidth";
    public static final String BRANCH_WIDTH_ATTRIBUTE_KEY = "branchWidthAttribute";

    // The defaults if there is nothing in the preferences
    public static Color DEFAULT_FOREGROUND_COLOUR = Color.BLACK;
    public static Color DEFAULT_BACKGROUND_COLOUR = Color.WHITE;
    public static Color DEFAULT_SELECTION_COLOUR = new Color(45, 54, 128);
    public static float DEFAULT_BRANCH_LINE_WIDTH = 1.0f;

    public static final String FIXED = "Fixed";

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

        userBranchColourDecorator = new AttributableDecorator();
        userBranchColourDecorator.setPaintAttributeName("!color");
        userBranchColourDecorator.setStrokeAttributeName("!stroke");
        treeViewer.setBranchDecorator(userBranchColourDecorator, false);

        int foregroundRGB = TreeAppearanceController.PREFS.getInt(CONTROLLER_KEY + "." + FOREGROUND_COLOUR_KEY, DEFAULT_FOREGROUND_COLOUR.getRGB());
        int backgroundRGB = TreeAppearanceController.PREFS.getInt(CONTROLLER_KEY + "." + BACKGROUND_COLOUR_KEY, DEFAULT_BACKGROUND_COLOUR.getRGB());
        int selectionRGB = TreeAppearanceController.PREFS.getInt(CONTROLLER_KEY + "." + SELECTION_COLOUR_KEY, DEFAULT_SELECTION_COLOUR.getRGB());
        float branchLineWidth = TreeAppearanceController.PREFS.getFloat(CONTROLLER_KEY + "." + BRANCH_LINE_WIDTH_KEY, DEFAULT_BRANCH_LINE_WIDTH);

        treeViewer.setForeground(new Color(foregroundRGB));
        treeViewer.setBackground(new Color(backgroundRGB));
        treeViewer.setSelectionColor(new Color(selectionRGB));
        treeViewer.setBranchStroke(new BasicStroke(branchLineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

        titleLabel = new JLabel(CONTROLLER_TITLE);

        optionsPanel = new ControllerOptionsPanel(2, 0);

        branchWidthAttributeCombo = new JComboBox(new String[] { "No attributes" });
        branchColourAttributeCombo = new JComboBox(new String[] { "No attributes" });
        backgroundColourAttributeCombo = new JComboBox(new String[] { "No attributes" });

//        setupAttributes(treeViewer.getTrees());

//        branchColourSettings.autoRange  = true;
//        branchColourSettings.fromValue = 0.0;
//        branchColourSettings.toValue = 1.0;
//        branchColourSettings.fromColour = new Color(0, 16, 192);
//        branchColourSettings.toColour = new Color(192, 16, 0);
//        branchColourSettings.middleColour = new Color(0, 0, 0);

        branchColourIsGradient = TreeAppearanceController.PREFS.getBoolean(CONTROLLER_KEY + "." + BRANCH_COLOR_GRADIENT_KEY, false);

        final JButton setupColourButton = new JButton("Colours");

        colourController.setupControls(branchColourAttributeCombo, setupColourButton);
        colourController.addControllerListener(new ControllerListener() {
            @Override
            public void controlsChanged() {
                setupBranchDecorators();
            }
        });

        branchColourGradientCheck = new JCheckBox("Gradient");
        branchColourGradientCheck.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent changeEvent) {
                branchColourIsGradient = branchColourGradientCheck.isSelected();
                setupBranchDecorators();
            }
        });
        optionsPanel.addComponentWithLabel("Colour by:", branchColourAttributeCombo);
        final JLabel setupColourButtonLabel = optionsPanel.addComponentWithLabel("Setup:", setupColourButton);
        optionsPanel.addComponent(branchColourGradientCheck);
        optionsPanel.addSeparator();

        boolean hilightingGradient = TreeAppearanceController.PREFS.getBoolean(CONTROLLER_KEY + "." + HILIGHTING_GRADIENT_KEY, false);

        hilightingGradientCheck = new JCheckBox("Hilight with gradient");
        hilightingGradientCheck.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent changeEvent) {
                treeViewer.setHilightingGradient(hilightingGradientCheck.isSelected());
            }
        });
        optionsPanel.addComponent(hilightingGradientCheck);
        hilightingGradientCheck.setSelected(hilightingGradient);
        optionsPanel.addSeparator();

        branchLineWidthSpinner = new JSpinner(new SpinnerNumberModel(1.0, 0.01, 48.0, 1.0));

        optionsPanel.addComponentWithLabel("Line Weight:", branchLineWidthSpinner);
        optionsPanel.addComponentWithLabel("Width by:", branchWidthAttributeCombo);

        branchMinLineWidthSpinner = new JSpinner(new SpinnerNumberModel(0.0, 0.0, 48.0, 1.0));
        final JLabel label = optionsPanel.addComponentWithLabel("Min Weight:", branchMinLineWidthSpinner);

        optionsPanel.addSeparator();

//        backgroundColourSettings.autoRange  = true;
//        backgroundColourSettings.fromValue = 0.0;
//        backgroundColourSettings.toValue = 1.0;
//        backgroundColourSettings.fromColour = new Color(255, 255, 255);
//        backgroundColourSettings.toColour = new Color(192, 16, 0);
//        backgroundColourSettings.middleColour = null;

        final JButton bgSetupColourButton = new JButton("Colours");
        colourController.setupControls(backgroundColourAttributeCombo, bgSetupColourButton);
        colourController.addControllerListener(new ControllerListener() {
            @Override
            public void controlsChanged() {
                setupBranchDecorators();
            }
        });

        optionsPanel.addComponentWithLabel("Background:", backgroundColourAttributeCombo);
        final JLabel bgSetupColourButtonLabel = optionsPanel.addComponentWithLabel("Setup:", bgSetupColourButton);

        new AttributeComboHelper(branchColourAttributeCombo, treeViewer, "User selection", false, true).addListener(new AttributeComboHelperListener() {
            @Override
            public void attributeComboChanged() {
                boolean isSelected = branchColourAttributeCombo.getSelectedIndex() != 0;
                setupColourButtonLabel.setEnabled(isSelected);
                setupColourButton.setEnabled(isSelected);

                setupBranchDecorators();
            }
        });

        new AttributeComboHelper(backgroundColourAttributeCombo, treeViewer, "Default").addListener(new AttributeComboHelperListener() {
            @Override
            public void attributeComboChanged() {
                boolean isSelected = backgroundColourAttributeCombo.getSelectedIndex() != 0;
                bgSetupColourButtonLabel.setEnabled(isSelected);
                bgSetupColourButton.setEnabled(isSelected);

                setupBranchDecorators();
            }
        });

        new AttributeComboHelper(branchWidthAttributeCombo, treeViewer, FIXED, true, false).addListener(new AttributeComboHelperListener() {
            @Override
            public void attributeComboChanged() {
                boolean isSelected = branchColourAttributeCombo.getSelectedIndex() != 0;
                label.setEnabled(isSelected);
                branchMinLineWidthSpinner.setEnabled(isSelected);
                setupBranchDecorators();
            }
        });

        branchLineWidthSpinner.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent changeEvent) {
                float lineWidth = ((Double) branchLineWidthSpinner.getValue()).floatValue();
                treeViewer.setBranchStroke(new BasicStroke(lineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
                setupBranchDecorators();
            }
        });
        branchMinLineWidthSpinner.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent changeEvent) {
                setupBranchDecorators();
            }
        });

    }

    private void setupBranchDecorators() {

        Decorator colourDecorator = colourController.getColourDecorator(branchColourAttributeCombo, userBranchColourDecorator);
//        DiversityContinuousColourDecorator colourDecorator = new DiversityContinuousColourDecorator();
//        colourDecorator.setTree((RootedTree)treeViewer.getCurrentTree());

//                if (colourDecorator == null) {
//                    if (attribute.endsWith("*")) {
//                        // This is a branch colouring (i.e., the colour can change
//                        // along the length of the branch...
//                        treeViewer.setBranchColouringDecorator(
//                                attribute.substring(0, attribute.length() - 2),
//                                new DiscreteColourDecorator());
//                        return;

//        if (colourDecorator != null && colourDecorator.isGradient()) {
//            // At present using a gradient precludes the use of the compoundDecorator
//            // and thus the branch width..
//            treeViewer.setBranchDecorator(colourDecorator);
//            return;
//        }

        CompoundDecorator compoundDecorator = new CompoundDecorator();

        if (colourDecorator != null) {
            treeViewer.setBranchColouringDecorator(null, null);
            compoundDecorator.addDecorator(colourDecorator);
        }

        if (branchWidthAttributeCombo.getSelectedIndex() > 0) {
            String attribute = (String) branchWidthAttributeCombo.getSelectedItem();
            ContinuousScale widthScale = new ContinuousScale(attribute, treeViewer.getTrees().get(0).getNodes());

            double fromWidth = (Double) branchMinLineWidthSpinner.getValue();
            double toWidth = (Double)branchLineWidthSpinner.getValue() + fromWidth;
            compoundDecorator.addDecorator(new ContinuousStrokeDecorator(
                    widthScale, (float)fromWidth, (float)toWidth)
            );
        }

        treeViewer.setBranchDecorator(compoundDecorator, branchColourIsGradient);

        Decorator backgroundDecorator = null;
        if (backgroundColourAttributeCombo.getSelectedIndex() > 1) {
            backgroundDecorator = colourController.getColourDecorator(backgroundColourAttributeCombo, null);
        }
        treeViewer.setNodeBackgroundDecorator(backgroundDecorator);

    }


    public JComponent getTitleComponent() {
        return titleLabel;
    }

    public JPanel getPanel() {
        return optionsPanel;
    }

    public boolean isInitiallyVisible() {
        return false;
    }

    public void initialize() {
        // nothing to do
    }

    public void setSettings(Map<String, Object> settings) {
        // These settings don't have controls yet but they will!
        treeViewer.setForeground((Color)settings.get(CONTROLLER_KEY + "." + FOREGROUND_COLOUR_KEY));
        treeViewer.setBackground((Color)settings.get(CONTROLLER_KEY + "." + BACKGROUND_COLOUR_KEY));
        treeViewer.setSelectionColor((Color)settings.get(CONTROLLER_KEY + "." + SELECTION_COLOUR_KEY));

        branchColourAttributeCombo.setSelectedItem(settings.get(CONTROLLER_KEY+"."+BRANCH_COLOR_ATTRIBUTE_KEY));
        branchColourGradientCheck.setSelected((Boolean)settings.get(CONTROLLER_KEY+"."+BRANCH_COLOR_GRADIENT_KEY));
        hilightingGradientCheck.setSelected((Boolean)settings.get(CONTROLLER_KEY+"."+HILIGHTING_GRADIENT_KEY));
        backgroundColourAttributeCombo.setSelectedItem(settings.get(CONTROLLER_KEY + "." + BACKGROUND_COLOR_ATTRIBUTE_KEY));
        branchLineWidthSpinner.setValue((Double) settings.get(CONTROLLER_KEY + "." + BRANCH_LINE_WIDTH_KEY));
        branchWidthAttributeCombo.setSelectedItem(settings.get(CONTROLLER_KEY+"."+BRANCH_WIDTH_ATTRIBUTE_KEY));
        branchMinLineWidthSpinner.setValue((Double) settings.get(CONTROLLER_KEY + "." + BRANCH_MIN_LINE_WIDTH_KEY));
    }

    public void getSettings(Map<String, Object> settings) {
        // These settings don't have controls yet but they will!
        settings.put(CONTROLLER_KEY + "." + FOREGROUND_COLOUR_KEY, treeViewer.getForeground());
        settings.put(CONTROLLER_KEY + "." + BACKGROUND_COLOUR_KEY, treeViewer.getBackground());
        settings.put(CONTROLLER_KEY + "." + SELECTION_COLOUR_KEY, treeViewer.getSelectionPaint());

        settings.put(CONTROLLER_KEY + "." + BRANCH_COLOR_ATTRIBUTE_KEY, branchColourAttributeCombo.getSelectedItem().toString());
        settings.put(CONTROLLER_KEY + "." + BRANCH_COLOR_GRADIENT_KEY, branchColourGradientCheck.isSelected());
        settings.put(CONTROLLER_KEY + "." + HILIGHTING_GRADIENT_KEY, hilightingGradientCheck.isSelected());
        settings.put(CONTROLLER_KEY + "." + BACKGROUND_COLOR_ATTRIBUTE_KEY, backgroundColourAttributeCombo.getSelectedItem().toString());
        settings.put(CONTROLLER_KEY + "." + BRANCH_LINE_WIDTH_KEY, branchLineWidthSpinner.getValue());
        settings.put(CONTROLLER_KEY + "." + BRANCH_WIDTH_ATTRIBUTE_KEY, branchWidthAttributeCombo.getSelectedItem().toString());
        settings.put(CONTROLLER_KEY + "." + BRANCH_MIN_LINE_WIDTH_KEY, branchMinLineWidthSpinner.getValue());
    }

    private final AttributeColourController colourController;

    private final AttributableDecorator userBranchColourDecorator;

    private final JLabel titleLabel;
    private final OptionsPanel optionsPanel;

    private final JComboBox branchColourAttributeCombo;
    private final JCheckBox branchColourGradientCheck;
    private final JCheckBox hilightingGradientCheck;
    private final JComboBox backgroundColourAttributeCombo;
    private final JSpinner branchLineWidthSpinner;
    private final JSpinner branchMinLineWidthSpinner;

    private final JComboBox branchWidthAttributeCombo;

    private final TreeViewer treeViewer;

    private boolean branchColourIsGradient = false;


}
