/* Copyright (C) 2005-2011 Fabio Riccardi */

package com.lightcrafts.ui.operation.colorbalance;

import com.lightcrafts.model.ColorPickerOperation;
import com.lightcrafts.model.Operation;
import com.lightcrafts.ui.help.HelpConstants;
import com.lightcrafts.ui.layout.Box;
import com.lightcrafts.ui.operation.OpStack;
import static com.lightcrafts.ui.operation.colorbalance.Locale.LOCALE;
import com.lightcrafts.ui.operation.generic.GenericControl;
import com.lightcrafts.ui.swing.ColorSwatch;
import com.lightcrafts.utils.xml.XMLException;
import com.lightcrafts.utils.xml.XmlNode;

import javax.swing.*;
import java.awt.*;
import java.util.Map;

/**
 * An OpControl to hold a ColorPickerOperation in an OpStack.  Provides
 * a color wheel to pick a color directly, and updates sliders accordingly.
 */
public class ColorPickerControl extends GenericControl {

    private ColorSwatch swatch;
    private ColorText text;
    private ColorWheel wheel;
    protected Box colorContent;

    // Track batch changes
    private boolean wasChanging;

    // Flag color wheel picks that just synchronize the color wheel with
    // a change commanded througha slider or the dropper:
    private boolean isColorWheelUpdating;

    public ColorPickerControl(ColorPickerOperation op, OpStack stack) {
        super(op, stack);
    }

    protected void operationChanged(Operation operation) {
        super.operationChanged(operation);

        Color color = ((ColorPickerOperation) operation).getColor();
        swatch = new ColorSwatch(color);
        text = new ColorText(color);

        wheel = new ColorWheel();
        ColorWheelMouseListener listener = new ColorWheelMouseListener(wheel) {
            void colorPicked(Color color, boolean isChanging) {
                // Colors from the ColorWheel are always fully bright.
                // This merges wheel color H and S with operation color B:
                color = getAdjustedWheelColor();
                setColor(color, isChanging);
            }
        };
        wheel.addMouseListener(listener);
        wheel.addMouseMotionListener(listener);

        colorContent = Box.createHorizontalBox();
        colorContent.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));

        colorContent.add(wheel);
        colorContent.add(Box.createHorizontalStrut(8));
        colorContent.add(swatch);
        colorContent.add(Box.createHorizontalStrut(8));
        colorContent.add(text);
        colorContent.add(Box.createHorizontalStrut(8));

        JComponent content = getContent();
        content.add(colorContent, 0);
        content.add(Box.createVerticalStrut(8), 0);
    }

    private void setColor(Color color, boolean isChanging) {
        if (isChanging && ! wasChanging) {
            Operation op = getOperation();
            op.changeBatchStarted();
        }
        else if (wasChanging && ! isChanging) {
            Operation op = getOperation();
            op.changeBatchEnded();
        }
        wasChanging = isChanging;

        ColorPickerOperation op = (ColorPickerOperation) getOperation();
        Map<String, Double> map = op.setColor(color);
        updateColor(map);
        if (! undoSupport.isRestoring() && ! isChanging) {
            undoSupport.postEdit(LOCALE.get("SetColorEditName"));
        }
    }

    private Color getColor() {
        return ((ColorPickerOperation) getOperation()).getColor();
    }

    protected void updateColor(Map<String, Double> sliders) {
        for (String key : sliders.keySet()) {
            double value = sliders.get(key);
            slewSlider(key, value);
        }
        Color color = getColor();
        swatch.setColor(color);
        text.setColor(color);
        if (! isColorWheelUpdating) {
            isColorWheelUpdating = true;
            wheel.pickColor(color);
            isColorWheelUpdating = false;
        }
    }

    // Colors from the Color wheel are always fully bright.  This method merges
    // the hue and saturation from the ColorWheel and the brightness from the
    // ColorPickerOperation into a third Color.
    private Color getAdjustedWheelColor() {
        Color wheelColor = wheel.getPickedColor();
        Color opColor = ((ColorPickerOperation) getOperation()).getColor();
        float[] wheelHsb = Color.RGBtoHSB(
            wheelColor.getRed(),
            wheelColor.getGreen(),
            wheelColor.getBlue(),
            null
        );
        float[] opHsb = Color.RGBtoHSB(
            opColor.getRed(),
            opColor.getGreen(),
            opColor.getBlue(),
            null
        );
        int rgb = Color.HSBtoRGB(wheelHsb[0], wheelHsb[1], opHsb[2]);
        return new Color(rgb);
    }

    private final static String ColorTag = "Color";

    public void save(XmlNode node) {
        super.save(node);
        Color color = getColor();
        XmlNode colorNode = node.addChild(ColorTag);
        colorNode.setAttribute("r", Integer.toString(color.getRed()));
        colorNode.setAttribute("g", Integer.toString(color.getGreen()));
        colorNode.setAttribute("b", Integer.toString(color.getBlue()));
    }

    public void restore(XmlNode node) throws XMLException {
        super.restore(node);
        XmlNode whiteNode = node.getChild(ColorTag);
        int r = Integer.parseInt(whiteNode.getAttribute("r"));
        int g = Integer.parseInt(whiteNode.getAttribute("g"));
        int b = Integer.parseInt(whiteNode.getAttribute("b"));
        Color color = new Color(r, g, b);
        undoSupport.restoreStart();
        setColor(color, false);
        undoSupport.restoreEnd();
    }

    protected String getHelpTopic() {
        return HelpConstants.HELP_TOOL_BLACK_AND_WHITE;
    }
}
