
package gov.va.med.cds.response.organizer.strategy;

import java.net.URI;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.io.DOMReader;
import org.dom4j.io.DOMWriter;

import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.response.organizer.ResponseOrganazerException;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.XQueryCompiler;
import net.sf.saxon.s9api.XQueryEvaluator;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmValue;

public class DefaultXQueryStrategy implements OrderingStrategyInterface {
	private static final String BASE_URI = "xquery:root";
	private static final String EXPRESSION = "for $p in %s order by $p %s return $p";
	private Map<String, String> elementsToSortMap;
	private Processor saxonProcessor;

	/**
	 * Orders XML document using XQuery.
	 * 
	 * @param sortExpression
	 * @param responseDoc
	 * @return sorted XML document
	 * @throws ResponseOrganazerException
	 */

	@Override

	@SuppressWarnings("unchecked")
	public Document order(String sortExpression, String domainEntryPoint, Document responseDoc)
			throws ResponseOrganazerException 
	{
		String elementsToSort = (elementsToSortMap != null) ? elementsToSortMap.get(domainEntryPoint) : null;
		if (elementsToSort == null) 
		{
			throw new ResponseOrganazerException(ErrorCodeEnum.XQUERY_EXPRESSION_EXCEPTION, domainEntryPoint,
					elementsToSort, sortExpression);
		}

		try 
		{
			String expression = String.format(EXPRESSION, elementsToSort, sortExpression);
							
			DocumentBuilder builder = saxonProcessor.newDocumentBuilder();
			org.w3c.dom.Document inDocument = new DOMWriter().write(responseDoc);
			XdmNode sourceDocument = builder.wrap(inDocument);

			XQueryCompiler compiler = saxonProcessor.newXQueryCompiler();
			compiler.setBaseURI(new URI(BASE_URI));
			XQueryExecutable exp = compiler.compile(expression);

			XQueryEvaluator evaluator = exp.load();
			evaluator.setSource(sourceDocument.asSource());
			XdmValue xqueryResult = evaluator.evaluate();

			org.w3c.dom.Node parent = null;
			org.w3c.dom.Node firstChild = null;
			org.w3c.dom.Node nd = null;
			for (XdmItem item : xqueryResult) 
			{
				nd = (org.w3c.dom.Node) ((XdmNode) item).getExternalNode();
				if (parent == null || firstChild == null) 
				{
					parent = nd.getParentNode();
					firstChild = parent.getFirstChild();
				}

				if (nd != firstChild) 
				{
					parent.insertBefore(nd, firstChild);
				}
				else 
				{
					firstChild = firstChild.getNextSibling();
				}
			}
			responseDoc = new DOMReader().read(inDocument);

		} catch (Exception e) {
			throw new ResponseOrganazerException(ErrorCodeEnum.XQUERY_EXPRESSION_EXCEPTION, e, elementsToSort,
					sortExpression);
		}

		return responseDoc;
	}

	/**
	 * @return the elementsToSort
	 */
	public Map<String, String> getElementsToSortMap() {
		return elementsToSortMap;
	}

	/**
	 * @param elementsToSort
	 *            the elementsToSort to set
	 */
	public void setElementsToSortMap(Map<String, String> elementsToSortMap) {
		this.elementsToSortMap = elementsToSortMap;
	}
	
	public void setSaxonProcessor(Processor saxonProcessor) {
		this.saxonProcessor = saxonProcessor;
	}

}
