/*
 * $Id: MenuManager.java 3700 2008-05-31 18:57:42Z uckelman $
 *
 * Copyright (c) 2008 by Joel Uckelman 
 *
 * 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.tools.menu;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenuBar;

/**
 * @author Joel Uckelman
 * @since 3.1.0
 */
public abstract class MenuManager {
  protected static MenuManager instance;

  public static MenuManager getInstance() {
    if (instance == null) throw new IllegalStateException();
    return instance;
  }

  public MenuManager() {
    if (instance != null) throw new IllegalStateException();
    instance = this;
  }

  public abstract JMenuBar getMenuBarFor(JFrame fc);

  public abstract MenuBarProxy getMenuBarProxyFor(JFrame fc);

  private Map<String,List<MenuItemProxy>> actionLocations =
    new HashMap<String,List<MenuItemProxy>>();

  private Map<String,MenuMarker> markers = new HashMap<String,MenuMarker>();

  public MenuItemProxy addKey(String key) {
    List<MenuItemProxy> items = actionLocations.get(key);
    if (items == null) {
      items = new ArrayList<MenuItemProxy>();
      actionLocations.put(key, items);
    }

    final MenuItemProxy item = new MenuItemProxy();
    items.add(item);
    return item;
  }

  public List<MenuItemProxy> getItems(String key) {
    return actionLocations.get(key);
  }

  public void addAction(String key, Action a) {
    final List<MenuItemProxy> items = actionLocations.get(key);
    if (items != null) {
      for (MenuItemProxy i : items) {
        i.setAction(a);
      }
    }
  }

  public void removeAction(String key) {
    addAction(key, null);
  }

  public MenuMarker getMarker(String key) {
    return markers.get(key);
  }

  public MenuMarker addMarker(String key) {
    final MenuMarker marker = new MenuMarker();
    markers.put(key, marker);
    return marker;
  }

  public void addToSection(String key, ChildProxy<?> item) {
    final MenuMarker start = getMarker(key + ".start");
    final MenuMarker end = getMarker(key + ".end");
    final ParentProxy parent = end.getParent();

    final int startPos = parent.getIndex(start);
    final int endPos = parent.getIndex(end);

    // do separator check if this is the first item in the group
    if (parent instanceof MenuProxy && startPos + 1 == endPos) {
      // check from the end of the group to the next visible item
      // and insert a separator if that item is not one
      if (nextVisibleItemNotASeparator(end)) {
        ((MenuProxy) parent).insertSeparator(endPos+1);
      }
      
      // check from the start of the group to the previous visible
      // item and insert a separator if that item is not one
      if (prevVisibleItemNotASeparator(start)) {
        ((MenuProxy) parent).insertSeparator(startPos);
      }
    }

    // insert the item between the markers
    parent.insert(item, endPos);
  }

  public void removeFromSection(String key, ChildProxy<?> item) {
    final MenuMarker start = getMarker(key + ".start");
    final MenuMarker end = getMarker(key + ".end");
    final ParentProxy parent = end.getParent();

    // remove the item
    parent.remove(item);
  
    // do separator check if this was the last item in the group 
    if (parent instanceof MenuProxy) {
      final int startPos = parent.getIndex(start);     
      final int endPos = parent.getIndex(end);    
      if (startPos + 1 == endPos) {
        if (visibleItemBefore(start)) {
          // if we have a group on each side, or before but not after;
          // remove our top separator
          parent.remove(startPos-1);
        }
        else if (visibleItemAfter(end)) {
          // we have a group after, but none before;
          // remove our bottom separator
          parent.remove(endPos+1);
        }
        // otherwise, we were the sole group, our parent has no visible
        // items now 
      }
    }
  }

  private boolean visibleItemAfter(ChildProxy<?> child) {
    final ParentProxy parent = child.getParent();
    final int count = parent.getChildCount();
    for (int i = parent.getIndex(child) + 1; i < count; i++) {
      final ChildProxy<?> c = parent.getChild(i);
      if (!(c instanceof MenuMarker)) return true; 
    }
    return false;
  }

  private boolean visibleItemBefore(ChildProxy<?> child) {
    final ParentProxy parent = child.getParent();
    for (int i = parent.getIndex(child) - 1; i >= 0; i++) {
      final ChildProxy<?> c = parent.getChild(i);
      if (!(c instanceof MenuMarker)) return true; 
    }
    return false;
  }

  private boolean nextVisibleItemNotASeparator(ChildProxy<?> child) {
    final ParentProxy parent = child.getParent();
    final int count = parent.getChildCount();
    for (int i = parent.getIndex(child) + 1; i < count; i++) {
      final ChildProxy<?> c = parent.getChild(i);
      if (c instanceof MenuMarker) continue;
      
      return !(c instanceof SeparatorProxy);
    }
    return false;
  }

  private boolean prevVisibleItemNotASeparator(ChildProxy<?> child) {
    final ParentProxy parent = child.getParent();
    for (int i = parent.getIndex(child) - 1; i >= 0; i--) {
      final ChildProxy<?> c = parent.getChild(i);
      if (c instanceof MenuMarker) continue;
      
      return !(c instanceof SeparatorProxy);
    }
    return false;
  }

}
