package com.intellij.structuralsearch.impl.matcher;

import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
import com.intellij.dupLocator.iterators.NodeIterator;
import com.intellij.openapi.util.Key;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.structuralsearch.StructuralSearchUtil;
import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
import com.intellij.structuralsearch.impl.matcher.handlers.SimpleHandler;
import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
import com.intellij.structuralsearch.impl.matcher.strategies.MatchingStrategy;

import java.util.HashMap;
import java.util.List;

/**
 * Class to hold compiled pattern information
 */
public abstract class CompiledPattern {
  private SearchScope scope;
  private NodeIterator nodes;
  private MatchingStrategy strategy;
  private PsiElement targetNode;
  private int optionsHashStamp;
  private int nodeCount;

  // @todo support this property during matching (how many nodes should be advanced)
  // when match is not successful (or successful partially)
  //private int advancement = 1;

  public abstract String[] getTypedVarPrefixes();
  public abstract boolean isTypedVar(String str);

  public void setTargetNode(final PsiElement element) {
    targetNode = element;
  }

  public PsiElement getTargetNode() {
    return targetNode;
  }

  public int getOptionsHashStamp() {
    return optionsHashStamp;
  }

  public void setOptionsHashStamp(final int optionsHashStamp) {
    this.optionsHashStamp = optionsHashStamp;
  }

  public static final Key<Object> HANDLER_KEY = Key.create("ss.handler");

  public MatchingStrategy getStrategy() {
    return strategy;
  }

  public void setStrategy(MatchingStrategy strategy) {
    this.strategy = strategy;
  }

  public int getNodeCount() {
    return nodeCount;
  }

  public NodeIterator getNodes() {
    return nodes;
  }

  public void setNodes(List<PsiElement> elements) {
    this.nodes = new ArrayBackedNodeIterator(PsiUtilCore.toPsiElementArray(elements));
    this.nodeCount = elements.size();
  }

  public boolean isTypedVar(final PsiElement element) {
    return element!=null && isTypedVar( element.getText() );
  }

  public boolean isRealTypedVar(PsiElement element) {
    if (element!=null && element.getTextLength()>0) {
      String str = getTypedVarString(element);
      if (str.length()==0) {
        return false;
      }
      return isTypedVar( str );
    } else {
      return false;
    }
  }

  public String getTypedVarString(PsiElement element) {
    return StructuralSearchUtil.getProfileByPsiElement(element).getTypedVarString(element);
  }

  private final HashMap<Object,MatchingHandler> handlers = new HashMap<Object,MatchingHandler>();

  public MatchingHandler getHandlerSimple(PsiElement node) {
    return handlers.get(node);
  }

  private PsiElement last;
  private MatchingHandler lastHandler;

  public MatchingHandler getHandler(PsiElement node) {
    if (node==last) {
      return lastHandler;
    }
    MatchingHandler handler = handlers.get(node);

    if (handler==null) {
      handler = new SimpleHandler();
      setHandler(node,handler);
    }

    last = node;
    lastHandler = handler;

    return handler;
  }

  public MatchingHandler getHandler(String name) {
    return handlers.get(name);
  }

  public void setHandler(PsiElement node, MatchingHandler handler) {
    last = null;
    handlers.put(node,handler);
  }

  public SubstitutionHandler createSubstitutionHandler(
    String name, String compiledName, boolean target,int minOccurs, int maxOccurs, boolean greedy) {

    SubstitutionHandler handler = (SubstitutionHandler) handlers.get(compiledName);
    if (handler != null) return handler;

    handler = doCreateSubstitutionHandler(name, target, minOccurs, maxOccurs, greedy);

    handlers.put(compiledName,handler);

    return handler;
  }

  protected SubstitutionHandler doCreateSubstitutionHandler(String name, boolean target, int minOccurs, int maxOccurs, boolean greedy) {
    return new SubstitutionHandler(name, target, minOccurs, maxOccurs, greedy);
  }

  public SearchScope getScope() {
    return scope;
  }

  public void setScope(SearchScope scope) {
    this.scope = scope;
  }

  public void clearHandlers() {
    handlers.clear();
    last = null;
    lastHandler = null;
  }

  void clearHandlersState() {
    for (final MatchingHandler h : handlers.values()) {
      if (h != null) h.reset();
    }
  }
}

@@ -327,16 +327,78 @@
 ilCore;%0A
+import com.intellij.structuralsearch.StructuralSearchProfile;%0A
 import c
@@ -2710,22 +2710,55 @@
 ) %7B%0A    
-return
+final StructuralSearchProfile profile =
  Structu
@@ -2802,16 +2802,101 @@
 element)
+;%0A    if (profile == null) %7B%0A      return element.getText();%0A    %7D%0A    return profile
 .getType

fix NPE