// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.ui.customization;

import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.Separator;
import com.intellij.openapi.keymap.impl.ui.ActionsTreeUtil;
import com.intellij.openapi.keymap.impl.ui.Group;
import com.intellij.openapi.util.*;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.util.ArrayList;

public class ActionUrl implements JDOMExternalizable {
  public static final int ADDED = 1;
  public static final int DELETED = -1;

  //temp action only
  public static final int MOVE = 2;

  private ArrayList<String> myGroupPath;
  private Object myComponent;
  private int myActionType;
  private int myAbsolutePosition;


  public int myInitialPosition = -1;

  @NonNls private static final String IS_GROUP = "is_group";
  @NonNls private static final String SEPARATOR = "seperator";
  @NonNls private static final String IS_ACTION = "is_action";
  @NonNls private static final String VALUE = "value";
  @NonNls private static final String PATH = "path";
  @NonNls private static final String ACTION_TYPE = "action_type";
  @NonNls private static final String POSITION = "position";


  public ActionUrl() {
    myGroupPath = new ArrayList<>();
  }

  public ActionUrl(final ArrayList<String> groupPath,
                   final Object component,
                   final int actionType,
                   final int position) {
    myGroupPath = groupPath;
    myComponent = component;
    myActionType = actionType;
    myAbsolutePosition = position;
  }

  public ArrayList<String> getGroupPath() {
    return myGroupPath;
  }

  public String getParentGroup(){
    return myGroupPath.get(myGroupPath.size() - 1);
  }

  public String getRootGroup() {
    return myGroupPath.size() >= 1 ? myGroupPath.get(1) : "";
  }

  public Object getComponent() {
    return myComponent;
  }

  @Nullable
  public AnAction getComponentAction(){
    if (myComponent instanceof Separator){
      return Separator.getInstance();
    }
    if (myComponent instanceof String){
      return ActionManager.getInstance().getAction((String)myComponent);
    }
    if (myComponent instanceof Group){
      final String id = ((Group)myComponent).getId();
      if (id == null || id.length() == 0){
        return ((Group)myComponent).constructActionGroup(true);
      }
      return ActionManager.getInstance().getAction(id);
    }
    return null;
  }

  public int getActionType() {
    return myActionType;
  }

  public void setActionType(final int actionType) {
    myActionType = actionType;
  }

  public int getAbsolutePosition() {
    return myAbsolutePosition;
  }

  public void setAbsolutePosition(final int absolutePosition) {
    myAbsolutePosition = absolutePosition;
  }

  public int getInitialPosition() {
    return myInitialPosition;
  }

  public void setInitialPosition(final int initialPosition) {
    myInitialPosition = initialPosition;
  }

  @Override
  public void readExternal(Element element) throws InvalidDataException {
    myGroupPath = new ArrayList<>();
    for (Element o : element.getChildren(PATH)) {
      myGroupPath.add(o.getAttributeValue(VALUE));
    }
    @NlsSafe final String attributeValue = element.getAttributeValue(VALUE);
    if (element.getAttributeValue(IS_ACTION) != null) {
      myComponent = attributeValue;
    }
    else if (element.getAttributeValue(SEPARATOR) != null) {
      myComponent = Separator.getInstance();
    }
    else if (element.getAttributeValue(IS_GROUP) != null) {
      final AnAction action = ActionManager.getInstance().getAction(attributeValue);
      myComponent = action instanceof ActionGroup
                    ? ActionsTreeUtil.createGroup((ActionGroup)action, true, null)
                    : new Group(attributeValue, attributeValue, null);
    }
    myActionType = Integer.parseInt(element.getAttributeValue(ACTION_TYPE));
    myAbsolutePosition = Integer.parseInt(element.getAttributeValue(POSITION));
    DefaultJDOMExternalizer.readExternal(this, element);
  }

  @Override
  public void writeExternal(Element element) throws WriteExternalException {
    for (String s : myGroupPath) {
      Element path = new Element(PATH);
      path.setAttribute(VALUE, s);
      element.addContent(path);
    }
    if (myComponent instanceof String) {
      element.setAttribute(VALUE, (String)myComponent);
      element.setAttribute(IS_ACTION, Boolean.TRUE.toString());
    }
    else if (myComponent instanceof Separator) {
      element.setAttribute(SEPARATOR, Boolean.TRUE.toString());
    }
    else if (myComponent instanceof Group) {
      final String groupId = ((Group)myComponent).getId() != null && ((Group)myComponent).getId().length() != 0
                             ? ((Group)myComponent).getId()
                             : ((Group)myComponent).getName();
      element.setAttribute(VALUE, groupId != null ? groupId : "");
      element.setAttribute(IS_GROUP, Boolean.TRUE.toString());
    }
    element.setAttribute(ACTION_TYPE, Integer.toString(myActionType));
    element.setAttribute(POSITION, Integer.toString(myAbsolutePosition));
    DefaultJDOMExternalizer.writeExternal(this, element);
  }

  public boolean isGroupContainsInPath(ActionGroup group){
    for (String s : myGroupPath) {
      if (s.equals(group.getTemplatePresentation().getText())) {
        return true;
      }
    }
    return false;
  }

  public static void changePathInActionsTree(JTree tree, ActionUrl url){
     if (url.myActionType == ADDED){
      addPathToActionsTree(tree, url);
    } else if (url.myActionType == DELETED) {
      removePathFromActionsTree(tree, url);
    } else if (url.myActionType == MOVE){
      movePathInActionsTree(tree, url);
    }
  }

  private static void addPathToActionsTree(JTree tree, ActionUrl url) {
    final TreePath treePath = CustomizationUtil.getTreePath(tree, url);
    if (treePath == null) return;
    DefaultMutableTreeNode node = (DefaultMutableTreeNode)treePath.getLastPathComponent();
    final int absolutePosition = url.getAbsolutePosition();
    if (node.getChildCount() >= absolutePosition && absolutePosition >= 0) {
      if (url.getComponent() instanceof Group){
        node.insert(ActionsTreeUtil.createNode((Group)url.getComponent()), absolutePosition);
      } else {
        node.insert(new DefaultMutableTreeNode(url.getComponent()), absolutePosition);
      }
    }
  }

  private static void removePathFromActionsTree(JTree tree, ActionUrl url) {
    if (url.myComponent == null) return;
    final TreePath treePath = CustomizationUtil.getTreePath(tree, url);
    if (treePath == null) return;
    DefaultMutableTreeNode node = (DefaultMutableTreeNode)treePath.getLastPathComponent();
    final int absolutePosition = url.getAbsolutePosition();
    if (node.getChildCount() > absolutePosition && absolutePosition >= 0) {
      DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getChildAt(absolutePosition);
      if (child.getUserObject().equals(url.getComponent())) {
        node.remove(child);
      }
    }
  }

  private static void movePathInActionsTree(JTree tree, ActionUrl url){
    final TreePath treePath = CustomizationUtil.getTreePath(tree, url);
    if (treePath != null){
      if (treePath.getLastPathComponent() != null){
        final DefaultMutableTreeNode parent = ((DefaultMutableTreeNode)treePath.getLastPathComponent());
        final int absolutePosition = url.getAbsolutePosition();
        final int initialPosition = url.getInitialPosition();
        if (parent.getChildCount() > absolutePosition && absolutePosition >= 0) {
          if (parent.getChildCount() > initialPosition && initialPosition >= 0) {
            final DefaultMutableTreeNode child = (DefaultMutableTreeNode)parent.getChildAt(initialPosition);
            if (child.getUserObject().equals(url.getComponent())){
              parent.remove(child);
              parent.insert(child, absolutePosition);
            }
          }
        }
      }
    }
  }

  public static ArrayList<String> getGroupPath(final TreePath treePath){
    final ArrayList<String> result = new ArrayList<>();
    for (int i = 0; i < treePath.getPath().length - 1; i++) {
      Object o = ((DefaultMutableTreeNode)treePath.getPath()[i]).getUserObject();
      if (o instanceof Group){
        result.add(((Group)o).getName());
      }
    }
    return result;
  }

  @Override
  public boolean equals(Object object){
    if (!(object instanceof ActionUrl)){
      return false;
    }
    ActionUrl url = (ActionUrl)object;
    Object comp = myComponent instanceof Pair ? ((Pair<?, ?>)myComponent).first : myComponent;
    Object thatComp = url.myComponent instanceof Pair ? ((Pair<?, ?>)url.myComponent).first : url.myComponent;
    return Comparing.equal(comp, thatComp) && myGroupPath.equals(url.myGroupPath) && myAbsolutePosition == url.getAbsolutePosition();
  }

  @Override
  public int hashCode() {
    int result = myComponent != null ? myComponent.hashCode() : 0;
    result += 29 * myGroupPath.hashCode();
    return result;
  }

  public void setComponent(final Object object) {
    myComponent = object;
  }

  public void setGroupPath(final ArrayList<String> groupPath) {
    myGroupPath = groupPath;
  }

  @Override
  @NonNls
  public String toString() {
    return "ActionUrl{" +
           "myGroupPath=" + myGroupPath +
           ", myComponent=" + myComponent +
           ", myActionType=" + myActionType +
           ", myAbsolutePosition=" + myAbsolutePosition +
           ", myInitialPosition=" + myInitialPosition +
           '}';
  }
}
