package edu.berkeley.cde.processors;

import javax.xml.transform.TransformerException;
import org.orbeon.oxf.processor.ProcessorInputOutputInfo;
import org.orbeon.oxf.processor.SimpleProcessor;
import org.orbeon.oxf.processor.pipeline.PipelineContext;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import javax.xml.transform.sax.SAXResult;
import org.iso_relax.verifier.*;
import org.w3c.dom.*;
import java.io.*;
import org.apache.xpath.XPathAPI;
import org.apache.xpath.objects.XObject;
import org.orbeon.oxf.xml.TransformerUtils;
import javax.xml.transform.dom.DOMSource;
import org.orbeon.oxf.resources.*;

/**
 * @author peter charles
 *This Class takes an xform input and an xml document in the form of 
 *<ValidateElements schema="Location">
 *	<Xpath valid="false">/Element/Element/ElementToValidate</Xpath>
 *	<Xpath valid="false">/Element/ElementToValidate</Xpath>
 *	........
 *</ValidateElements>
 *It will return the same document with the valid attribute set to 
 *the outcome of a validation using the xform input and the schema
 *
 */
public class NodeValidationProcessor extends SimpleProcessor {
	
	private org.w3c.dom.Document xpathDOM;
   	private org.w3c.dom.Document xformToBeValidated;
	private Verifier verifier; 
	  
   public NodeValidationProcessor() {
		addInputInfo(new ProcessorInputOutputInfo("xform"));
		addInputInfo(new ProcessorInputOutputInfo("xpath"));
		addOutputInfo(new ProcessorInputOutputInfo("xpath"));
	}

	public void generateXpath(PipelineContext context, ContentHandler contentHandler) throws SAXException{

		xpathDOM = readInputAsDOM(context,"xpath");	
		xformToBeValidated = readInputAsDOM(context,"xform");

		//get schema reference
		Element root = xpathDOM.getDocumentElement();
		String schema = root.getAttribute("schema");
		InputStream is = ResourceManagerWrapper.instance().getContentAsStream(schema);
		//build verifier from schema
		BuildVerifier(is);

		//Retrive all Xpath Elements as a node list
		NodeList xpathList = xpathDOM.getElementsByTagName("Xpath");  
		//Run through each xpath node and validate corisponding node of xform
		for(int x=0;x<xpathList.getLength();x++)
		{
			try{	
			//get list of xpaths to evaluate
			XObject nodeValue = XPathAPI.eval(xpathList.item(x),"string()");
			String xpathValue = nodeValue.str();

			//call for validation
			boolean valid = ValidateNode(xpathValue);

			//Get attributes from Xpath node and set validity
			NamedNodeMap nm = xpathList.item(x).getAttributes();
			nm.item(0).setNodeValue(valid+"");
			

			}catch(TransformerException te){//System.out.println("Start TE: "+ te);
			}						
		}
		//Hook up to output
		try {
			TransformerUtils.getXMLIdentityTransformer().transform (new DOMSource(xpathDOM), new SAXResult(contentHandler));
			}catch (TransformerException te) {
							//System.out.println(te);
							//return te.toString();
				}
	}
	/**
	 * Validates a Node in a xform document when give an xpath statement
	 *
	 */
	private boolean ValidateNode(String xpath){
		boolean valid = false;
		try{
	
			Node testNode = XPathAPI.selectSingleNode(xformToBeValidated,xpath);
			valid =verifier.verify(testNode);
			return valid;
		}catch(TransformerException te){//System.out.println("vN TE: "+te);
		}catch(SAXException se){//System.out.println("vN SaxE: "+ se);
		}catch(Exception e){//e.getLocalizedMessage();e.getCause();e.printStackTrace();
		}
		
		return valid;
	}	
	/**
	 * Builds a Verifer given a schema
	 * Should I overload method to take other forms of input?
	 */	
	private void BuildVerifier(InputStream schema){
		// Use MSV JARV API and compile a schema.
		try{
		VerifierFactory factory = new com.sun.msv.verifier.jarv.TheFactoryImpl();
		verifier = factory.compileSchema(schema).newVerifier();
		}catch(IOException io){//System.out.println("BV IO: "+io);
		}catch(SAXException se){//System.out.println("BV SaxE: "+ se);
		}catch(VerifierConfigurationException vce){//System.out.println("BV VCE: "+vce);
		}
	}
}
