package com.jclark.xsl.tr;

import com.jclark.xsl.om.*;
import com.jclark.xsl.expr.PathPattern;
import com.jclark.xsl.expr.TopLevelPattern;
import com.jclark.xsl.expr.PatternList;
import com.jclark.xsl.expr.ExprContext;

import java.util.Vector;
import java.util.Enumeration;

class TemplateRuleSet {
  private static class Rule {
    final PathPattern pattern;
    final Importance ruleImportance;
    final Importance importImportance;
    final Priority priority;
    final Action action;
    Rule(PathPattern pattern,
	 Importance ruleImportance,
	 Importance importImportance,
	 Priority priority,
	 Action action) {
      this.ruleImportance = ruleImportance;
      this.importImportance = importImportance;
      if (priority == null)
	this.priority = Priority.createDefault(pattern.getDefaultPriority());
      else
        this.priority = priority;
      this.pattern = pattern;
      this.action = action;
    }
    static boolean isBetter(Rule rule1, Rule rule2) {
      int n = rule1.ruleImportance.compareTo(rule2.ruleImportance);
      if (n == 0)
	return rule1.priority.compareTo(rule2.priority) > 0;
      // if n < 0, rule1.importance - rule2.importance < 0
      // => rule1.importance < rule2.importance
      // => false
      return n > 0;
    }
  }
  
  private Vector rules = new Vector();
  private PatternList patternList = new PatternList();
  private Action builtinAction;

  TemplateRuleSet(Action builtinAction) {
    this.builtinAction = builtinAction;
  }

  void compile() {
    reverse(rules);
    sortRulesVector(rules);
    for (Enumeration iter = rules.elements(); iter.hasMoreElements();) {
      Rule r = (Rule)iter.nextElement();
      patternList.add(r.pattern, r);
    }
  }
  
  private static void reverse(Vector v) {
    int i = 0;
    int j = v.size() - 1;
    for (; i < j; i++, j--) {
      Object tem = v.elementAt(i);
      v.setElementAt(v.elementAt(j), i);
      v.setElementAt(tem, j);
    }
      
  }

  private static void sortRulesVector(Vector v) {
    // Insertion sort
    int sz = v.size();
    for (int i = 1; i < sz; i++) {
      Rule rule = (Rule)v.elementAt(i);
      int j;
      for (j = i; j > 0; j--) {
	Rule tem = (Rule)v.elementAt(j - 1);
	// Order best first.
	// So stop when rule is worse or equal to tem
	// => stop when not rule is better than tem.
	if (!Rule.isBetter(rule, tem))
	  break;
	v.setElementAt(tem, j);
      }
      v.setElementAt(rule, j);
    }
  }

  void add(TopLevelPattern pattern,
	   Importance ruleImportance,
	   Importance importImportance,
	   Priority priority,
	   Action action) {
    PathPattern[] alternatives = pattern.getAlternatives();
    for (int i = 0; i < alternatives.length; i++)
      rules.addElement(new Rule(alternatives[i],
				ruleImportance,
				importImportance,
				priority,
				action));
  }

  Action getAction(Node node, ExprContext context) throws XSLException {
    Rule r = (Rule)patternList.get(node, context);
    if (r == null)
      return builtinAction;
    return r.action;
  }

  Action getImportAction(Node node, ExprContext context, int importLevel) throws XSLException {
    Enumeration rules = patternList.getAll(node, context);
    Rule r = (Rule)rules.nextElement();
    Importance minImportance = r.importImportance;
    Importance limImportance = r.ruleImportance;
    int currentLevel = 0;
    while (rules.hasMoreElements()) {
      r = (Rule)rules.nextElement();
      if (r.ruleImportance.compareTo(limImportance) >= 0)
	break;
      if (r.ruleImportance.compareTo(minImportance) >= 0) {
	if (currentLevel == importLevel)
	  return r.action;
	currentLevel++;
	minImportance = r.importImportance;
	limImportance = r.ruleImportance;
      }
    }
    return builtinAction;
  }
}
