/*
 * @(#)ToolButton.java 5.1
 *
 */

package CH.ifa.draw.standard;

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;

import CH.ifa.draw.DrawPlugin;
import CH.ifa.draw.application.DrawApplication;
import CH.ifa.draw.util.PaletteListener;
import de.renew.draw.ui.ontology.Tool;
import de.renew.plugin.PluginManager;
import de.renew.plugin.PluginProperties;


/**
 * A PaletteButton that is associated with a tool.
 *
 * @see Tool
 */
public class ToolButton implements ActionListener, MouseListener, KeyListener {
    private static final String PROP_PRE = "de.renew.keymap.";
    public static final int POPUP_MENU = 0;
    public static final int DIALOG = 1;

    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(ToolButton.class);
    private String _name;
    private Tool _tool;
    private JToggleButton _button;
    private PaletteListener _listener;
    private char _keyChar;
    private final ArrayList<Component> _additionalComponents = new ArrayList<>();
    private int _alternativeMode = DIALOG;

    public ToolButton(PaletteListener listener, String iconName, String name, Tool tool) {
        ImageIcon[] icons = createIconsByGuess(iconName);
        createButton(listener, icons[0], icons[1], name, tool);
        associateKey(name.replaceAll(" ", "_"));
    }

    public ToolButton(
        PaletteListener listener, Icon icon, Icon selectedIcon, String name, Tool tool)
    {
        createButton(listener, icon, selectedIcon, name, tool);
        associateKey(name.replaceAll(" ", "_"));
    }

    private void associateKey(String modifiedName) {
        DrawPlugin app = DrawPlugin.getCurrent();
        if (app != null) {
            PluginProperties props = app.getProperties();
            if (props.getBoolProperty(PROP_PRE + "use-mapping")) {
                String key = props.getProperty(PROP_PRE + modifiedName);
                if (key != null) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug(
                            ToolButton.class.getName() + ": found key mapping entry: " + key);
                    }
                    if (key.trim().length() == 1) {
                        //assumption: if a tool button is created the gui exists

                        final ToolButton handle = this;
                        Runnable r = new Runnable() {
                            int _count = 0;

                            @Override
                            public void run() {
                                DrawApplication gui = DrawPlugin.getGui();

                                if (gui != null) {

                                    JFrame frame = gui.getFrame();
                                    if (frame != null) {
                                        frame.addKeyListener(handle);
                                    } else if (_count < 10) {
                                        _count++;
                                        EventQueue.invokeLater(this);
                                    }
                                } else if (_count < 10) {
                                    _count++;
                                    EventQueue.invokeLater(this);
                                }


                            }
                        };
                        EventQueue.invokeLater(r);
                        _keyChar = key.trim().charAt(0);
                    }
                }
            }
        }
    }

    private void createButton(
        PaletteListener listener, Icon icon, Icon selectedIcon, String name, Tool tool)
    {

        _button = new JToggleButton();

        LOGGER.debug("ToolButton overwrites setSelected method in it's model.");
        _button.setModel(new ToolButtonModel());
        _button.setIcon(icon);
        _button.setToolTipText(name);
        _button.setName(name);
        if (selectedIcon != null) {
            _button.setPressedIcon(selectedIcon);
        }
        _button.addMouseListener(this);
        _tool = tool;
        _name = name;
        _listener = listener;
    }

    public Tool tool() {
        return _tool;
    }

    public void setTool(Tool tool) {
        _tool = tool;
    }

    public String name() {
        return _name;
    }

    public String getHotkey() {
        if (Character.isLetterOrDigit(_keyChar) || _keyChar == '.') {
            return " (" + _keyChar + ")";
        }
        return "";
    }

    /**
     * Adds an additional component to be presented in an alternative view mode.
     *
     * @param component
     */
    public void addAdditionalComponent(Component component) {
        _additionalComponents.add(component);
    }

    /**
     * Remove an additional component if it exists.
     *
     * @param component
     */
    public void removeAdditionalComponent(Component component) {
        _additionalComponents.remove(component);
    }

    /**
     * Set the style in which additional components are presented.
     * Currently supported modes:
     * POPUP_MENU: Only considers components from type {@link JMenuItem}.
     * DIALOG: Standard. Puts each component in a {@link JTabbedPane} and presents them in a dialog.
     *
     * @param mode
     */
    public void setAlternativeMode(int mode) {
        _alternativeMode = mode;
    }

    public Object attributeValue() {
        return tool();
    }

    public AbstractButton button() {
        return _button;
    }

    public void mouseClicked(MouseEvent e) {

        if (SwingUtilities.isRightMouseButton(e) || e.isControlDown()) {
            if (!_additionalComponents.isEmpty()) {
                switch (_alternativeMode) {
                    case POPUP_MENU:
                        showAdditionalComponentsMenu(e);
                        break;
                    case DIALOG:
                        showAdditionalComponentsDialog();
                        break;
                }
            }
        } else {
            boolean doubleClick = (e.getClickCount() > 1);
            _listener.paletteUserSelected(this, doubleClick);
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {
        _listener.paletteUserOver(this, true);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        _listener.paletteUserOver(this, false);
    }

    public final void actionPerformed(ActionEvent e) {
        _listener.paletteUserSelected(this, true);
    }

    private void showAdditionalComponentsMenu(MouseEvent e) {
        JPopupMenu menu = new JPopupMenu();
        boolean isEmpty = true;
        for (Component c : _additionalComponents) {
            if (c instanceof JMenuItem) {
                menu.add(c);
                isEmpty = false;
            }
        }

        if (!isEmpty) {
            Point p = e.getLocationOnScreen();
            Point p2 = button().getLocationOnScreen();

            menu.show(button(), p.x - p2.x, p.y - p2.y);
        }
    }

    private void showAdditionalComponentsDialog() {

        JTabbedPane tabbedPane = new JTabbedPane();
        for (Component c : _additionalComponents) {
            tabbedPane.add(c);
        }

        final JDialog dialog = new JDialog(SwingUtilities.getWindowAncestor(button()));
        dialog.setLocationRelativeTo(button());
        dialog.add(tabbedPane);
        //dialog.setUndecorated(true);

        dialog.addWindowFocusListener(new WindowAdapter() {
            @Override
            public void windowLostFocus(WindowEvent e) {
                dialog.setVisible(false);
                dialog.dispose();
            }
        });

        dialog.pack();
        dialog.validate();
        dialog.setVisible(true);
    }

    private static ImageIcon[] createIconsByGuess(final String name) {
        ImageIcon[] result = new ImageIcon[2];
        ImageIcon firstGuess;
        try {
            firstGuess = createIcon(name + ".gif");
        } catch (Exception e) {
            LOGGER.error("Guess for " + name + ".gif failed with " + e);
            firstGuess = null;
        }
        if (firstGuess != null) {
            result[0] = firstGuess;
            result[1] = null;
        } else {
            result[0] = createIcon(name + "1.gif");
            result[1] = createIcon(name + "2.gif");
        }
        return result;
    }

    private static ImageIcon createIcon(String name) {
        URL iconURL = PluginManager.getInstance().getBottomClassLoader().getResource(name);

        if (iconURL == null) {
            File icon = new File(name);
            if (!icon.isFile()) {
                return null;
            }
            try {
                iconURL = icon.toURI().toURL();
            } catch (MalformedURLException e) {
                return null;
            }
        }
        return new ImageIcon(iconURL);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
     */
    public void keyPressed(KeyEvent e) {
        if (!e.isControlDown() && !e.isAltDown() && !e.isMetaDown()) {
            if (e.getKeyChar() == _keyChar) {
                _listener.paletteUserSelected(this, false);
            } else if (e.getKeyChar() == Character.toUpperCase(_keyChar)) {
                _listener.paletteUserSelected(this, true);
            }
        }
    }

    public void keyReleased(KeyEvent e) {}

    public void keyTyped(KeyEvent e) {}

    public static class ToolButtonModel extends JToggleButton.ToggleButtonModel {

        @Override
        public void setSelected(boolean b) {
            // We want to control when the button is actually selected (only when clicked, not dragged)
        }

        public void setTrulySelected(boolean b) {
            super.setSelected(b);
        }
    }
}