package org.mvel2.util;

import java.util.ArrayList;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author Mike Brock
 */
public class LineMapper {
  private char[] expr;

  private ArrayList<Node> lineMapping;
  private Set<Integer> lines;

  public LineMapper(char[] expr) {
    this.expr = expr;
  }

  public LineLookup map() {
    lineMapping = new ArrayList<Node>();
    lines = new TreeSet<Integer>();

    int cursor = 0;
    int start = 0;
    int line = 1;

    for (; cursor < expr.length; cursor++) {
      switch (expr[cursor]) {
        case '\n':
          lines.add(line);
          lineMapping.add(new Node(start, cursor, line++));
          start = cursor + 1;
          break;
      }
    }

    if (cursor > start) {
      lines.add(line);
      lineMapping.add(new Node(start, cursor, line));
    }

    return new LineLookup() {
      public int getLineFromCursor(int cursor) {
        for (Node n : lineMapping) {
          if (n.isInRange(cursor)) {
            return n.getLine();
          }
        }
        return -1;
      }

      public boolean hasLine(int line) {
        return lines.contains(line);
      }
    };
  }

  public static interface LineLookup {
    public int getLineFromCursor(int cursor);
    public boolean hasLine(int line);
  }

  private static class Node implements Comparable<Node> {
    private int cursorStart;
    private int cursorEnd;

    private int line;


    private Node(int cursorStart, int cursorEnd, int line) {
      this.cursorStart = cursorStart;
      this.cursorEnd = cursorEnd;
      this.line = line;
    }

    public int getLine() {
      return line;
    }

    public boolean isInRange(int cursor) {
      return cursor >= cursorStart && cursor <= cursorEnd;
    }

    public int compareTo(Node node) {
      if (node.cursorStart >= cursorEnd) {
        return 1;
      } else if (node.cursorEnd < cursorStart) {
        return -1;
      } else {
        return 0;
      }
    }
  }
}
