/*
 * Copyright 2003-2011 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.nodeEditor.cellActions;

import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples._4;
import jetbrains.mps.editor.runtime.cells.AbstractCellAction;
import jetbrains.mps.ide.datatransfer.CopyPasteUtil;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations;
import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.nodeEditor.text.TextRenderUtil;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.openapi.editor.TextBuilder;
import jetbrains.mps.openapi.editor.cells.CellTraversalUtil;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.openapi.editor.cells.EditorCell_Collection;
import jetbrains.mps.openapi.editor.selection.SelectionManager;
import jetbrains.mps.util.SNodeOperations;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.util.Condition;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * Author: Sergey Dmitriev.
 * Time: Nov 26, 2003 2:06:41 PM
 */
public class CellAction_CopyNode extends AbstractCellAction {
  private static final Logger LOG = LogManager.getLogger(CellAction_CopyNode.class);

  @Override
  public boolean canExecute(EditorContext context) {
    return context.getEditorComponent().getSelectionManager().getSelection() != null;
  }

  @Override
  public void execute(EditorContext context) {
    _4<List<SNode>, List<SNode>, Map<SNode, Set<SNode>>, String> tuple = extractSelection(context);
    if (tuple == null) return;
    CopyPasteUtil.copyNodesAndTextToClipboard(tuple._0(), tuple._2(), tuple._3());
  }

  /**
   * Returns two collection of nodes (among other things) - nodes to copy and nodes to remove.
   * These collections may differ slightly - when a Copy/Cut action is invoked on a Attribute node, its parent node (the one annotated with the attribute) must be copied,
   * but only the attribute should be deleted from the source (in case of Cut).
   *
   * @param context The EditorContext
   * @return A four-element tuple - nodes to pass through clipboard, nodes to remove from the source, map of attributes per copied node, text representation of the copied nodes
   */
  protected _4<List<SNode>, List<SNode>, Map<SNode, Set<SNode>>, String> extractSelection (EditorContext context) {
    EditorComponent editorComponent = (EditorComponent) context.getEditorComponent();
    TextBuilder textBuilder = TextRenderUtil.getTextBuilderForSelectedCellsOfEditor(editorComponent);

    SelectionManager selectionManager = editorComponent.getSelectionManager();
    List<EditorCell> selectedCells = selectionManager.getSelection().getSelectedCells();
    if (selectedCells.size() == 0) {
      return null;
    }
    if (selectedCells.size() > 1) {
      LOG.debug("Copy " + selectedCells.size() + " nodes : ");
      for (EditorCell aCell : selectedCells) {
        LOG.debug("    " + SNodeOperations.getDebugText(aCell.getSNode()));
      }
    } else {
      LOG.debug("Copy node : " + SNodeOperations.getDebugText(selectedCells.get(0).getSNode()));
    }

    List<SNode> copyNodeList = new ArrayList<SNode>();
    List<SNode> deleteNodeList = new ArrayList<SNode>();
    Map<SNode, Set<SNode>> nodesAndAttributes = new HashMap<SNode, Set<SNode>>();
    for (EditorCell selectedCell : selectedCells) {
      SNode node = selectedCell.getSNode();
      final SNode parent = node.getParent();
      if (parent != null && AttributeOperations.isAttribute(node)) {
        Condition<EditorCell> condition = new Condition<EditorCell>() {
          @Override
          public boolean met(EditorCell object) {
            SNode selectedNode = object.getSNode();
            return selectedNode != null &&
                    selectedNode.getParent() == parent && AttributeOperations.isAttribute(selectedNode);
          }
        };

        //Store the attribute by default. Store the parent only of it is also part of the selection.
        SNode nodeToDelete = node;
        Set<SNode> selectedAttributes = new HashSet<SNode>();
        selectedAttributes.add(node);
        if (selectedCell instanceof EditorCell_Collection) {
          for (EditorCell cell : CellTraversalUtil.iterateTree(selectedCell, selectedCell, true)) {
            if (condition.met(cell)) {
              selectedAttributes.add(cell.getSNode());
            }
            if (cell.getSNode() == parent) {  //Is the parent part of the selection
              nodeToDelete = parent;
            }
          }
        }

        copyNodeList.add(parent);
        deleteNodeList.add(nodeToDelete);
        nodesAndAttributes.put(parent, selectedAttributes);
      } else {
        copyNodeList.add(node);
        deleteNodeList.add(node);
      }
    }
    return new MultiTuple._4<List<SNode>, List<SNode>, Map<SNode, Set<SNode>>, String> (copyNodeList, deleteNodeList, nodesAndAttributes, textBuilder.getText());
  }
}
