/*
 * 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.smodel.action;

import jetbrains.mps.actions.runtime.impl.ActionsUtil;
import jetbrains.mps.nodeEditor.EditorManager;
import jetbrains.mps.nodeEditor.cellMenu.AbstractNodeSubstituteInfo;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.typesystem.inference.TypeChecker;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;

public class DefaultSChildSubstituteAction extends AbstractSubstituteAction {
  private SNode myCurrentChild;
  private SNode myOldChild;
  private IChildNodeSetter mySetter;
  private SAbstractConcept myConcept;

  public DefaultSChildSubstituteAction(SAbstractConcept outputConcept, SNode parentNode, SNode currentChild, IChildNodeSetter setter) {
    super(parentNode);
    myConcept = outputConcept;
    myCurrentChild = currentChild;
    myOldChild = myCurrentChild;
    mySetter = setter;
  }


  //todo do we need this?
  private void setupOldChild() {
    if (myCurrentChild != null && myCurrentChild.getUserObject(EditorManager.OLD_NODE_FOR_SUBSTITUTION) != null) {
      myOldChild = (SNode) myCurrentChild.getUserObject(EditorManager.OLD_NODE_FOR_SUBSTITUTION);
    } else {
      myOldChild = myCurrentChild;
    }
  }

  @Override
  public final SNode doSubstitute(@Nullable final EditorContext editorContext, String pattern) {
    SNode parentNode = getSourceNode();
    SNode newChild = createChildNode(parentNode.getModel());
    if (newChild != null) {
      SNode result = mySetter.execute(parentNode, myCurrentChild, newChild, editorContext);
      if (result != newChild) {
        // node was wrapped by mySetter
        return result;
      }
      return selectChildNode(result, parentNode.getModel(), pattern, editorContext);
    }
    return null;
  }

  private SNode createChildNode(SModel model) {
    return NodeFactoryManager.createNode(myConcept, myOldChild, getSourceNode(), model);
  }

  protected SNode selectChildNode(SNode createdNode, SModel model, String pattern, EditorContext editorContext) {
    return createdNode;
  }

  @Override
  public Object getParameterObject() {
    return myConcept.getDeclarationNode();
  }

  @Override
  public SNode getOutputConcept() {
    return myConcept.getDeclarationNode();
  }

  @Override
  public SNode getIconNode(String pattern) {
    return getOutputConcept();
  }

  @Override
  public SNode getActionType(String pattern) {
    SNode node = createChildNode(AbstractNodeSubstituteInfo.getModelForTypechecking());
    if (node == null) return null;
    if (ActionsUtil.isInstanceOfIType(node)) return node;

    //the following is for smart-type completion

    //todo add own model for typecheck
    AbstractNodeSubstituteInfo.getModelForTypechecking().addRootNode(node);
    try {
      return TypeChecker.getInstance().getTypeOf(node);
    } finally {
      AbstractNodeSubstituteInfo.getModelForTypechecking().removeRootNode(node);
    }
  }

  @Override
  public String getMatchingText(String pattern) {
    String conceptAlias = myConcept.getConceptAlias();
    if (conceptAlias != null && !conceptAlias.isEmpty()) {
      return conceptAlias;
    }
    return myConcept.getName();
  }

  @Override
  public String getDescriptionText(String pattern) {
    return myConcept.getLanguage().getQualifiedName();
  }
}
