/*
 * BaseMenuBar.java
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
package org.rstudio.core.client.command;

import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.MenuItemSeparator;
import com.google.gwt.user.client.ui.UIObject;
import org.rstudio.core.client.SeparatorManager;
import org.rstudio.core.client.widget.events.GlassVisibilityEvent;
import org.rstudio.studio.client.RStudioGinjector;
import org.rstudio.studio.client.application.events.EventBus;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class BaseMenuBar extends MenuBar
{
   private class PositionComparator implements Comparator<UIObject>
   {
      public int compare(UIObject a, UIObject b)
      {
         return getPosition(a) - getPosition(b);
      }

      private int getPosition(UIObject item)
      {
         if (item instanceof MenuItem)
            return getItemIndex((MenuItem) item);
         else if (item instanceof MenuItemSeparator)
            return getSeparatorIndex((MenuItemSeparator) item);

         assert false;
         return -1;
      }
   }

   public BaseMenuBar(boolean vertical)
   {
      super(vertical);
      vertical_ = vertical;

      // Would prefer to inject this from the constructor but some
      // subclasses are instantiated using generated code--don't feel
      // like messing with all that now
      eventBus_ = RStudioGinjector.INSTANCE.getEventBus();
   }

   @Override
   protected void onLoad()
   {
      if (vertical_ && glass++ == 0)
         eventBus_.fireEvent(new GlassVisibilityEvent(true));
      super.onLoad();
      for (MenuItem child : getItems())
      {
         if (child instanceof AppMenuItem)
            ((AppMenuItem)child).onShow();
         else
         {
            // if this is a submenu that consists entirely of hidden commands, 
            // hide the submenu and its flyout icon 
            MenuBar submenu = child.getSubMenu(); 
            if (submenu != null &&
                submenu instanceof AppMenuBar)
            {
               boolean visible = child.isVisible();
               boolean newVisible = !((AppMenuBar)submenu).allInvisibleCmds();
               if (visible != newVisible)
               {
                  child.setVisible(newVisible);
                  updateSubmenuIcon(child);
               }
            }
         }
      }
      if (autoHideRedundantSeparators_)
         manageSeparators();
   }

   @Override
   protected void onUnload()
   {
      super.onUnload();
      if (vertical_ && --glass == 0)
         eventBus_.fireEvent(new GlassVisibilityEvent(false));
   }

   public void setAutoHideRedundantSeparators(boolean value)
   {
      autoHideRedundantSeparators_ = value;
   }

   @Override
   public MenuItemSeparator insertSeparator(MenuItemSeparator separator,
                                            int beforeIndex) throws IndexOutOfBoundsException
   {
      MenuItemSeparator value = super.insertSeparator(separator, beforeIndex);
      separators_.add(value);
      return value;
   }

   @Override
   public void removeSeparator(MenuItemSeparator separator)
   {
      separators_.remove(separator);
      super.removeSeparator(separator);
   }

   @Override
   public MenuItem getSelectedItem()
   {
      return super.getSelectedItem();
   }

   private static class MenuSeparatorManager extends SeparatorManager<UIObject>
   {
      @Override
      protected boolean isSeparator(UIObject item)
      {
         return item instanceof MenuItemSeparator;
      }

      @Override
      protected boolean isVisible(UIObject item)
      {
         return item.isVisible();
      }

      @Override
      protected void setVisible(UIObject item, boolean visible)
      {
         item.setVisible(visible);
      }
   }

   /**
    * Make sure the proper separators appear and disappear
    */
   protected void manageSeparators()
   {
      if (separators_.size() == 0)
         return;
      List<MenuItem> menuItems = getItems();
      ArrayList<UIObject> allItems =
            new ArrayList<UIObject>(menuItems.size() + separators_.size());
      allItems.addAll(separators_);
      allItems.addAll(menuItems);
      Collections.sort(allItems, new PositionComparator());

      new MenuSeparatorManager().manageSeparators(allItems);
   }

   public int getItemCount()
   {
      return getItems().size();
   }

   public ArrayList<MenuItem> getVisibleItems()
   {
      ArrayList<MenuItem> items = new ArrayList<MenuItem>();
      for (MenuItem item : getItems())
         if (item.isVisible())
            items.add(item);
      return items;
   }

   /**
    * Reference count for glass visibility. NOTE: Perhaps this should be
    * hoisted into a more general class so that everyone who raises
    * GlassVisibilityEvent shares the same refcount.
    */
   private static int glass = 0;

   private boolean autoHideRedundantSeparators_ = true;
   private final ArrayList<MenuItemSeparator> separators_ =
         new ArrayList<MenuItemSeparator>();
   private final EventBus eventBus_;
   private final boolean vertical_;
}
