/*
 * Copyright 2003-2016 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.generator;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeReference;
import org.jetbrains.mps.openapi.module.SRepository;

import javax.swing.Icon;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * UI-related aspects of tracer nodes, not to use TracerNode from GenerationTracerView or GenerationTracerTree.
 * @author Artem Tikhomirov
 */
final class TraceNodeUI {
  @Nullable
  private final Kind myKind;
  @Nullable
  private final SNodeReference myTargetNode;
  private final Icon myIcon;
  private List<TraceNodeUI> myChildren;
  private final String myText;

  /*package*/ TraceNodeUI(@NotNull String text, Icon icon, @Nullable SNodeReference targetNode) {
    myKind = null;
    myTargetNode = targetNode;
    myIcon = icon;
    myText = text;
  }

  /*package*/ TraceNodeUI(@Nullable Kind kind, @Nullable SNodeReference targetNode) {
    myKind = kind;
    myTargetNode = targetNode;
    myIcon = null;
    myText = null;
  }

  public String getText(SRepository repo) {
    if (myText != null) {
      return myText;
    }
    if (myTargetNode == null) {
      if (myKind != null) {
        return '<' + String.valueOf(myKind) + '>';
      }
      return "<unknown>";
    }
    SNode n = myTargetNode.resolve(repo);
    if (n == null) {
      // GenTrace records additions and deletions as <no node id> at corresponding side, here
      // we check whether it's the node we simply can't access now, or it's consciously recorded addition/removal
      if (myTargetNode.getNodeId() == null) {
        if (myKind == Kind.INPUT) {
          return "<no input>";
        } else if (myKind == Kind.OUTPUT) {
          return "<no output>";
        }
        // fall-through
      }
      return myTargetNode.toString();
    }
    // TODO initialize myText once in the cons
    String text = String.format("[%s] %s (%s)", n.getConcept().getName(), n.getPresentation(), n.getNodeId());
    if (myKind == Kind.APPROXIMATE_OUTPUT || myKind == Kind.APPROXIMATE_INPUT) {
      return "[approximate location] " + text;
    } else {
      return text;
    }
  }
  public Icon getIcon(SRepository contextRepo) {
    if (myIcon != null) {
      return myIcon;
    }
    return Icons.getIcon(myKind, myTargetNode.resolve(contextRepo));
  }

  public boolean hasChildren() {
    return myChildren != null && !myChildren.isEmpty();
  }
  public Iterable<TraceNodeUI> getChildren() {
    if (myChildren == null) {
      return Collections.emptyList();
    }
    return myChildren;
  }
  public void addChild(@NotNull TraceNodeUI child) {
    if (myChildren == null) {
      myChildren = new ArrayList<TraceNodeUI>(5);
    }
    myChildren.add(child);
  }

  // whatever it is, MPSTreeNode requires one
  public String getNodeIdentifier() {
    if (myTargetNode != null) {
      return String.valueOf(myTargetNode.hashCode());
    } else if (myKind != null) {
      return '<' + String.valueOf(myKind) + '>';
    } else {
      return String.valueOf(hashCode());
    }
  }

  public boolean hasNextStep() {
    return myKind == Kind.OUTPUT || myKind == Kind.APPROXIMATE_OUTPUT;
  }

  public boolean hasPrevStep() {
    return myKind == Kind.INPUT || myKind == Kind.APPROXIMATE_INPUT;
  }

  @Nullable
  public SNodeReference getNavigateTarget() {
    return myTargetNode;
  }

  public enum Kind {
    INPUT("input"),
    OUTPUT("output"),
    TEMPLATE("template"),
    MACRO("macro"),
    RULE("rule"),
    RULE_CONSEQUENCE("rule consequence"),
    SWITCH("switch"),
    COPY_OPERATION("copy"),
    MAPPING_SCRIPT("mapping script"),
    APPROXIMATE_OUTPUT("approximate output"),
    APPROXIMATE_INPUT("approximate input");

    private String myText;

    Kind(String text) {
      myText = text;
    }

    public String toString() {
      return myText;
    }
  }
}
