/**
 *  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.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;

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

import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.border.EtchedBorder;

import net.sf.jaxodraw.util.JaxoConstants;
import net.sf.jaxodraw.util.JaxoLanguage;
import net.sf.jaxodraw.util.JaxoLocalized;
import net.sf.jaxodraw.util.JaxoUtils;


/** Responsible for displaying the buttons in the toolBar.
 * @since 2.0
 */
public class JaxoToolBar extends JToolBar implements PropertyChangeListener,
    JaxoLocalized {
    private static final long serialVersionUID = 7526471155622776147L;
    private static final ButtonInfo[] BUTTON_INFOS =
        new ButtonInfo[]{
            new ButtonInfo("filenew.png", "filenewro.png", JaxoConstants.NEWG,
                "New", null, KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK)),
            new ButtonInfo("folderclose.png", "folderopen.png",
                JaxoConstants.OPEN, "Open", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK)),
            new ButtonInfo("closecb.png", "closecbro.png",
                JaxoConstants.CLOSE, "Close", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK + InputEvent.SHIFT_DOWN_MASK)),
            new ButtonInfo("filesave.png", "filesavero.png",
                JaxoConstants.SAVE, "Save", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)),
            new ButtonInfo("filesaveas.png", "filesaveasro.png",
                JaxoConstants.SAVE_AS, "Save_as", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK + InputEvent.SHIFT_DOWN_MASK)),
            new ButtonInfo("fileimport.png", "fileimportro.png",
                JaxoConstants.IMPORT, "Import", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK)),
            new ButtonInfo("fileexport.png", "fileexportro.png",
                JaxoConstants.EXPORT, "Export", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK + InputEvent.SHIFT_DOWN_MASK)),
            new ButtonInfo("fileprint.png", "fileprintro.png",
                JaxoConstants.PRINT, "Print", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK)),
            new ButtonInfo("texpreview.png", "texpreviewro.png",
                JaxoConstants.PREVIEW, "Latex_Preview", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.ALT_DOWN_MASK)),
            new ButtonInfo("watchModeOff.png", "watchModeRo.png",
                JaxoConstants.WATCHFILE, "Watch_file_mode", "watchModeOn.png"),
            new ButtonInfo("paste.png", "pastero.png", JaxoConstants.PASTE,
                "Paste", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK)),
            new ButtonInfo("prefs.png", "prefsro.png",
                JaxoConstants.PREFERENCES, "Preferences", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.CTRL_DOWN_MASK)),
            new ButtonInfo(ButtonInfo.GLUE),
            new ButtonInfo("info.png", "inforo.png", JaxoConstants.USR_GUIDE,
                "User_guide", null,
                KeyStroke.getKeyStroke(KeyEvent.VK_G, InputEvent.SHIFT_DOWN_MASK)),
        };
    private static final int WATCH_MODE_INDEX = 9;
    private final AbstractButton[] toolBarButtons;
    private final JToggleButton watchModeButton;

    /**
     * Constructor: lays out the toolBar.
     *
     * @param listener the ActionListener to receive events from this ToolBar.
     */
    public JaxoToolBar(final ActionListener listener) {
        super();
        setFloatable(false);
        setRollover(true); // not really much sense if borders are turned off
        setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));

        toolBarButtons = new AbstractButton[BUTTON_INFOS.length];

        for (int i = 0; i < BUTTON_INFOS.length; i++) {
            switch (BUTTON_INFOS[i].getType()) {
                case ButtonInfo.GLUE:
                    add(Box.createHorizontalGlue());
                    break;
                case ButtonInfo.SEPARATOR:
                    addSeparator();
                    break;
                case ButtonInfo.BUTTON:
                    toolBarButtons[i] =
                        (BUTTON_INFOS[i].getSelectedIcon() == null)
                        ? new JButton() : (AbstractButton) new JToggleButton();
                    toolBarButtons[i].setIcon(JaxoUtils.newImageIcon(
                            BUTTON_INFOS[i].getIcon()));

                    toolBarButtons[i].setRolloverIcon(JaxoUtils.newImageIcon(
                            BUTTON_INFOS[i].getRolloverIcon()));
                    toolBarButtons[i].setPressedIcon(toolBarButtons[i]
                        .getRolloverIcon());

                    if (BUTTON_INFOS[i].getSelectedIcon() != null) {
                        toolBarButtons[i].setSelectedIcon(JaxoUtils
                            .newImageIcon(BUTTON_INFOS[i].getSelectedIcon()));
                        toolBarButtons[i].setRolloverSelectedIcon(toolBarButtons[i]
                            .getRolloverIcon());
                    }

                    toolBarButtons[i].setActionCommand(JaxoConstants
                        .getModeAsString(BUTTON_INFOS[i].getMode()));

                    if (BUTTON_INFOS[i].getKeyStroke() != null) {
                        InputMap inputMap = toolBarButtons[i].getInputMap(JButton.WHEN_IN_FOCUSED_WINDOW);
                        inputMap.put(BUTTON_INFOS[i].getKeyStroke(), BUTTON_INFOS[i].getToolTipKey());
                        toolBarButtons[i].getActionMap().put(
                            BUTTON_INFOS[i].getToolTipKey(), new ClickAction(toolBarButtons[i]));
                    }

                    // cut off extravagant horizontal margin.
                    final Insets n = toolBarButtons[i].getMargin();
                    final int l = Math.max(n.top, n.bottom);
                    toolBarButtons[i].setMargin(new Insets(n.top, l, n.bottom,
                            l));

                    toolBarButtons[i].setBorderPainted(false);
                    toolBarButtons[i].setRolloverEnabled(true);
                    toolBarButtons[i].setFocusPainted(false);
                    toolBarButtons[i].setContentAreaFilled(false);

                    add(toolBarButtons[i]);
                    break;
                default:
                    break;
            }
        }

        watchModeButton = (JToggleButton) toolBarButtons[WATCH_MODE_INDEX];

        updateLanguage();
        setWatchMode(false);
        addActionListener(listener);
    }

    /**
     * Applies a property change event.
     *
     * @param e the change event.
     */
    public void propertyChange(final PropertyChangeEvent e) {
        setWatchMode(Boolean.TRUE.equals(e.getNewValue()));
    }

    /** Adds the given ActionListener to all buttons in this panel.
    * @param l The ActionListener to add.
    */
    private void addActionListener(final ActionListener l) {
        for (int i = 0; i < BUTTON_INFOS.length; i++) {
            if (BUTTON_INFOS[i].getType() == ButtonInfo.BUTTON) {
                toolBarButtons[i].addActionListener(l);
            }
        }
    }

    /** Notification from the main panel on watchMode changes.
        Adjusts the watchModeButton state.
     * @param value Whether watchmode is on
     */
    private void setWatchMode(final boolean value) {
        watchModeButton.setSelected(value);
    }

    /** {@inheritDoc} */
    public final void updateLanguage() {
        for (int i = 0; i < BUTTON_INFOS.length; i++) {
            if (toolBarButtons[i] != null) {
                toolBarButtons[i].setToolTipText(JaxoLanguage.translate(
                        BUTTON_INFOS[i].getToolTipKey()));
            }
        }
    }

    private static class ClickAction extends AbstractAction {
        private static final long serialVersionUID = 1L;
        private final AbstractButton button;

        ClickAction(final AbstractButton abutton) {
            this.button = abutton;
        }

        public void actionPerformed(final ActionEvent e) {
            button.doClick();
        }
    }

    /** Information about a potential button, or separator, or glue.
     * If 'selectedIcon' is not 'null', a JToggleButton will be used,
     * otherwise a JButton.
     */
    private static class ButtonInfo {
        /** ButtonInfo Button type. */
        public static final int BUTTON = 0;

        /** ButtonInfo Separator type. */
        public static final int SEPARATOR = 1;

        /** ButtonInfo Glue type. */
        public static final int GLUE = 2;

        //private variables
        private final int type;
        private final String icon;
        private final String rolloverIcon;
        private final int mode;
        private final String toolTipKey;
        private final String selectedIcon;
        private final KeyStroke keyStroke;

        /** Constructor: This should only be used for type = SEPARATOR or GLUE.
         * @param newType The type of this ButtonInfo.
         */
        ButtonInfo(final int newType) {
            if (!((newType == SEPARATOR) || (newType == GLUE))) {
                throw new IllegalArgumentException("Wrong ButtonInfo type!");
            }
            this.type = newType;
            this.icon = null;
            this.rolloverIcon = null;
            this.mode = -1;
            this.toolTipKey = null;
            this.selectedIcon = null;
            this.keyStroke = null;
        }

        /** Constructor: This gives type = BUTTON and selectedIcon = null.
         * @param newIcon The icon of this ButtonInfo.
         * @param newRolloverIcon The rollover Icon.
         * @param newMode The mode of this ButtonInfo.
         * @param newToolTipKey The tooltip key.
         */
        ButtonInfo(final String newIcon, final String newRolloverIcon, final int newMode,
            final String newToolTipKey) {
            this(newIcon, newRolloverIcon, newMode, newToolTipKey, null);
        }

        /** Constructor: This gives a type = BUTTON.
         * @param newIcon The icon of this ButtonInfo.
         * @param newRolloverIcon The rollover Icon.
         * @param newMode The mode of this ButtonInfo.
         * @param newToolTipKey The tooltip key.
         * @param newSelectedIcon the selected icon.
         */
        ButtonInfo(final String newIcon, final String newRolloverIcon, final int newMode,
            final String newToolTipKey, final String newSelectedIcon) {
            this(newIcon, newRolloverIcon, newMode, newToolTipKey, newSelectedIcon, null);
        }

        /** Constructor: This gives a type = BUTTON.
         * @param newIcon The icon of this ButtonInfo.
         * @param newRolloverIcon The rollover Icon.
         * @param newMode The mode of this ButtonInfo.
         * @param newToolTipKey The tooltip key.
         * @param newSelectedIcon the selected icon.
         */
        ButtonInfo(final String newIcon, final String newRolloverIcon, final int newMode,
            final String newToolTipKey, final String newSelectedIcon, final KeyStroke newKeyStroke) {
            this.type = BUTTON;
            this.icon = newIcon;
            this.rolloverIcon = newRolloverIcon;
            this.mode = newMode;
            this.toolTipKey = newToolTipKey;
            this.selectedIcon = newSelectedIcon;
            this.keyStroke = newKeyStroke;
        }

        /** Returns the type of this ButtonInfo.
         * @return The type of this ButtonInfo.
         */
        public int getType() {
            return type;
        }

        /** Returns the icon of this ButtonInfo.
         * @return The icon of this ButtonInfo.
         */
        public String getIcon() {
            return icon;
        }

        /** Returns the rollover icon of this ButtonInfo.
         * @return The rollover icon of this ButtonInfo.
         */
        public String getRolloverIcon() {
            return rolloverIcon;
        }

        /** Returns the mode of this ButtonInfo.
         * @return The mode of this ButtonInfo.
         */
        public int getMode() {
            return mode;
        }

        /** Returns the tooltip key of this ButtonInfo.
         * @return The tooltip key of this ButtonInfo.
         */
        public String getToolTipKey() {
            return toolTipKey;
        }

        /** Returns the selected icon of this ButtonInfo.
         * @return The selected icon of this ButtonInfo.
         */
        public String getSelectedIcon() {
            return selectedIcon;
        }

        /** Returns the keyboard shortcut keyStroke of this ButtonInfo.
         * @return The keyboard shortcut keyStroke of this ButtonInfo.
         */
        public KeyStroke getKeyStroke() {
            return keyStroke;
        }
    }
}
