/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander 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 3 of the License, or
 * (at your option) any later version.
 *
 * muCommander 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, see <http://www.gnu.org/licenses/>.
 */

package com.mucommander.ui.chooser;

import com.mucommander.commons.util.ui.dialog.FocusDialog;
import com.mucommander.commons.util.ui.layout.XBoxPanel;
import com.mucommander.commons.util.ui.layout.YBoxPanel;
import com.mucommander.text.Translator;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * Component used to let users pick a color.
 * <p>
 * The main reason for this component's existence is Swing's <code>JColorChooser</code> does not offer
 * alpha transparency edition, and that its localisation is incomplete. This is a wrapper that fixes both
 * of these shortcomings.
 * </p>
 * <p>
 * This component can be used either as a regular widget and be added to a <code>container</code> or as
 * a dialog box through the {@link #createDialog(Frame,ColorChooser) createDialog} method.
 * </p>
 * @author Nicolas Rinaudo, Maxence Bernard
 */
public class ColorChooser extends YBoxPanel implements ChangeListener {

    // - UI components ----------------------------------------------------------
    // --------------------------------------------------------------------------
    /** Component that displays a preview of the current color */
    private JComponent previewComponent;
    /** Color chooser. */
    private JColorChooser  chooser;
    /** Alpha transparency chooser. */
    private IntegerChooser alpha;


    // - Color editing ----------------------------------------------------------
    // --------------------------------------------------------------------------
    /** Currently selected color. */
    private Color          currentColor;
    /** Color on which the dialog was initialised. */
    private Color initialColor;
    /** Property to change in the preview component when the current color changes */
    private String previewColorPropertyName;


    // - Localisation -----------------------------------------------------------
    // --------------------------------------------------------------------------
    /**
     * Overrides the default, incomplete Swing translations.
     */
    static {
        String buffer;

        // Tab labels.
        UIManager.put("ColorChooser.rgbNameText",        Translator.get("color_chooser.rgb"));
        UIManager.put("ColorChooser.hsbNameText",        Translator.get("color_chooser.hsb"));
        UIManager.put("ColorChooser.swatchesNameText",   Translator.get("color_chooser.swatches"));

        // Red color name.
        UIManager.put("ColorChooser.rgbRedText",         buffer = Translator.get("color_chooser.red"));
        UIManager.put("ColorChooser.hsbRedText",         buffer);

        // Green color name.
        UIManager.put("ColorChooser.rgbGreenText",       buffer = Translator.get("color_chooser.green"));
        UIManager.put("ColorChooser.hsbGreenText",       buffer);

        // Blue color name.
        UIManager.put("ColorChooser.rgbBlueText",        buffer = Translator.get("color_chooser.blue"));
        UIManager.put("ColorChooser.hsbBlueText",        buffer);

        // HSB tab specific strings.
        UIManager.put("ColorChooser.hsbHueText",         Translator.get("color_chooser.hue"));
        UIManager.put("ColorChooser.hsbSaturationText",  Translator.get("color_chooser.saturation"));
        UIManager.put("ColorChooser.hsbBrightnessText",  Translator.get("color_chooser.brightness"));

        // Swatches tab specific strings.
        UIManager.put("ColorChooser.swatchesRecentText", Translator.get("color_chooser.recent"));
    }


    // - Initialisation ---------------------------------------------------------
    // --------------------------------------------------------------------------
    public ColorChooser() {
        this(Color.WHITE, null, null);
    }

    public ColorChooser(Color initialColor) {
        this(initialColor, null, null);
    }

    public ColorChooser(Color initialColor, JComponent previewComponent, String previewColorPropertyName) {
        this.currentColor  = initialColor;
        this.initialColor = initialColor;

        // Initialises the UI.
        add(createChooserPanel());
        add(createTransparencyPanel());

        alpha.setValue(initialColor.getAlpha());
        chooser.setColor(initialColor);

        if(previewComponent!=null && previewColorPropertyName!=null) {
            this.previewComponent = previewComponent;
            this.previewColorPropertyName = previewColorPropertyName;
            add(createPreviewPanel(previewComponent));

            updatePreview();
        }
    }



    // - Dialog creation --------------------------------------------------------
    // --------------------------------------------------------------------------
    /**
     * Creates a dialog containing the specified color chooser.
     * @param  parent  component on which to center the dialog.
     * @param  chooser chooser to use within the dialog.
     * @return         a dialog containing the specified chooser.
     */
    public static FocusDialog createDialog(Dialog parent, ColorChooser chooser) {
        return new ChooserDialog(parent, chooser);
    }

    /**
     * Creates a dialog containing the specified color chooser.
     * @param  parent  component on which to center the dialog.
     * @param  chooser chooser to use within the dialog.
     * @return         a dialog containing the specified chooser.
     */
    public static FocusDialog createDialog(Frame parent, ColorChooser chooser) {
        return new ChooserDialog(parent, chooser);
    }



    // - UI Initialisation ------------------------------------------------------
    // --------------------------------------------------------------------------


    /**
     * Creates the preview panel.
     */
    private JPanel createPreviewPanel(JComponent previewComponent) {
        JPanel    panel;
        Dimension size;

        // Sets the label's preferred size (same width as the chooser, twice the normal label height).
        size = previewComponent.getPreferredSize();
        size.width = chooser.getPreferredSize().width;
        size.height *= 2;
        previewComponent.setPreferredSize(size);

        // Sets the preview label appearance.
        previewComponent.setOpaque(true);

        // Creates the preview panel.
        panel = new JPanel();
        panel.add(previewComponent);
        panel.setBorder(BorderFactory.createTitledBorder(Translator.get("preview")));

        return panel;
    }

    /**
     * Creates the color chooser panel.
     */
    private JColorChooser createChooserPanel() {
        // Creates the color chooser.
        chooser = new JColorChooser();
        chooser.setPreviewPanel(new JPanel());
        chooser.getSelectionModel().addChangeListener(this);

        return chooser;
    }

    /**
     * Creates the transparency selection panel.
     */
    private JPanel createTransparencyPanel() {
        // Creates and initialises the transparency selector.
        alpha = new IntegerChooser(0, 255, 255);
        alpha.setMajorTickSpacing(85);
        alpha.setMinorTickSpacing(17);
        alpha.setPaintTicks(true);
        alpha.setPaintLabels(true);
        alpha.setBorder(BorderFactory.createTitledBorder(Translator.get("color_chooser.alpha")));
        alpha.addChangeListener(this);

        return alpha;
    }


    // - Color / font management ------------------------------------------------
    // --------------------------------------------------------------------------
    /**
     * Returns the color selected by the user.
     * @return the color selected by the user.
     */
    public Color getColor() {
        return currentColor;
    }

    /**
     * Resets the dialog to the initial color.
     */
    public void reset() {
        reset(true);
    }

    /**
     * Resets the dialog to the initial color.
     * @param updateUI if set to <code>false</code>, the component's UI won't be updated.
     */
    private void reset(boolean updateUI) {
        currentColor = initialColor;

        /// Propagates the color to the choosers.
        if(updateUI) {
            alpha.setValue(currentColor.getAlpha());
            chooser.setColor(currentColor);

            currentColor = initialColor;    // Need to set it again as the value is changed by stateChanged()
            updatePreview();
        }
    }

    /**
     * Update the preview panel to the current color.
     */
    private void updatePreview() {
        if(previewComponent!=null) {
            previewComponent.putClientProperty(previewColorPropertyName, currentColor);
        }
    }

    /**
     * This method is public as an implementation side effect and should not be called directly.
     */
    public void stateChanged(ChangeEvent e) {
        Color buffer;

        // Creates the new current color.
        buffer       = chooser.getColor();
        currentColor = new Color(buffer.getRed(), buffer.getGreen(), buffer.getBlue(), alpha.getValue());

        updatePreview();
    }


    // - Chooser dialog ---------------------------------------------------------
    // --------------------------------------------------------------------------
    /**
     * Component used to present a <code>ColorChooser</code> from within a modal dialog.
     * @author Nicolas Rinaudo
     */
    private static class ChooserDialog extends FocusDialog implements ActionListener {
        /** Resets the color to the original one. */
        private JButton resetButton;
        /** Closes the dialog without applying the color selection. */
        private JButton cancelButton;
        /** Color chooser contained by this dialog. */
        private ColorChooser chooser;

        /**
         * Creates a new dialog containing the specified color chooser.
         */
        public ChooserDialog(Frame parent, ColorChooser chooser) {
            super(parent, Translator.get("color_chooser.title"), parent);
            initUI(chooser);
        }

        /**
         * Creates a new dialog containing the specified color chooser.
         */
        public ChooserDialog(Dialog parent, ColorChooser chooser) {
            super(parent, Translator.get("color_chooser.title"), parent);
            initUI(chooser);
        }

        /**
         * Initialises the dialog's UI.
         */
        private void initUI(ColorChooser chooser) {
            Container   contentPane;

            this.chooser = chooser;

            // Initialises the dialog and its content pane.
            contentPane = getContentPane();
            contentPane.setLayout(new BorderLayout());

            // Creates the content of the dialog.
            contentPane.add(chooser, BorderLayout.CENTER);
            contentPane.add(createButtonsPanel(), BorderLayout.SOUTH);
        }

        /**
         * Creates the panel that contains the dialog's buttons.
         */
        private JPanel createButtonsPanel() {
            XBoxPanel buttonsPanel;
            JPanel    panel;
            JButton okButton;

            // Creates the panel and buttons.
            buttonsPanel = new XBoxPanel();
            buttonsPanel.add(resetButton = new JButton(Translator.get("reset")));
            buttonsPanel.addSpace(20);
            buttonsPanel.add(okButton = new JButton(Translator.get("ok")));
            buttonsPanel.add(cancelButton = new JButton(Translator.get("cancel")));

            // Tracks events.
            resetButton.addActionListener(this);
            okButton.addActionListener(this);
            cancelButton.addActionListener(this);

            // OK will be selected when the user presses enter.
            getRootPane().setDefaultButton(okButton);

            // Aligns the buttons to the right of the panel.
            panel = new JPanel();
            panel.setLayout(new FlowLayout(FlowLayout.RIGHT));
            panel.add(buttonsPanel);

            return panel;
        }

        /**
         * In case the dialog was cancelled, resets the color before closing it.
         */
        @Override
        public void cancel() {
            chooser.reset(false);
            super.cancel();
        }

        /**
         * This method is public as an implementation side effect and should not be called directly.
         */
        public void actionPerformed(ActionEvent e) {
            // Resets the current color.
            if(e.getSource() == resetButton)
                chooser.reset(true);

            // Closes the dialog, applying modifications if necessary.
            else {
                if(e.getSource() == cancelButton)
                    chooser.reset(false);
                dispose();
            }
        }
    }
}
