/**
 *  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.menu.popup;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

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


/**
 * Displays a pop-up menu relating the currently marked objects
 * (in JaxoCanvas, if the user right-clicks on the canvas,
 * drag the mouse to enclose some objects and release the mouse).
 *
 * @since 2.0
 */
public class JaxoFBoxPopupMenu extends JPopupMenu implements ActionListener,
    JaxoLocalized {
    /**
     * The menu item for paste.
     *
     * @since 2.1
     */
    public static final int PASTE = 1;
    /**
     * The menu item for ungroup.
     *
     * @since 2.1
     */
    public static final int UNGROUP = 2;

    /**
     * The menu item for group.
     *
     * @since 2.1
     */
    public static final int GROUP = 3;

    private static final long serialVersionUID = 7526471155622776147L;
    private final ActionListener alistener;
    private final JMenu editM;
    private final JMenuItem cutMI;
    private final JMenuItem copyMI;
    private final JMenuItem pasteMI;
    private final JMenuItem ungroupMI;
    private final JMenuItem groupMI;
    private final JMenu orderM;
    private final JMenuItem foreMI;
    private final JMenuItem backMI;
    private final JMenu selectionM;
    private final JMenuItem saveasMI;
    private final JMenuItem exportMI;
    private final JMenuItem texpreviewMI;
    private boolean pasteMode;

    /**
     * Constructor.
     *
     * @since 2.1
     */
    public JaxoFBoxPopupMenu() {
        this(null);
    }

    /**
     * Constructor: sets all the menu entries and adds one action listener.
     *
     * @param listener an ActionListener to receive events from this PopupMenu. May be null.
     */
    public JaxoFBoxPopupMenu(final ActionListener listener) {
        super();
        this.alistener = listener;

        editM = new JMenu();

        cutMI = new JMenuItem();
        cutMI.addActionListener(this);
        cutMI.setActionCommand(JaxoConstants.getModeAsString(JaxoConstants.CUT));
        cutMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,
                ActionEvent.CTRL_MASK));

        copyMI = new JMenuItem();
        copyMI.addActionListener(this);
        copyMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.SCOPY));
        copyMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
                ActionEvent.CTRL_MASK));

        pasteMI = new JMenuItem();
        pasteMI.addActionListener(this);
        pasteMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.PASTE));
        pasteMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,
                ActionEvent.CTRL_MASK));

        editM.add(cutMI);
        editM.add(copyMI);
        editM.add(pasteMI);

        groupMI = new JMenuItem();
        groupMI.addActionListener(this);
        groupMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.SGROUP));
        groupMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G,
                ActionEvent.CTRL_MASK));

        ungroupMI = new JMenuItem();
        ungroupMI.addActionListener(this);
        ungroupMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.SUNGROUP));
        ungroupMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G,
                ActionEvent.SHIFT_MASK + ActionEvent.CTRL_MASK));

        orderM = new JMenu();

        foreMI = new JMenuItem();
        foreMI.addActionListener(this);
        foreMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.SFORE));

        backMI = new JMenuItem();
        backMI.addActionListener(this);
        backMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.SBACK));

        orderM.add(foreMI);
        orderM.add(backMI);

        selectionM = new JMenu();

        saveasMI = new JMenuItem();
        saveasMI.addActionListener(this);
        saveasMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.SAVE_SELECTION_AS));

        exportMI = new JMenuItem();
        exportMI.addActionListener(this);
        exportMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.EXPORT_SELECTION));
        texpreviewMI = new JMenuItem();
        texpreviewMI.addActionListener(this);
        texpreviewMI.setActionCommand(JaxoConstants.getModeAsString(
                JaxoConstants.LATEX_PREVIEW_SELECTION));

        selectionM.add(saveasMI);
        selectionM.add(exportMI);
        selectionM.add(texpreviewMI);

        this.add(editM);
        this.addSeparator();
        this.add(groupMI);
        this.add(ungroupMI);
        this.addSeparator();
        this.add(orderM);
        this.addSeparator();
        this.add(selectionM);

        updateLanguage();

        JaxoLooknFeel.registerComponent(this);
    }

    /**
     * Action events will be fire when the menu is "closed"
     * - either because an item was chosen or because it was cancelled.
     * @param l The ActionListener to add.
     */
    public void addActionListener(final ActionListener l) {
        listenerList.add(ActionListener.class, l);
    }

    /**
     * Removes the action listener.
     * @param l The ActionListener to remove.
     */
    public void removeActionListener(final ActionListener l) {
        listenerList.remove(ActionListener.class, l);
    }

    /**
     * Notifies all components of an action event.
     * @param actionCommand The action command of the action to be fire.
     */
    protected void fireActionPerformed(final String actionCommand) {
        fireActionPerformed(actionCommand, null);
    }

    /**
     * Notifies all components of an action event.
     *
     * @param actionCommand The action command of the action to be fire.
     * @param e the ActionEvent to be fired. May be null.
     * @since 2.1
     */
    protected void fireActionPerformed(final String actionCommand, final ActionEvent e) {
        final Object[] pairs = listenerList.getListenerList();

        for (int i = pairs.length - 2; i >= 0; i -= 2) {
            if (pairs[i] == ActionListener.class) {
                if (e == null) {
                    ((ActionListener) pairs[i + 1]).actionPerformed(
                            new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand));
                } else {
                    ((ActionListener) pairs[i + 1]).actionPerformed(e);
                }
            }
        }
    }

    /**
     * Enabled/Disables the given  menu item. Currently only works for
     * mitem = {@link #PASTE}, = {@link #GROUP}, = {@link #GROUP}.
     * @param mitem Integer specifying the menu item.
     * @param value True for enable, false for disable.
     */
    public final void setMenuItemEnabled(final int mitem, final boolean value) {
        if (mitem == PASTE) {
            pasteMI.setEnabled(value);
        } else if (mitem == UNGROUP) {
            ungroupMI.setEnabled(value);
        } else if (mitem == GROUP) {
            groupMI.setEnabled(value);
        }
    }

    /** Prepare for a popup only showing "Paste". */
    public final void onlyPastePopup() {
        if (!pasteMode) {
            pasteMode = true;
            for (int j = this.getComponentIndex(selectionM); j > -1; j--) {
                this.remove(j);
            }
            this.add(pasteMI);
        }
    }

    /** Prepare for a normal popup. */
    public final void normalPopup() {
        if (pasteMode) {
            pasteMode = false;
            this.remove(0);
            editM.add(pasteMI);
            this.add(editM);
            this.addSeparator();
            this.add(groupMI);
            this.add(ungroupMI);
            this.addSeparator();
            this.add(orderM);
            this.addSeparator();
            this.add(selectionM);
        }
    }

    /**
     * forward to main panel, then fire actionPerformed.
     * @param e The ActionEvent.
     */
    public void actionPerformed(final ActionEvent e) {
        if (alistener != null) {
            alistener.actionPerformed(e);
        }
        fireActionPerformed("action", e);
    }

    /** {@inheritDoc}, then fire actionPerformed. */
    @Override
    protected void firePopupMenuCanceled() {
        super.firePopupMenuCanceled();
        fireActionPerformed("canceled");
    }

    /**
     * Displays the popup menu.
     *
     * @param invoker the component in whose space the popup is to appear.
     * @param x the x-coordinate of the popup.
     * @param y the y-coordinate of the popup.
     */
    @Override
    public void show(final Component invoker, final int x, final int y) {
        final MenuSelectionManager m = MenuSelectionManager.defaultManager();
        final MenuElement[] path = m.getSelectedPath();

        // also firePopupMenuCanceled if the user chooses a disabled
        // menu item (really JPopupMenu bug)
        m.addChangeListener(new ChangeListener() {
                private MenuElement[] oldPath = path;

                public void stateChanged(final ChangeEvent e) {
                    final MenuSelectionManager m =
                        (MenuSelectionManager) e.getSource();

                    final MenuElement[] path = m.getSelectedPath();
                    boolean found = false;

                    for (int i = path.length - 1; i >= 0; --i) {
                        if (path[i] == JaxoFBoxPopupMenu.this) {
                            found = true;
                            break;
                        }
                    }

                    if (found) {
                        oldPath = m.getSelectedPath();
                    } else {
                        if ((oldPath.length > 0)
                            && (oldPath[oldPath.length - 1] instanceof JMenuItem)
                            && (!((JMenuItem) oldPath[oldPath.length - 1]).isEnabled())) {
                            firePopupMenuCanceled();
                        }
                        m.removeChangeListener(this);
                    }
                }
            });

        super.show(invoker, x, y);
    }

    /** {@inheritDoc} */
    public final void updateLanguage() {
        editM.setText(JaxoLanguage.translate("Edit"));
        cutMI.setText(JaxoLanguage.translate("Cut"));
        copyMI.setText(JaxoLanguage.translate("Copy"));
        pasteMI.setText(JaxoLanguage.translate("Paste"));
        groupMI.setText(JaxoLanguage.translate("group"));
        ungroupMI.setText(JaxoLanguage.translate("Ungroup"));
        orderM.setText(JaxoLanguage.translate("Order"));
        foreMI.setText(JaxoLanguage.translate("Foreground"));
        backMI.setText(JaxoLanguage.translate("Background"));
        selectionM.setText(JaxoLanguage.translate("Selection"));
        saveasMI.setText(JaxoLanguage.translate("Save_as"));
        exportMI.setText(JaxoLanguage.translate("Export"));
        texpreviewMI.setText(JaxoLanguage.translate("Latex_Preview"));
    }
}
