/*
 * Copyright 2003-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.mps.ide.devkit.cellExplorer;

import com.intellij.icons.AllIcons.Actions;
import com.intellij.ide.actions.CloseTabToolbarAction;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.ui.ScrollPaneFactory;
import jetbrains.mps.ide.devkit.cellExplorer.cellsTree.CellsTree;
import jetbrains.mps.ide.devkit.cellExplorer.detailTree.CellDetailTree;
import jetbrains.mps.ide.icons.IconManager;
import jetbrains.mps.ide.tools.BaseTabbedProjectTool;
import jetbrains.mps.nodeEditor.EditorComponent.EditorDisposeListener;
import jetbrains.mps.openapi.editor.EditorComponent;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.plugins.tool.IComponentDisposer;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.util.Computable;
import jetbrains.mps.util.Pair;
import jetbrains.mps.workbench.action.ActionUtils;
import jetbrains.mps.workbench.action.BaseAction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SNode;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;

public class CellExplorerTab implements IComponentDisposer<JComponent> {
  private final BaseTabbedProjectTool myTool;
  private final EditorComponent myEditorComponent;
  private final Runnable myEditorActivator;
  private final EditorDisposeListener myCloseTabOnEditorDisposeListener = new CloseTabOnEditorDisposeListener();
  private final SimpleToolWindowPanel myComponent;
  private final CellsTree myCellsTree;
  private final CellDetailTree myCellDetailTree;

  public CellExplorerTab(@NotNull BaseTabbedProjectTool tool, @NotNull EditorComponent editorComponent, @NotNull Runnable editorActivator) {
    myTool = tool;
    myEditorComponent = editorComponent;
    myEditorActivator = editorActivator;

    ((jetbrains.mps.nodeEditor.EditorComponent) myEditorComponent).addDisposeListener(myCloseTabOnEditorDisposeListener);

    myCellDetailTree = new CellDetailTree();

    ActionListener selectCurrentCellInEditor = new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        selectCurrentCellInEditor();
      }
    };
    myCellDetailTree.setActionListener(selectCurrentCellInEditor);

    myCellsTree = new CellsTree();
    myCellsTree.addTreeSelectionListener(new TreeSelectionListener() {
      @Override
      public void valueChanged(TreeSelectionEvent e) {
        CellsTree source = (CellsTree) e.getSource();
        myCellDetailTree.setCell(source.getCellByPath(e.getNewLeadSelectionPath()));
      }
    });
    myCellsTree.setActionListener(selectCurrentCellInEditor);

    OnePixelSplitter splitter = new OnePixelSplitter(false, "CellExplorerTool.Splitter.Proportion", 0.5f);
    splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myCellsTree, true));
    splitter.setSecondComponent(ScrollPaneFactory.createScrollPane(myCellDetailTree, true));

    myComponent = new SimpleToolWindowPanel(false, true);
    myComponent.setToolbar(createToolbar(myCellsTree));
    myComponent.setContent(splitter);
  }

  public void showCell(@NotNull EditorCell cell) {
    myCellsTree.showCellInTree(cell);
  }

  @Override
  public void disposeComponent(JComponent component) {
    ((jetbrains.mps.nodeEditor.EditorComponent) myEditorComponent).removeDisposeListener(myCloseTabOnEditorDisposeListener);

    myCellsTree.dispose();
    myCellDetailTree.dispose();
  }

  public void openTab(boolean openTool) {
    assert !isDisposed() : "Re-adding disposed tab";

    Pair<String, Icon> nodeData = new ModelAccessHelper(myEditorComponent.getEditorContext().getRepository()).runReadAction(
        new Computable<Pair<String, Icon>>() {
          @NotNull
          @Override
          public Pair<String, Icon> compute() {
            SNode editedNode = myEditorComponent.getEditedNode();
            if (editedNode == null) {
              return new Pair<String, Icon>(null, null);
            }

            return new Pair<String, Icon>(editedNode.getPresentation(), IconManager.getIconFor(editedNode));
          }
        });

    if (nodeData.o1 == null) {
      nodeData.o1 = "(unknown node)";
    }

    myTool.addTab(myComponent, nodeData.o1, nodeData.o2, this, openTool);
  }

  private void closeTab() {
    myTool.closeTab(myComponent);
  }

  private boolean isDisposed() {
    return myEditorComponent == null;
  }

  private JComponent createToolbar(JComponent targetComponent) {
    DefaultActionGroup group = ActionUtils.groupFromActions(
        new CloseAction(),
        ActionManager.getInstance().getAction(IdeActions.ACTION_PIN_ACTIVE_TAB),
        new SelectInEditorAction());
    ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.TOOLBAR, group, false);
    actionToolbar.setTargetComponent(targetComponent);
    return actionToolbar.getComponent();
  }

  private class CloseAction extends CloseTabToolbarAction {
    @Override
    public void actionPerformed(AnActionEvent e) {
      closeTab();
    }
  }

  private class CloseTabOnEditorDisposeListener implements EditorDisposeListener {
    @Override
    public void editorWillBeDisposed(@NotNull jetbrains.mps.nodeEditor.EditorComponent component) {
      closeTab();
    }
  }

  private class SelectInEditorAction extends BaseAction implements DumbAware {
    public SelectInEditorAction() {
      super("Select in Editor", "Select current cell in its editor", Actions.EditSource);
    }

    @Override
    protected void doExecute(AnActionEvent e, Map<String, Object> params) {
      selectCurrentCellInEditor();
    }
  }

  private void selectCurrentCellInEditor() {
    if (isDisposed()) return;

    EditorCell cell = myCellDetailTree.getCell();
    EditorComponent editorComponent = cell.getEditorComponent();
    if (editorComponent == null || editorComponent.isDisposed()) return;

    editorComponent.changeSelection(cell);
    myEditorActivator.run();
  }
}
