package org.eclipse.stp.sc.xmlvalidator.rule.model;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.eclipse.stp.common.logging.LoggingProxy;
import org.eclipse.stp.sc.xmlvalidator.rule.parser.RuleXmlParser;
import org.w3c.dom.Document;

public class VRuleInstance extends VRule {
	private static final LoggingProxy LOG = LoggingProxy.getlogger(VRuleInstance.class);
	List<VRuleError> errorList = new LinkedList<VRuleError>();
	
	XPathFactory factory;
	XPath xpath;
	
	public VRuleInstance(VRuleDef def) {
		super(def);
		factory = XPathFactory.newInstance();
        xpath = factory.newXPath();
	}
	
	
	public boolean validate(Document doc){
		//clear the previous errors
//		LOG.debug("validating using rule:" + ruleDef.getId());
		errorList.clear();
	    Collection<VRuleAssert> asserts = ruleDef.getAssertList();
	    for (VRuleAssert ruleAssert : asserts) {
	    	validateOneAssert(doc, ruleAssert);
	    }
	    return errorList.size() == 0;
	}
	
	private void validateOneAssert(Document doc, VRuleAssert ruleAssert) {
		XPathExpression exp = ruleAssert.xpathExp; 
		if ( exp == null) {
			//somthing wrong here. the xpath defined in rule file did not pass compile
			return;
		}
		try {
			//String retObj = exp.evaluate(doc);
			String retString = (String)exp.evaluate(doc, XPathConstants.STRING);
//			LOG.debug("xpath result string:" + retString);
			String lineNumberStr = "1";
			if (retString.lastIndexOf(RuleXmlParser.XPATH_RESULT_SEPERATOR) > 0) {
				lineNumberStr = retString.substring(0, 
						retString.lastIndexOf(RuleXmlParser.XPATH_RESULT_SEPERATOR));
			}
			String boolStr = retString.substring(
					retString.lastIndexOf(RuleXmlParser.XPATH_RESULT_SEPERATOR) + 1,
					retString.length());
//			LOG.debug("linenumberStr:" + lineNumberStr + " boolStr:" + boolStr);
			
		    Boolean ret = Boolean.parseBoolean(boolStr);
		    if (!isValid(ret, ruleAssert.getAssertType())) {
		    	LOG.debug("invlid xml for exp:" + ruleAssert.expression);
		    	errorList.add(new VRuleError(ruleAssert.getErrorMsg(), Integer.parseInt(lineNumberStr),
		    			ruleDef.getSeverityAsInt()));
		    }
		} catch (Exception e) {
			LOG.error("error during xpath validating", e);
			e.printStackTrace();
		}
	}
	
	
	private boolean isValid(boolean expResult, VRuleAssert.Type type) {
		if (type == VRuleAssert.Type.TRUE && expResult==false) {
			return false;
		}
		if (type == VRuleAssert.Type.FALSE && expResult==true) {
			return false;
		}
		return true;
	}
	
	public List<VRuleError> getErrors() {
		return errorList;
	}
	
}
