/*
 * $Id: ToolbarMenu.java 4561 2008-11-28 15:57:51Z uckelman $
 *
 * Copyright (c) 2006 Rodney Kinney
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available 
 * at http://www.opensource.org.
 */
package VASSAL.build.module;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import VASSAL.build.AbstractConfigurable;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.command.Command;
import VASSAL.configure.StringArrayConfigurer;
import VASSAL.i18n.Resources;
import VASSAL.tools.LaunchButton;
import VASSAL.tools.ToolBarComponent;

/**
 * Takes buttons from the toolbar of a Map or the main module and places
 * them into a popup menu
 * 
 * @author rkinney
 * 
 */
public class ToolbarMenu extends AbstractConfigurable
                         implements ContainerListener,
                                    PropertyChangeListener,
                                    GameComponent {
  public static final String BUTTON_TEXT = "text"; //$NON-NLS-1$
  public static final String BUTTON_ICON = "icon"; //$NON-NLS-1$
  public static final String BUTTON_HOTKEY = "hotkey"; //$NON-NLS-1$
  public static final String TOOLTIP = "tooltip"; //$NON-NLS-1$
  public static final String MENU_ITEMS = "menuItems"; //$NON-NLS-1$
  /** Buttons where this property contains a JPopupMenu will turn into sub-menus */
  public static final String MENU_PROPERTY = "ToolbarMenu.popup"; //$NON-NLS-1$
  protected List<String> menuItems = new ArrayList<String>();
  protected Map<AbstractButton,JMenuItem> buttonsToMenuMap =
    new HashMap<AbstractButton,JMenuItem>();
  protected LaunchButton launch;
  protected JToolBar toolbar;
  protected JPopupMenu menu;
  protected Runnable menuBuilder;

  public ToolbarMenu() {
    launch = new LaunchButton(Resources.getString(Resources.MENU), TOOLTIP, BUTTON_TEXT, BUTTON_HOTKEY, BUTTON_ICON, new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        launch();
      }
    });
    menu = new JPopupMenu();
    launch.putClientProperty(MENU_PROPERTY, menu);
    launch.setToolTipText("Display Menu Options");
    GameModule.getGameModule().getGameState().addGameComponent(this);
  }

  public void launch() {
    menu.show(launch, 0, 0);
  }

  public String[] getAttributeDescriptions() {
    return new String[] {Resources.getString(Resources.BUTTON_TEXT), Resources.getString(Resources.TOOLTIP_TEXT), Resources.getString(Resources.BUTTON_ICON), 
                         Resources.getString(Resources.HOTKEY_LABEL),Resources.getString("Editor.ToolbarMenu.menu_entries")}; //$NON-NLS-1$
  }

  public Class<?>[] getAttributeTypes() {
    return new Class<?>[] {
      String.class,
      String.class,
      Icon.class,
      KeyStroke.class,
      String[].class
    };
  }

  public String[] getAttributeNames() {
    return new String[] {
      BUTTON_TEXT,
      TOOLTIP,
      BUTTON_ICON,
      BUTTON_HOTKEY,
      MENU_ITEMS
    };
  }

  public String getAttributeValueString(String key) {
    if (MENU_ITEMS.equals(key)) {
      return StringArrayConfigurer.arrayToString(
        menuItems.toArray(new String[menuItems.size()]));
    }
    else {
      return launch.getAttributeValueString(key);
    }
  }

  public void setAttribute(String key, Object value) {
    if (MENU_ITEMS.equals(key)) {
      if (value instanceof String) {
        value = StringArrayConfigurer.stringToArray((String) value);
      }
      menuItems = new ArrayList<String>(Arrays.asList((String[]) value));
      if (toolbar != null) {
        scheduleBuildMenu();
      }
    }
    else if (BUTTON_TEXT.equals(key)) {
      setConfigureName((String) value);
      launch.setAttribute(key, value);
    }
    else {
      launch.setAttribute(key, value);
    }
  }

  public void addTo(Buildable parent) {
    if (parent instanceof ToolBarComponent) {
      toolbar = ((ToolBarComponent) parent).getToolBar();
    }
    toolbar.add(launch);
    toolbar.addContainerListener(this);
    scheduleBuildMenu();
  }

  public Class<?>[] getAllowableConfigureComponents() {
    return new Class<?>[0];
  }

  public HelpFile getHelpFile() {
    return HelpFile.getReferenceManualPage("ToolbarMenu.htm"); //$NON-NLS-1$
  }

  public static String getConfigureTypeName() {
    return Resources.getString("Editor.ToolbarMenu.component_type"); //$NON-NLS-1$
  }

  public void removeFrom(Buildable parent) {
    toolbar.remove(launch);
    toolbar.removeContainerListener(this);
  }

  protected void buildMenu() {
    for (AbstractButton b : buttonsToMenuMap.keySet()) {
      b.setVisible(true);
      b.removePropertyChangeListener(this);
    }
    buttonsToMenuMap.clear();
    menu.removeAll();
    HashMap<String,JButton> nameToButton = new HashMap<String,JButton>();
    if (toolbar != null) {
      for (int i = 0, n = toolbar.getComponentCount(); i < n; ++i) {
        if (toolbar.getComponentAtIndex(i) instanceof JButton) {
          JButton b = ((JButton) toolbar.getComponentAtIndex(i));
          String text =
            (String) b.getClientProperty(LaunchButton.UNTRANSLATED_TEXT);
          if (text == null) {
            text = b.getText();
          }
          nameToButton.put(text, b);
        }
      }
    }
    
    for (String item : menuItems) {
      final JButton b = nameToButton.get(item);
      if (b != null) {
        Object property = b.getClientProperty(MENU_PROPERTY);
        b.addPropertyChangeListener(this);
        b.setVisible(false);
        if (property instanceof JPopupMenu) {
          // This button corresponds to another ToolbarMenu button.
          // Turn it into a submenu.
          JPopupMenu toolbarMenu = (JPopupMenu) property;
          toolbarMenu.addContainerListener(this);
          JMenu subMenu = new JMenu(b.getText());
          Component[] items = toolbarMenu.getComponents();
          for (int i = 0; i < items.length; i++) {
            final JMenuItem otherItem = (JMenuItem) items[i];
            JMenuItem myItem =
              new JMenuItem(otherItem.getText(), otherItem.getIcon());
            myItem.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                otherItem.doClick();
              }
            });
            subMenu.add(myItem);
            buttonsToMenuMap.put(otherItem, myItem);
          }
          buttonsToMenuMap.put(b, subMenu);
          menu.add(subMenu);
        }
        else {
          JMenuItem mi = new JMenuItem(b.getText(), b.getIcon());
          mi.setEnabled(b.isEnabled());
          mi.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              b.doClick();
            }
          });
          buttonsToMenuMap.put(b, mi);
          menu.add(mi);
        }
      }
    }
  }

  protected void scheduleBuildMenu() {
    if (menuBuilder == null) {
      menuBuilder = new Runnable() {
        public void run() {
          buildMenu();
          menuBuilder = null;
        }
      };
      SwingUtilities.invokeLater(menuBuilder);
    }
  }

  public void componentAdded(ContainerEvent e) {
    scheduleBuildMenu();
  }

  public void componentRemoved(ContainerEvent e) {
    scheduleBuildMenu();
  }

  public void propertyChange(PropertyChangeEvent evt) {
    JButton b = (JButton) evt.getSource();
    JMenuItem mi = buttonsToMenuMap.get(b);
    if (mi != null) {
      if (AbstractButton.TEXT_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
        scheduleBuildMenu();
      }
      else if ("enabled".equals(evt.getPropertyName())) { //$NON-NLS-1$
        mi.setEnabled(b.isEnabled());
      }
      else if (AbstractButton.ICON_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
        mi.setIcon(b.getIcon());
      }
    }
  }

  public void setup(boolean gameStarting) {
    // Prevent our Toolbar buttons from becoming visible on Game close/reopen
    scheduleBuildMenu();
  }

  public Command getRestoreCommand() {
    return null;
  }
}
