/*
 * Copyright (c) 2012-2018 Red Hat, Inc.
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Red Hat, Inc. - initial API and implementation
 */
package org.eclipse.che.ide.menu;

import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.ActionGroup;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.ActionSelectedHandler;
import org.eclipse.che.ide.api.action.CustomComponentAction;
import org.eclipse.che.ide.api.action.IdeActions;
import org.eclipse.che.ide.api.action.Presentation;
import org.eclipse.che.ide.api.action.Separator;
import org.eclipse.che.ide.ui.toolbar.CloseMenuHandler;
import org.eclipse.che.ide.ui.toolbar.MenuLockLayer;
import org.eclipse.che.ide.ui.toolbar.PresentationFactory;

/**
 * Implements {@link StatusPanelGroupView}
 *
 * @author Oleksii Orel
 * @author Dmitry Shnurenko
 */
public class StatusPanelGroupViewImpl extends Composite
    implements StatusPanelGroupView, CloseMenuHandler, ActionSelectedHandler {
  private final MenuResources resources;

  private final PresentationFactory presentationFactory = new PresentationFactory();

  private final FlowPanel centerPanel = new FlowPanel();

  private final FlowPanel rightPanel = new FlowPanel();

  private final FlowPanel leftPanel = new FlowPanel();
  /** Panel, which contains top menu. */
  private final FlowPanel rootPanel = new FlowPanel();

  /** Lock layer for displaying popup menus. */
  private MenuLockLayer lockLayer;
  /** List Menu Bar items. */
  private Map<Action, MenuBarItem> action2barItem = new HashMap<>();

  /** Store selected Menu Bar item. */
  private MenuBarItem selectedMenuBarItem;

  private List<Action> rightVisibleActions = new ArrayList<>();
  private List<Action> centerVisibleActions = new ArrayList<>();
  private List<Action> leftVisibleActions = new ArrayList<>();
  private ActionManager actionManager;

  /** Create new {@link MainMenuViewImpl} */
  @Inject
  public StatusPanelGroupViewImpl(MenuResources resources, ActionManager actionManager) {
    this.resources = resources;
    this.actionManager = actionManager;

    initWidget(rootPanel);

    rootPanel.setStyleName(resources.menuCss().menuBar());
    rootPanel.addStyleName(resources.menuCss().statusPanel());

    leftPanel.addStyleName(resources.menuCss().leftPanel());
    leftPanel.getElement().getStyle().setPropertyPx("marginLeft", 1);
    rootPanel.add(leftPanel);

    centerPanel.setStyleName(resources.menuCss().leftPanel());
    rootPanel.add(centerPanel);

    rightPanel.addStyleName(resources.menuCss().rightPanel());
    rightPanel.getElement().getStyle().setPropertyPx("marginRight", 1);
    rootPanel.add(rightPanel);
  }

  @Override
  public void setDelegate(ActionDelegate delegate) {
    new Timer() {
      @Override
      public void run() {
        updateMenuActions();
      }
    }.scheduleRepeating(1000);
  }

  /** Handle closing of all popup windows. */
  public void onCloseMenu() {
    selectedMenuBarItem.setNormalState();
    selectedMenuBarItem = null;
    lockLayer = null;
  }

  void updateMenuActions() {
    if (selectedMenuBarItem != null) {
      return;
    }
    List<Action> newCenterVisibleActions = new ArrayList<>();
    expandActionGroup(IdeActions.GROUP_CENTER_STATUS_PANEL, newCenterVisibleActions, actionManager);
    if (!newCenterVisibleActions.equals(centerVisibleActions)) {
      centerPanel.clear();
      for (Action action : newCenterVisibleActions) {
        addToPanel(centerPanel, action, presentationFactory);
      }
      centerVisibleActions = newCenterVisibleActions;
    }
    List<Action> newRightVisibleActions = new ArrayList<>();
    expandActionGroup(IdeActions.GROUP_RIGHT_STATUS_PANEL, newRightVisibleActions, actionManager);
    if (!newRightVisibleActions.equals(rightVisibleActions)) {
      rightPanel.clear();
      for (Action action : newRightVisibleActions) {
        addToPanel(rightPanel, action, presentationFactory);
      }
      rightVisibleActions = newRightVisibleActions;
    }
    List<Action> newLeftVisibleActions = new ArrayList<>();
    expandActionGroup(IdeActions.GROUP_LEFT_STATUS_PANEL, newLeftVisibleActions, actionManager);
    if (!newLeftVisibleActions.equals(leftVisibleActions)) {
      leftPanel.clear();
      for (Action action : newLeftVisibleActions) {
        addToPanel(leftPanel, action, presentationFactory);
      }
      leftVisibleActions = newLeftVisibleActions;
    }
    centerPanel
        .getElement()
        .getStyle()
        .setPropertyPx(
            "marginLeft",
            rootPanel.getOffsetWidth() / 2
                - leftPanel.getOffsetWidth()
                - centerPanel.getOffsetWidth() / 2);
  }

  /** Create a new widget and add it to panel menu. */
  private void addToPanel(FlowPanel panel, Action action, PresentationFactory presentationFactory) {
    Presentation presentation = presentationFactory.getPresentation(action);

    if (action instanceof Separator) {
      panel.add(new SeparatorItem(resources.menuCss().panelSeparator()));

      // todo find way to render non custom actions
    } else if (action instanceof CustomComponentAction) {
      CustomComponentAction customComponentAction = (CustomComponentAction) action;
      Widget component = customComponentAction.createCustomComponent(presentation);
      component.addStyleName(resources.menuCss().customComponent());
      panel.add(component);
    }
  }

  private void expandActionGroup(
      String actionGroupId, final List<Action> newVisibleActions, ActionManager actionManager) {
    final ActionGroup mainActionGroup = (ActionGroup) actionManager.getAction(actionGroupId);
    if (mainActionGroup == null) return;
    expandActionGroup(newVisibleActions, actionManager, mainActionGroup);
  }

  private void expandActionGroup(
      List<Action> newVisibleActions, ActionManager actionManager, ActionGroup mainActionGroup) {
    final Action[] children = mainActionGroup.getChildren(null);
    for (final Action action : children) {
      final Presentation presentation = presentationFactory.getPresentation(action);
      final ActionEvent e = new ActionEvent(presentation, actionManager);
      action.update(e);
      if (presentation.isVisible()) { // add only visible items
        newVisibleActions.add(action);
      }
      if (action2barItem.containsKey(action)) {
        action2barItem.get(action).update();
      }
    }
  }

  @Override
  public void onActionSelected(Action action) {
    if (action2barItem.containsKey(action)) {
      MenuBarItem item = action2barItem.get(action);
      if (selectedMenuBarItem != null && selectedMenuBarItem != item) {
        selectedMenuBarItem.setNormalState();
        selectedMenuBarItem.closePopupMenu();
      }

      selectedMenuBarItem = item;
    } else {
      lockLayer.close();
      lockLayer = null;
    }
  }

  private static class SeparatorItem extends Composite {
    SeparatorItem(String styleName) {
      final FlowPanel widget = new FlowPanel();
      widget.addStyleName(styleName);
      Element separator = widget.getElement();
      for (int i = 0; i < 6; i++) {
        separator.appendChild(DOM.createDiv());
      }

      initWidget(widget);
    }
  }
}
