package org.opengroupware.jope.rules;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opengroupware.jope.eocontrol.EOBooleanQualifier;
import org.opengroupware.jope.eocontrol.EOQualifier;
import org.opengroupware.jope.eocontrol.EOQualifierParser;
import org.opengroupware.jope.foundation.NSJavaRuntime;
import org.opengroupware.jope.foundation.NSObject;
import org.opengroupware.jope.foundation.NSPropertyListParser;
import org.opengroupware.jope.foundation.UString;

// TODO: proper reports errors in last-exception !
// TODO: improve performance
// TODO: parse assignment class? (eg "a = b; (BoolAssignment)")

public class RuleParser extends NSObject {
  protected static Log log = LogFactory.getLog("JoRuleParser");
  
  public static int RULE_PRIORITY_NORMAL = 100;

  public Rule parseRule(String _s) {
    if (_s == null || _s.length() == 0) return null;
    
    int idx = UString.indexOfStringBySkippingQuotes(_s, "=>", "\"'", '\\');
    if (idx < 3) {
      log.info("could not parse rule: '" + _s + "'");
      return null;
    }
    
    /* parse qualifier */
    
    EOQualifier q = this.parseQualifier(_s.substring(0, idx).trim());
    if (q == null) {
      log.info("could not parse qualifier of rule: '" + _s + "'");
      return null;
    }
    
    /* scan for priority separator */
    
    String remainder = _s.substring(idx + 2);
    idx = UString.indexOfStringBySkippingQuotes(remainder, ";", "\"", '\\');
    
    /* parse priority */
    
    int priority = RULE_PRIORITY_NORMAL;
    if (idx != -1)
      priority = this.parsePriority(remainder.substring(idx + 1).trim());
    
    /* parse action / assignment */
    
    Object action = this.parseAction(remainder.substring(0, idx));
    if (action == null) {
      log.info("could not parse action of rule: '" + _s + "'");
      return null;
    }
    
    /* return rule */
    return new Rule(q, action, priority);
  }
  
  public EOQualifier parseQualifier(String _s) {
    if (_s == null || _s.length() == 0) return null;
    
    if ("*true*".equals(_s))
      return EOBooleanQualifier.trueQualifier;
    if ("*false*".equals(_s))
      return EOBooleanQualifier.falseQualifier;
    
    EOQualifierParser p =
      new EOQualifierParser(_s.toCharArray(), null /* args */);
    
    return p.parseQualifier();
  }
  
  public int parsePriority(String _s) {
    if (_s == null || _s.length() == 0)
      return RULE_PRIORITY_NORMAL;
    
    char c0 = _s.charAt(0);
    if (Character.isDigit(c0) || c0 == '-')
      return NSJavaRuntime.intValueForObject(_s);
    
    if ("important".equals(_s)) return 1000;
    if ("very high".equals(_s)) return 200;
    if ("high".equals(_s))      return 150;
    if ("normal".equals(_s))    return RULE_PRIORITY_NORMAL;
    if ("default".equals(_s))   return RULE_PRIORITY_NORMAL;
    if ("low".equals(_s))       return 50;
    if ("very low".equals(_s))  return 5;
    if ("fallback".equals(_s))  return 0;
    
    log.warn("unknown rule priority: '" + _s + "'");
    return RULE_PRIORITY_NORMAL;
  }
  
  protected static final Class[] AssiCtorSig = { String.class, Object.class };
  
  public Object parseAction(String _s) {
    if (_s == null || _s.length() == 0)
      return null;
    
    int idx = UString.indexOfStringBySkippingQuotes(_s, "=", "\"'", '\\');
    if (idx < 1) {
      log.info("could not parse assignment: '" + _s + "'");
      return null;
    }
    
    String key    = _s.substring(0, idx).trim();
    String valstr = _s.substring(idx + 1).trim();
    
    Class assignmentClass = RuleKeyAssignment.class;
    Object value          = valstr;
    
    /* parse value */
    
    if (valstr.length() > 0) {
      char c0 = valstr.charAt(0);
      
      if (c0 == '"' || c0 == '\'') { /* quoted string */
        if (valstr.charAt(valstr.length() - 1) != c0) {
          log.error("string value of assignment misses a closing quote");
          value = valstr.substring(1);
        }
        else
          value = valstr.subSequence(1, valstr.length() - 1);
      }
      else if (Character.isDigit(c0) || c0 == '-') { /* number */
        value = NSJavaRuntime.intValueForObject(valstr);
      }
      else if (c0 == '{' || c0 == '(') { /* property list */
        NSPropertyListParser p = new NSPropertyListParser();
        value = p.parse(valstr);
        if (value == null)
          log.error("could not parse plist of assignment: " + valstr);
      }
      else {
        if      (valstr.equals("true"))  value = new Boolean(true);
        else if (valstr.equals("false")) value = new Boolean(false);
        else if (valstr.equals("YES"))   value = new Boolean(true);
        else if (valstr.equals("NO"))    value = new Boolean(false);
        else if (valstr.equals("null"))  value = null;
        else if (valstr.equals("nil"))   value = null;
      }
      
      if (value != valstr)
        assignmentClass = RuleAssignment.class;
    }
    
    /* construct */
    
    return NSJavaRuntime.NSAllocateObject
      (assignmentClass, AssiCtorSig, new Object[] { key, value } );
  }
}
