package org.jcon.ui.menu;

import org.jcon.df.util.security.SecurityServices;
import org.jcon.ui.CommandSourceEvent;
import org.jcon.ui.CommandSourceListener;
import org.jcon.util.GenLib;
import org.jcon.util.service.ContainerServices;
import org.jcon.param.Param;
import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * A Param driven menubar with many helpful methods.
 * This class also provides a "logical" menubar, separating
 * the business logic from the awt implementation.
 *
 * @author Jack Harich
 */
public class MenuBarParam implements
    CommandSourceListener {

//---------- Private Fields ------------------------------
private MenuBar           menuBar = new MenuBar();
private ActionListener    actionListener;
private ItemListener      itemListener;
private ContainerServices containerServices;
private SecurityServices  securityServices;

// Key = Text, Object = Menu
// Assumes all Menu labels are unique
private Hashtable menus = new Hashtable();

// Key = Command, Object = MenuItem
// Assumes all commands are unique
// For menuItem groups with same command only includes one
private Hashtable menuItems = new Hashtable();

// Key = MenuName, Object = MenuItemListEvent
private Hashtable menuItemLists = new Hashtable();

//---------- Initialization ------------------------------
public MenuBarParam(ActionListener actionListener) {
    this.actionListener = actionListener;
}
//---------- CommandSourceListener Implementation --------
public void commandSourceChanged(CommandSourceEvent evt) {
    String command = evt.getCommand();
    MenuItem menuItem = (MenuItem)menuItems.get(command);
    if (menuItem == null) return;

    if (evt.isEnabledChanged()) {
        menuItem.setEnabled(evt.isEnabled());
    }
    // Not supported - menuItem.setVisible(evt.isVisible());

    if (evt.isToggleOnChanged()) {
        if (menuItem instanceof CheckboxMenuItem) {
            ((CheckboxMenuItem)menuItem).setState(evt.isToggleOn());
        }
    }
}
//---------- Properties ----------------------------------
public void setContainerServices(ContainerServices containerServices) {
    this.containerServices = containerServices;
} 
private void checkServices() {
    if (securityServices == null) {
            securityServices = (SecurityServices)
        containerServices.getServiceDefaultNull("SecurityServices");
    }
}
//---------- Public Methods ------------------------------
public void setItemListener(ItemListener itemListener) {
    this.itemListener = itemListener;
}
/**
 * Passing a Vector containing the parameters for a MenuBar
 * causes the MenuBar to be built. It can later be changed
 * dynamically.
 */
public void setParamVector(Vector menus) {
    checkServices(); // To support isAllowed()
    
    // Empty menuBar
    while (menuBar.getMenuCount() > 0) {
        menuBar.remove(0);
    }
    // Build initial menuBar. All top elements must be a menu.
    for (int i = 0; i < menus.size(); i++) {
        Param menuParam = (Param)menus.elementAt(i);
        if (! isAllowed(menuParam)) continue;
        
        menuBar.add(buildMenu(menuParam));
    }
}
public MenuBar getMenuBar() {
    return menuBar;
}
public Enumeration getActionCommands() {
    return menuItems.keys();
}
/**
 * The menubar will be refreshed by removing old items and
 * adding the new items in the @param list. Numbers and
 * accelerators will be automatically assigned. This allows
 * a reusable approach to maintaining a list of file,
 * windows, etc.
 */
public void menuItemListChanged(MenuItemListEvent newList) {
    MenuItemListEvent oldList = (MenuItemListEvent)menuItemLists
        .get(newList.getMenuName());
    if (oldList != null) removeOldItemList(oldList);
    addNewItemList(newList);
    menuItemLists.put(newList.getMenuName(), newList);
}
//---------- Private Methods -----------------------------
// RECURSIVE
private Menu buildMenu(Param param) {
    
    String menuText = param.getString("Text");
    Menu menu = new Menu(menuText);
    menus.put(menuText, menu);

    if (param.hasProperty("Enabled")) {
        menu.setEnabled(param.isTrue("Enabled"));
    }
    Vector items = param.getVector("Menu");
    for (int i = 0; i < items.size(); i++) {
        Param itemParam = (Param)items.elementAt(i);
        if (! isAllowed(itemParam)) continue;
        
        String type = itemParam.getString("Type");

        // Enabled is true by default
        boolean enabled = itemParam.isTrueDefaultTrue("Enabled");

        // Process different types
        if (type.equals("MenuItem")) {
            String label = itemParam.getString("Text");
            String command = itemParam.getString("Command");
            MenuItem item = createNewMenuItem(label, command, menu, null);
            item.setEnabled(enabled);

        } else if (type.equals("Checkbox")) {
            String label = itemParam.getString("Text");
            String command = itemParam.getString("Command");
            MenuItem item = createCheckBox(label, command, menu, null);
            item.setEnabled(enabled);

        } else if(type.equals("Separator")) {
            menu.addSeparator();

        } else if(type.equals("Menu")) {
            String text = itemParam.getString("Text");
            menu.add(buildMenu(itemParam)); // RECURSE

        } else {
            GenLib.error("MenuBarWrapper.buildMenu()",
                "Unknown Menu Element Type '" + type + "'.");
        }
    }
    return menu;
}
private MenuItem createNewMenuItem(String text,
        String command, Menu menu, String accelerator) {
    MenuItem menuItem = new MenuItem(text);
    menuItem.setActionCommand(command);
    menuItem.addActionListener(actionListener);
    if (accelerator != null) {
        // *** disabled to use old awt, not Swing
        //menuItem.setKeyAccelerator(accelerator.charAt(0));
    }
    menu.add(menuItem);
    menuItems.put(command, menuItem);
    return menuItem;
}
private MenuItem createCheckBox(String text,
        String command, Menu menu, String accelerator) {
    CheckboxMenuItem menuItem = new CheckboxMenuItem(text);
    menuItem.setActionCommand(command);
    menuItem.addItemListener(itemListener);
    if (accelerator != null) {
        // *** disabled to use old awt, not Swing
        //menuItem.setKeyAccelerator(accelerator.charAt(0));
    }
    menu.add(menuItem);
    menuItems.put(command, menuItem);
    return menuItem;
}
private void removeOldItemList(MenuItemListEvent list) {
    Menu menu = (Menu)menus.get(list.getMenuName());
    if (menu == null) {
        GenLib.error("MenuBarWrapper.removeOldItemList()",
            "Cannot find MenuName '" + list.getMenuName() + "'.");
        return;
    }
    // Determine items to remove by assuming unique commands
    Vector itemsToRemove = new Vector();
//print("Helper menu.getItemCount() " + menu.getItemCount());
    // ****** Swing bug, "menu.getItemCount() not implemented"
    // Substitute 100 for menu.getItemCount()
    for (int i = 0; i < 100; i++) {
        MenuItem item;
        try {
            item = menu.getItem(i);
        } catch(Exception ex) {
            break; // Workaround ***
        }
        if (list.hasItemCommand(item.getActionCommand())) {
            itemsToRemove.addElement(item);
        }
    }
    // Remove'em
    for (int i = 0; i < itemsToRemove.size(); i++) {
        MenuItem item = (MenuItem)itemsToRemove.elementAt(i);
        menu.remove(item);
        menuItems.remove(item.getActionCommand());
    }
}
private void addNewItemList(MenuItemListEvent list) {
    Menu menu = (Menu)menus.get(list.getMenuName());
    if (menu == null) {
        GenLib.error("MenuBarWrapper.removeOldItemList()",
            "Cannot find MenuName '" + list.getMenuName() + "'.");
        return;
    }
    for (int i = 0; i < list.getItemCount(); i++) {
        String text = list.getItemText(i);
        String command = list.getItemCommand(i);
        String accelerator = String.valueOf(i);
        createNewMenuItem(i + "  " + text, command,
            menu, accelerator);
    }
}
private boolean isAllowed(Param param) {
    String secureItem = param.getString("SecureItem");
    
    if (secureItem == null) {
        return true;
        
    } else if (securityServices == null) {
        return true;
        
    } else {
        boolean allowed = securityServices.isAllowed(secureItem);    
        //print(".isAllowed() - " + secureItem + " - " + allowed);
        return allowed;
    }
}
//--- Std
private static void print(String text) {
    System.out.println("MenuBarParam" + text);
}

} // End class
