package annotator.scanner;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import annotator.find.LocalVariableCriterion;

import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.util.Pair;

/** LocalVariableScanner stores information about the names and offsets of
 * local variables inside a method, and can also be used to scan the source
 * tree and determine the index of a local variable with a given name, so that
 * the i^th index corresponds to the i^th declaration of a local variable with
 * that name, using 0-based indexing.
 */
public class LocalVariableScanner extends TreePathScanner<Void, Void> {
  /**
   * Computes the index i of the given tree along the given tree path
   * such that it is the i^th declaraction of the local variable with the given
   * var name, using 0-based indexing.
   *
   * @param path the source path that ends in varTree
   * @param varTree the variable tree that declares the local variable
   * @param varName the name of the local variable
   * @return the index of the variable tree with respect to the given
   *  local variable name
   */
  public static int indexOfVarTree(
        TreePath path,
        Tree varTree,
        String varName) {
    // Move all the way up the source tree to the overall method tree
    // containing varTree
    while (path.getLeaf().getKind() != Tree.Kind.METHOD) {
      path = path.getParentPath();
      if (path == null) {
        // Was called on something other than a local variable, so return
        // -1 to ensure that it doesn't match anything.
        return -1;
      }
    }
     LocalVariableScanner lvts =
      new LocalVariableScanner(varTree, varName);

     try {
       lvts.scan(path, null);
     } catch(Throwable e) {
       System.out.println("LocalVariableScanner: can't locate: " + varTree);
       return -2; // Don't return -1, which is above return code
     }
    return lvts.index;
  }

  private int index = -1;
  private boolean done = false;
  private Tree varTree;
  private String varName;

  private LocalVariableScanner(
        Tree varTree,
        String varName) {
    this.index = -1;
    this.done = false;
    this.varTree = varTree;
    this.varName = varName;
  }

  @Override
  public   Void visitVariable(VariableTree node, Void p) {
    // increment index only if you have not already reached the right node, and
    // if this node declares the same local variable you are searching for
    if (varName.equals(node.getName().toString())) {
      if (!done) {
        index++;
      }
      if (varTree == node) {
        done = true;
      }
    }
    return p;
  }



  // TODO: refactor class keys to avoid so many uses of generics

  // mapping from (method-name, variable-index, start-offset)
  // to variable name
  private static Map<Pair<String, Pair<Integer,Integer>>, String>
    methodNameIndexMap =
    new HashMap<Pair<String, Pair<Integer,Integer>>, String>();

  // map from method to map from variable name to
  // a list of start offsets
  private static Map<String, Map<String, List<Integer>>>
    methodNameCounter =
      new HashMap<String, Map<String,List<Integer>>>();

  /**
   * Adds the given variable specified as a pair of method name and
   *  (index, start-offset) under the given name to the list of all local
   *  variables.
   *
   * @param varInfo a pair of the method and a pair describing the local
   *  variable index and start offset of the local variable
   * @param name the name of the local variable
   */
  public static void addToMethodNameIndexMap(Pair<String, Pair<Integer,Integer>> varInfo, String name) {
    methodNameIndexMap.put(varInfo, name);
  }

  /**
   * Gets the name of the local variable in the given method, and at the
   *  the given index and offset.
   *
   * @param varInfo a pair of the method name and a pair of the local variable's
   *  index and start offset
   * @return the name of the local variable at the specified location
   */
  public static  String getFromMethodNameIndexMap(Pair<String, Pair<Integer, Integer>> varInfo) {
    return methodNameIndexMap.get(varInfo);
  }

  /**
   * Adds to the given method the fact that the local variable with the given
   *  name is declared at the given start offset.
   *
   * @param methodName the method containing the local variable
   * @param varName the name of the local variable
   * @param offset the start offset of the local variable
   */
  public static void addToMethodNameCounter(
        String methodName,
        String varName,
        Integer offset) {
     Map<String, List<Integer>> nameOffsetCounter =
      methodNameCounter.get(methodName);
    if (nameOffsetCounter == null) {
      nameOffsetCounter = new HashMap<String, List<Integer>>();
      methodNameCounter.put(methodName, nameOffsetCounter);
    }

     List<  Integer> listOfOffsets =
      nameOffsetCounter.get(varName);
    if (listOfOffsets == null) {
      listOfOffsets = new ArrayList<  Integer>();
      nameOffsetCounter.put(varName, listOfOffsets);
    }

    listOfOffsets.add(offset);
  }

  /**
   * Returns a list of all start bytecode offsets of variable declarations with
   * the given variable name in the given method.
   *
   * @param methodName the name of the method
   * @param varName the name of the local variable
   * @return a list of start offsets for live ranges of all local variables
   * with the given name in the given method
   */
  public static List<Integer> getFromMethodNameCounter(String methodName, String varName) {
    return methodNameCounter.get(methodName).get(varName);
  }
}

@@ -898,17 +898,16 @@
  declara
-c
 tion of 

Fix a typo.