package jetbrains.mps.debugger.api.ui.tree;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.ide.ui.tree.MPSTreeNode;
import javax.swing.tree.TreePath;
import java.util.Iterator;
import java.util.Arrays;
import jetbrains.mps.ide.ui.tree.MPSTree;
import java.util.List;
import java.util.ArrayList;

/**
 * Debugger tree elements are evaluated in a separate thread, while tree needs to filfil expand/select contract.
 * While evaluation is in process, we use placeholder nodes to populate the tree and later replace them with actual WatchanbleNode.
 * To match nodes, we keep 'escaped' identifier of a future WatchableNode.
 * 
 * IMPORTANT: despite the fact it's public class in api package, don't use outside of MPS. We need to access it from both
 * EvaluationTree and VariablesTree, and there's no better place to put this class I'm aware of.
 */
@GeneratedClass(node = "r:a35795b4-c996-4cf6-bdbd-9ddbda25cde5(jetbrains.mps.debugger.api.ui.tree)/7059155213106620134", model = "r:a35795b4-c996-4cf6-bdbd-9ddbda25cde5(jetbrains.mps.debugger.api.ui.tree)")
public final class PlaceholderTreeNode extends MPSTreeNode {
  /*package*/ PlaceholderTreeNode(String escapedNodeIdentifier) {
    // escapedNodeIdentifier means it's not the value to match directly with WatchableNode.calculateNodeId value
    // Instead, calculateNodeId.replaceAll(TREE_PATH_SEPARATOR, '-') shall be compared against escapedNodeIdentifier to get matched
    // This is due to the fact we use escape mechanism that lose information (we can't reverse the escape).
    setText("...");
    setNodeIdentifier(escapedNodeIdentifier);
  }

  public static TreePath stringToPathWithPlaceholders(MPSTreeNode startNode, String pathStringRelativeToStartNode) {
    Iterator<String> components = Arrays.asList(pathStringRelativeToStartNode.split(MPSTree.TREE_PATH_SEPARATOR)).iterator();
    List<Object> path = new ArrayList<Object>();
    MPSTreeNode current = startNode;
    path.add(current);
    while (components.hasNext()) {
      String pathComponent = components.next();
      if ((pathComponent == null || pathComponent.length() == 0)) {
        // it's odd to expect empty components, but we need to account for this case at least for the root element
        // which seems to be always empty (i.e. pathString always(?) starts with '/')
        continue;
      }
      // pathComponent has TREE_PATH_SEPARATOR == '/' escaped as '-'
      boolean found = false;
      for (int i = 0; i < current.getChildCount(); i++) {
        MPSTreeNode node = (MPSTreeNode) current.getChildAt(i);
        // nodeIdentifier of PlaceholderTreeNode is already an escaped path component, replaceAll() would be no-op for it, but it doesn't hurt
        // to make it apparent I've thought about this scenario
        found = node instanceof PlaceholderTreeNode && ((PlaceholderTreeNode) node).getNodeIdentifier().equals(pathComponent);
        found = found || node.getNodeIdentifier().replaceAll(MPSTree.TREE_PATH_SEPARATOR, "-").equals(pathComponent);
        if (found) {
          path.add(node);
          current = node;
          break;
        }
      }
      if (found) {
        continue;
      }
      if (current instanceof PlaceholderTreeNode || !(current.isInitialized())) {
        // Don't care if PTN is initialized here, just move to add placeholders further on.
        // Perhaps, could make PlaceholderTreeNode.isInitialized() == false constantly (or until otherwise empty doInit() call) for uniformity
        PlaceholderTreeNode placeholder = new PlaceholderTreeNode(pathComponent);
        // placeholder child for the un-initialized node, would get replaced once current completes its initialization
        current.add(placeholder);
        // use it as tree path element
        path.add(placeholder);
        // schedule init for the current node
        current.init();
        // and get ready for the next path component, if any
        current = placeholder;
      } else {
        // we've got initialized node, but no child matched expected path component, we assume the tree structure has changed and
        // therefore we stop path trace here.
        return new TreePath(path.toArray());
      }
    }
    return new TreePath(path.toArray());
  }

}
