/*
 * Copyright 2003-2014 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.sidetransform;

import com.intellij.ui.LightColors;
import jetbrains.mps.editor.runtime.SideTransformInfoUtil;
import jetbrains.mps.editor.runtime.cells.AbstractCellAction;
import jetbrains.mps.editor.runtime.cells.KeyMapActionImpl;
import jetbrains.mps.editor.runtime.cells.KeyMapImpl;
import jetbrains.mps.editor.runtime.style.StyleAttributes;
import jetbrains.mps.nodeEditor.CellSide;
import jetbrains.mps.nodeEditor.cellActions.SideTransformSubstituteInfo;
import jetbrains.mps.nodeEditor.cellActions.SideTransformSubstituteInfo.Side;
import jetbrains.mps.nodeEditor.cells.DefaultCellInfo;
import jetbrains.mps.nodeEditor.cells.EditorCell_Constant;
import jetbrains.mps.nodeEditor.cells.EditorCell_Label;
import jetbrains.mps.nodeEditor.cells.SynchronizeableEditorCell;
import jetbrains.mps.openapi.editor.EditorComponent;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.openapi.editor.cells.CellActionType;
import jetbrains.mps.openapi.editor.cells.CellInfo;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.openapi.editor.cells.EditorCellContext;
import jetbrains.mps.openapi.editor.cells.EditorCell_Collection;
import jetbrains.mps.openapi.editor.cells.KeyMap;
import jetbrains.mps.util.annotation.ToRemove;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SNode;

/**
 * User: shatalin
 * Date: 27/02/14
 */
public class EditorCell_STHint extends EditorCell_Constant {
  private static final String CELL_ID = "STHint";

  @Nullable
  private final CellInfo myRestoreSelectionCellInfo;
  @NotNull
  private final EditorCell myBigCell;
  @NotNull
  private final EditorCell myAnchorCell;
  @NotNull
  private final Side mySide;
  private boolean myInstalled;

  public static EditorCell_STHint getSTHintCell(SNode node, @NotNull EditorComponent editorComponent) {
    EditorCell stHintCell = editorComponent.findCellWithId(node, CELL_ID);
    return stHintCell instanceof EditorCell_STHint ? (EditorCell_STHint) stHintCell : null;
  }


  /**
   * @deprecated after MPS 3.4 side transform actions will be migrated from actions aspect to editor aspect
   * so the will be referenced directly from editor and  anchor tag will not be used.
   * Use {@link EditorCell_STHint#EditorCell_STHint(EditorCell, EditorCell, Side, CellInfo)}  }
   */
  @Deprecated
  @ToRemove(version = 2017.2)
  public EditorCell_STHint(@NotNull EditorCell bigCell, @NotNull EditorCell anchorCell, @NotNull CellSide oldSide, @NotNull String sideTransformTag,
                           @Nullable CellInfo restoreSelectionCellInto) {
    this(bigCell, anchorCell, oldSide == CellSide.LEFT ? Side.LEFT : Side.RIGHT, restoreSelectionCellInto);
  }

  public EditorCell_STHint(@NotNull EditorCell bigCell, @NotNull EditorCell anchorCell, @NotNull Side side, @Nullable CellInfo restoreSelectionCellInto) {
    super(anchorCell.getContext(), anchorCell.getSNode(), "");
    assert bigCell.isBig();
    myBigCell = bigCell;
    myAnchorCell = anchorCell;
    mySide = side;
    myRestoreSelectionCellInfo = restoreSelectionCellInto;
    init();
  }

  private void init() {
    setCellId(CELL_ID);
    setDefaultText(" ");
    setEditable(true);
    getStyle().set(StyleAttributes.BACKGROUND_COLOR, LightColors.BLUE);

    getStyle().set(StyleAttributes.PUNCTUATION_LEFT, true);
    getStyle().set(StyleAttributes.PUNCTUATION_RIGHT, true);
    getStyle().set(StyleAttributes.FIRST_POSITION_ALLOWED, true);
    getStyle().set(StyleAttributes.LAST_POSITION_ALLOWED, true);

    RemoveSTHintAction removeSTHintAction = new RemoveSTHintAction();
    // delete the hint when pressed ctrl-delete, delete or backspace
    setAction(CellActionType.DELETE, removeSTHintAction);
    setAction(CellActionType.BACKSPACE, removeSTHintAction);
    // delete the hint when double press 'space'
    setAction(CellActionType.RIGHT_TRANSFORM, removeSTHintAction);
    setAction(CellActionType.LEFT_TRANSFORM, removeSTHintAction);

    // delete the hint when double press 'esc'
    KeyMap keyMap = new KeyMapImpl();
    keyMap.putAction(KeyMap.KEY_MODIFIERS_NONE, "VK_ESCAPE", new RemoveSTHintKeyMapAction());
    addKeyMap(keyMap);

    // create the hint's auto-completion menu
    setSubstituteInfo(new SideTransformSubstituteInfo(myAnchorCell, mySide));
  }


  @NotNull
  @Override
  public CellInfo getCellInfo() {
    return new STHintCellInfo(EditorCell_STHint.this, myAnchorCell);
  }

  @Override
  public void changeText(String text) {
    super.changeText(text);
    if ("".equals(getText())) {
      SideTransformInfoUtil.removeTransformInfo(getSNode());
    }
  }

  @Override
  public void setCaretPosition(int position, boolean selection) {
    if (position != getText().length() && !isRight()) {
      validate(true, false);
    }
    super.setCaretPosition(position, selection);
  }

  @Override
  public void synchronizeViewWithModel() {
  }

  private void removeSTHintAndChangeSelection(final EditorContext context) {
    SideTransformInfoUtil.removeTransformInfo(getSNode());
    context.flushEvents();

    if (myRestoreSelectionCellInfo == null) {
      return;
    }

    jetbrains.mps.nodeEditor.EditorComponent editorComponent = (jetbrains.mps.nodeEditor.EditorComponent) context.getEditorComponent();
    EditorCell newlySelectedCell = myRestoreSelectionCellInfo.findCell(editorComponent);
    if (newlySelectedCell == null) {
      return;
    }
    editorComponent.changeSelection(newlySelectedCell);
    if (newlySelectedCell instanceof EditorCell_Label) {
      newlySelectedCell.end();
    }
  }

  public EditorCell install() {
    assert !myInstalled;
    myInstalled = true;
    if (myAnchorCell == myBigCell) {
      // Creating wrapper collection to hold both nodeCell & STHintCell
      jetbrains.mps.nodeEditor.cells.EditorCell_Collection wrapperCell =
          jetbrains.mps.nodeEditor.cells.EditorCell_Collection.createHorizontal(getContext(), getSNode());
      wrapperCell.setSelectable(false);
      wrapperCell.setBig(true);
      EditorCellContext cellContext = myBigCell.getCellContext();
      assert cellContext != null;
      wrapperCell.setCellContext(cellContext);
      wrapperCell.setCanBeSynchronized(myBigCell instanceof SynchronizeableEditorCell && ((SynchronizeableEditorCell) myBigCell).canBeSynchronized());
      myBigCell.setBig(false);
      if (!isRight()) {
        wrapperCell.addEditorCell(this);
      }
      wrapperCell.addEditorCell(myBigCell);
      if (isRight()) {
        wrapperCell.addEditorCell(this);
      }
      return wrapperCell;
    }

    EditorCell_Collection cellCollection = myAnchorCell.getParent();
    if (isRight()) {
      cellCollection.addEditorCellAfter(this, myAnchorCell);
    } else {
      cellCollection.addEditorCellBefore(this, myAnchorCell);
    }
    return myBigCell;
  }

  private boolean isRight() {
    return mySide == Side.RIGHT;
  }

  @NotNull
  public Side getSide() {
    return mySide;
  }

  public EditorCell uninstall() {
    assert myInstalled;
    if (myAnchorCell == myBigCell) {
      // not necessary to remove EditorCell_STHint from wrapper
      // collection - just returning myBigCell in the end
      myBigCell.setBig(true);
    } else {
      this.getParent().removeCell(this);
    }
    return myBigCell;
  }

  private class RemoveSTHintAction extends AbstractCellAction {
    @Override
    public void execute(EditorContext context) {
      removeSTHintAndChangeSelection(context);
    }
  }

  private class RemoveSTHintKeyMapAction extends KeyMapActionImpl {
    @Override
    public void execute(EditorContext context) {
      removeSTHintAndChangeSelection(context);
    }
  }

  private static class STHintCellInfo extends DefaultCellInfo {
    CellInfo myAnchorCellInfo;

    STHintCellInfo(EditorCell_STHint rightTransformHintCell, EditorCell anchorCell) {
      super(rightTransformHintCell);
      myAnchorCellInfo = anchorCell.getCellInfo();
    }

    @Override
    public EditorCell findCell(@NotNull EditorComponent editorComponent) {
      EditorCell anchorCell = myAnchorCellInfo.findCell(editorComponent);
      return anchorCell != null ? getSTHintCell(anchorCell.getSNode(), editorComponent) : super.findCell(editorComponent);
    }

    @Override
    public EditorCell findClosestCell(@NotNull EditorComponent editorComponent) {
      EditorCell anchorCell = myAnchorCellInfo.findCell(editorComponent);
      if (anchorCell == null) {
        return super.findCell(editorComponent);
      }
      EditorCell_Label rtHint = getSTHintCell(anchorCell.getSNode(), editorComponent);
      return rtHint != null ? rtHint : (jetbrains.mps.nodeEditor.cells.EditorCell) anchorCell;
    }
  }
}
