/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.gui.panel;

import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import net.sf.jaxodraw.gui.JaxoTab;
import net.sf.jaxodraw.gui.grid.JaxoDefaultGrid;
import net.sf.jaxodraw.gui.grid.JaxoPaintableGrid;
import net.sf.jaxodraw.gui.panel.button.JaxoColorButton;
import net.sf.jaxodraw.gui.swing.spinner.JaxoFixedJSpinner;
import net.sf.jaxodraw.gui.swing.spinner.JaxoSpinnerComboBox;
import net.sf.jaxodraw.gui.swing.spinner.JaxoSpinnerIntModel;
import net.sf.jaxodraw.util.JaxoColor;
import net.sf.jaxodraw.util.JaxoConstants;
import net.sf.jaxodraw.util.JaxoLanguage;
import net.sf.jaxodraw.util.JaxoLocalized;
import net.sf.jaxodraw.util.JaxoPrefs;


/** Responsible for displaying the grid options bar.
 * @since 2.0
 */
public class JaxoGridBar extends JPanel implements
    PropertyChangeListener, ChangeListener, JaxoLocalized {
    private static final long serialVersionUID = 7526471155622776147L;
    private static final List<Integer> GRID_SIZES;
    private static final String RECTANGULAR = "Rectangular";

    static {
        final List<Integer> tmp = new ArrayList<Integer>(8);

        tmp.add(Integer.valueOf(6));
        tmp.add(Integer.valueOf(8));
        tmp.add(Integer.valueOf(10));
        tmp.add(Integer.valueOf(12));
        tmp.add(Integer.valueOf(14));
        tmp.add(Integer.valueOf(18));
        tmp.add(Integer.valueOf(24));
        tmp.add(Integer.valueOf(36));

        GRID_SIZES = Collections.unmodifiableList(tmp);
    }

    private final JComboBox chooseGridType = new JComboBox(getGridCombTypes());
    private final JaxoSpinnerIntModel gridSize = new JaxoSpinnerIntModel(1, 76, 1, 0);
    private final JaxoSpinnerComboBox chooseGridSize =
        new JaxoSpinnerComboBox(new JaxoFixedJSpinner(gridSize), getGridSizeDefaults());
    private final JCheckBox gridCheckBox = new JCheckBox();
    private final JCheckBox snapCheckBox = new JCheckBox();
    private final JaxoCommunicator thePanel;
    private JaxoColorChooser colorChooser;
    private final JaxoColorButton gridColorButton = new JaxoColorButton();
    private final JaxoColorButton canvasBackgroundButton = new JaxoColorButton();
    private final JComboBox chooseGridStyle = new JComboBox(getGridStyles());
    private final JLabel gridTypeLabel = new JLabel("", JLabel.TRAILING);
    private final JLabel gridStyleLabel = new JLabel("", JLabel.TRAILING);
    private final JLabel gridSizeLabel = new JLabel("", JLabel.TRAILING);
    private final JLabel gridColorLabel = new JLabel("", JLabel.TRAILING);
    private final JLabel canvasBackgroundLabel = new JLabel("", JLabel.TRAILING);

    private transient boolean setting;

    /** Creates a new instance of JaxoGridBar.
     * @param panel An instance of the main panel.
     */
    public JaxoGridBar(final JaxoCommunicator panel) {
        super(new GridBagLayout());
        this.thePanel = panel;

        initComponents();

        updateLanguage();
    }

    /** List of numbers of default grid sizes to display to the user.
     * @return List
     */
    public static List<Integer> getGridSizeDefaults() {
        return GRID_SIZES;
    }

    private void initComponents() {
        final JPanel gOnOffPanel = getGridOnOffPanel();
        final JPanel gSnapOnOffPanel = getSnapOnOffPanel();
        final JPanel gTypePanel = getGridTypePanel();
        final JPanel gStylePanel = getGridStylePanel();
        final JPanel gColorPanel = getGridColorPanel();
        final JPanel gSizePanel = getGridSizePanel();
        final JPanel canvasBackgroundPanel = getCanvasBackgroundPanel();

        this.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));

        final GridBagConstraints sc = new GridBagConstraints();

        sc.gridx = 0;
        sc.gridy = 0;
        this.add(gOnOffPanel, sc);

        sc.gridx = 1;
        sc.gridy = 0;
        this.add(gSnapOnOffPanel, sc);

        sc.gridx = 2;
        sc.gridy = 0;
        this.add(gTypePanel, sc);

        sc.gridx = 3;
        sc.gridy = 0;
        this.add(gStylePanel, sc);

        sc.gridx = 4;
        sc.gridy = 0;
        this.add(gColorPanel, sc);

        sc.gridx = 5;
        sc.gridy = 0;
        this.add(gSizePanel, sc);

        sc.gridx = 6;
        sc.gridy = 0;
        this.add(canvasBackgroundPanel, sc);

        //Bogus component used to allign everything on the left
        final Component glue = Box.createHorizontalGlue();

        sc.gridx = 7;
        sc.gridy = 0;
        sc.weightx = 1;
        this.add(glue, sc);
    }

    private JPanel getGridOnOffPanel() {
        final JPanel gridCheckBoxPanel = new JPanel();

        gridCheckBoxPanel.add(gridCheckBox);

        gridCheckBox.addActionListener(thePanel);
        gridCheckBox.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.GRID_ONOFF));

        return gridCheckBoxPanel;
    }

    private JPanel getSnapOnOffPanel() {
        final JPanel snapCheckBoxPanel = new JPanel();

        snapCheckBox.setSelected(false);
        snapCheckBoxPanel.add(snapCheckBox);

        snapCheckBox.addActionListener(thePanel);
        snapCheckBox.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.GRID));

        return snapCheckBoxPanel;
    }

    private JPanel getGridTypePanel() {
        chooseGridType.addItemListener(new ItemListener() {
            public void itemStateChanged(final ItemEvent e) {
                if (e.getStateChange() != ItemEvent.SELECTED) {
                    return;
                }

                if (chooseGridType.getSelectedItem().equals(JaxoLanguage.translate(RECTANGULAR))) {
                    if (!setting) {
                        thePanel.actionPerformed(new ActionEvent(JaxoGridBar.this, JaxoConstants.RECTANGULAR_GRID,
                                JaxoConstants.getModeAsString(JaxoConstants.RECTANGULAR_GRID)));
                    }
                    if (chooseGridStyle.getItemCount() == (
                                JaxoPaintableGrid.STYLE_LINE_HONEYCOMB + 1
                            )) {
                        chooseGridStyle.removeItemAt(JaxoPaintableGrid.STYLE_LINE_HONEYCOMB);
                    }
                    gridSize.setIntStepSize(1);
                } else {
                    if (!setting) {
                        thePanel.actionPerformed(new ActionEvent(JaxoGridBar.this, JaxoConstants.HEXAGONAL_GRID,
                                JaxoConstants.getModeAsString(JaxoConstants.HEXAGONAL_GRID)));
                    }
                    if (chooseGridStyle.getItemCount() < (
                                JaxoPaintableGrid.STYLE_LINE_HONEYCOMB + 1
                            )) {
                        chooseGridStyle.insertItemAt(JaxoLanguage.translate(
                                "Honeycomb"), JaxoPaintableGrid.STYLE_LINE_HONEYCOMB);
                    }
                    gridSize.setIntStepSize(2);
                }
            }
        });

        final JPanel gridTypePanel = new JPanel();

        gridTypePanel.add(gridTypeLabel);
        gridTypePanel.add(chooseGridType);

        return gridTypePanel;
    }

    private static String[] getGridCombTypes() {
        final String[] gridCombTypes =
        {
            JaxoLanguage.translate(RECTANGULAR),
            JaxoLanguage.translate("Hexagonal")
        };

        return gridCombTypes;
    }

    private static String[] getGridStyles() {
        final String[] styles =
        {
            JaxoLanguage.translate("Dot"), JaxoLanguage.translate("Cross"),
            JaxoLanguage.translate("Line"), JaxoLanguage.translate("Honeycomb")
        };

        return styles;
    }

    private JPanel getGridStylePanel() {
        updateGridStyleWidth();

        final String type = (String) chooseGridType.getSelectedItem();
        if (JaxoLanguage.translate(RECTANGULAR).equals(type)) {
            chooseGridStyle.removeItemAt(JaxoPaintableGrid.STYLE_LINE_HONEYCOMB);
        }

        chooseGridStyle.addItemListener(new ItemListener() {
            public void itemStateChanged(final ItemEvent e) {
                if (e.getStateChange() != ItemEvent.SELECTED || setting) {
                    return;
                }

                final int style = getStyleFromIndex(chooseGridStyle.getSelectedIndex());
                thePanel.actionPerformed(new ActionEvent(JaxoGridBar.this, style,
                                JaxoConstants.getModeAsString(style)));
            }
        });

        final JPanel gridStylePanel = new JPanel();

        gridStylePanel.add(gridStyleLabel);
        gridStylePanel.add(chooseGridStyle);

        return gridStylePanel;
    }

    private JPanel getGridColorPanel() {
        final JPanel gridColorPanel = new JPanel();

        colorChooser = new JaxoColorChooser(gridColorPanel);
        gridColorButton.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(final PropertyChangeEvent e) {
                if (setting) {
                    return;
                }

                if (e.getPropertyName().equals("color")) {
                    thePanel.distributePropertyChange(
                            "Jaxo.gridColor", e.getOldValue(), e.getNewValue());
                }
            }
        });

        gridColorButton.setChooser(colorChooser);
        gridColorButton.setChooserMode(JaxoColor.ALL_COLORS_MODE);

        gridColorPanel.add(gridColorLabel);
        gridColorPanel.add(gridColorButton);

        return gridColorPanel;
    }

    private JPanel getCanvasBackgroundPanel() {
        final JPanel result = new JPanel();

        canvasBackgroundButton.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(final PropertyChangeEvent e) {
                if (setting) {
                    return;
                }

                if (e.getPropertyName().equals("color")) {
                    thePanel.distributePropertyChange(
                            "Jaxo.canvasBackground", e.getOldValue(), e.getNewValue());
                }
            }
        });

        canvasBackgroundButton.setChooser(colorChooser);
        canvasBackgroundButton.setChooserMode(JaxoColor.ALL_COLORS_MODE);

        result.add(canvasBackgroundLabel);
        result.add(canvasBackgroundButton);

        setCanvasBackground(JaxoColor.getColor(JaxoPrefs.getStringPref(
                JaxoPrefs.PREF_CANVASBACKGROUND), JaxoColor.ALL_COLORS_MODE));

        return result;
    }

    private JPanel getGridSizePanel() {
        final JPanel pgrid = new JPanel();

        pgrid.add(gridSizeLabel);

        gridSize.addChangeListener(new ChangeListener() {
            public void stateChanged(final ChangeEvent e) {
                if (setting) {
                    return;
                }

                thePanel.distributePropertyChange(
                        "Jaxo.gridSize", 0, gridSize.getIntValue());
            }
        });

        pgrid.add(chooseGridSize);

        return pgrid;
    }

    private static int getStyleFromIndex(final int selectedIndex) {
        int mode = -1;

        switch (selectedIndex) {
            case 0 :
                mode = JaxoConstants.GRID_STYLE_DOT;
                break;
            case 1 :
                mode = JaxoConstants.GRID_STYLE_CROSS;
                break;
            case 2 :
                mode = JaxoConstants.GRID_STYLE_LINE;
                break;
            case 3 :
                mode = JaxoConstants.GRID_STYLE_LINE_HONEYCOMB;
                break;
            default: break;
        }

        return mode;
    }

    /** Enable/Disable the gridBar according to the status of the
     * gridOn checkBox.
     *@param enable the status of the gridBar to be set
     **/
    private void enableGridBar(final boolean enable) {
        snapCheckBox.setEnabled(enable);
        chooseGridType.setEnabled(enable);
        gridTypeLabel.setEnabled(enable);
        chooseGridStyle.setEnabled(enable);
        gridStyleLabel.setEnabled(enable);
        gridColorButton.setEnabled(enable);
        gridColorLabel.setEnabled(enable);
        chooseGridSize.setEnabled(enable);
        gridSizeLabel.setEnabled(enable);
    }

    private void setGridOnOff(final boolean onoff) {
        this.gridCheckBox.setSelected(onoff);
        enableGridBar(onoff);
    }

    private void setSnapOnOff(final boolean onoff) {
        this.snapCheckBox.setSelected(onoff);
    }

    private void setCanvasBackground(final Color value) {
        canvasBackgroundButton.setColor(value);
    }

    private void updateTabSettings(final JaxoTab t) {
        updateGridSettings((t == null) ? JaxoDefaultGrid.newDefaultGrid()
                                       : t.getGrid());
    }

    /** Called when changing a tab, to reflect in the grid panel
     * the correct grid settings in the gridBar
     */
    private void updateGridSettings(final JaxoPaintableGrid tabGrid) {
        setting = true;

        try {
            chooseGridType.setSelectedIndex(tabGrid.getGridType());
            chooseGridStyle.setSelectedIndex(tabGrid.getGridStyle());
            gridSize.setIntValue(tabGrid.getGridSize());

            gridColorButton.setColor(tabGrid.getGridColor());
            setGridOnOff(tabGrid.isPainted());
            setSnapOnOff(tabGrid.isSnapping());
        } finally {
            setting = false;
        }
    }

    /** {@inheritDoc} */
    public final void updateLanguage() {
        gridTypeLabel.setText(JaxoLanguage.label("JaxoGridBar.type"));
        gridSizeLabel.setText(JaxoLanguage.label("JaxoGridBar.size"));
        gridStyleLabel.setText(JaxoLanguage.label("JaxoGridBar.style"));
        gridColorLabel.setText(JaxoLanguage.label("JaxoGridBar.color"));
        canvasBackgroundLabel.setText(JaxoLanguage.label(
                "JaxoGridBar.canvasBackground"));
        gridCheckBox.setText(JaxoLanguage.translate("JaxoGridBar.gridOn"));
        snapCheckBox.setText(JaxoLanguage.translate("JaxoGridBar.snapOn"));

        setting = true;

        try {
            final int selectedStyle = chooseGridStyle.getSelectedIndex();
            final int selectedType = chooseGridType.getSelectedIndex();

            chooseGridType.removeAllItems();
            final String[] gridCombTypes = getGridCombTypes();
            for (int i = 0; i < gridCombTypes.length; i++) {
                chooseGridType.addItem(gridCombTypes[i]);
            }
            chooseGridType.setSelectedIndex(selectedType);

            chooseGridStyle.removeAllItems();
            final String[] gridStyles = getGridStyles();
            for (int i = 0; i < gridStyles.length; i++) {
                chooseGridStyle.addItem(gridStyles[i]);
            }

            final String type = (String) chooseGridType.getSelectedItem();
            if (JaxoLanguage.translate(RECTANGULAR).equals(type)) {
                chooseGridStyle.removeItemAt(JaxoPaintableGrid.STYLE_LINE_HONEYCOMB);
            }
            chooseGridStyle.setSelectedIndex(selectedStyle);

            updateGridStyleWidth();
        } finally {
            setting = false;
        }
    }

    private void updateGridStyleWidth() {
        int width = -1;
        int widestIndex = -1;

        final String[] allGridStyles = getGridStyles();
        for (int i = 0; i < allGridStyles.length; i++) {
            chooseGridStyle.setPrototypeDisplayValue(allGridStyles[i]);
            final int currentWidth = chooseGridStyle.getPreferredSize().width;

            if (currentWidth > width) {
                width = currentWidth;
                widestIndex = i;
            }
        }

        chooseGridStyle.setPrototypeDisplayValue(allGridStyles[widestIndex]);
    }

    /** {@inheritDoc} */
        public void propertyChange(final PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals("Jaxo.selectedTab")) {
                final JaxoTab old = (JaxoTab) evt.getOldValue();

                if (old != null) {
                    old.getGrid().removeChangeListener(this);
                }

                final JaxoTab t = (JaxoTab) evt.getNewValue();

                if (t != null) {
                    t.getGrid().addChangeListener(this);
                }

                updateTabSettings(t);
            } else if (evt.getPropertyName().equals("Jaxo.gridOn")) {
                setGridOnOff(((Boolean) evt.getNewValue()).booleanValue());
            } else if (evt.getPropertyName().equals("Jaxo.snap")) {
                setSnapOnOff(((Boolean) evt.getNewValue()).booleanValue());
            } else if (evt.getPropertyName().equals("Jaxo.canvasBackground")) {
                setCanvasBackground((Color) evt.getNewValue());
            }
        }

        /** {@inheritDoc} */
        public void stateChanged(final ChangeEvent e) {
            updateGridSettings((JaxoPaintableGrid) e.getSource());
        }
}
