package com.tandbergtv.workflow.adaptor.handler;


import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import javax.xml.XMLConstants;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.tandbergtv.workflow.adaptor.conf.IHandlerConfiguration;
import com.tandbergtv.workflow.message.HTTPAttachment;
import com.tandbergtv.workflow.message.HTTPMessage;
import com.tandbergtv.workflow.message.HTTPPayload;
import com.tandbergtv.workflow.message.IMessage;
import com.tandbergtv.workflow.message.util.WFSMessageConstants;
import com.tandbergtv.workflow.util.XMLDocumentUtility;

/**
 * For FMS Delete Files' message, converts the PathList parameter which contains a comma-separated list of paths
 * to a list of Path* parameters.
 * 
 * @author Raj Prakash
 * 
 */
public class DeleteFilesHandler extends AbstractHandler
{
	private static final Logger logger = Logger.getLogger(DeleteFilesHandler.class);

	private static final String SOAP_NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/";

	private static final String SOAP_ENVELOPE_TAG = "Envelope";

	private static final String SOAP_BODY_TAG = "Body";

	private static final String SOAP_PREFIX = "soapenv";

	private static final String SCHEMA_ATTR = XMLConstants.XML_NS_PREFIX + ":xsd";

	private static final String SCHEMA_INSTANCE_ATTR = XMLConstants.XML_NS_PREFIX + ":xsi";

	/**
	 * Default Constructor
	 */
	public DeleteFilesHandler()
	{
	}

	/**
	 * @param conf
	 *            The Handler Configuration
	 */
	public DeleteFilesHandler(IHandlerConfiguration conf)
	{
		super(conf);
	}

	/**
	 * @see com.tandbergtv.workflow.adaptor.handler.IHandler#process(com.tandbergtv.workflow.message.IMessage)
	 */
	public IMessage process(IMessage msg) throws HandlerException
	{
		logger.debug("Processing message: " + msg);

		String messageContent = msg.getPayload().getContent();
		try {
			messageContent = convertCommaSepPathsToListOfPath(messageContent);
		} catch(Exception e) {
			throw new HandlerException("Cannot convert PathList(comma-separated list) parameter to list of Path parameters", e);
		}
		
		/* adding soap header and a footer */
		String soapEnvelope = createSOAPEnvelope(messageContent);

		IMessage result = this.constructMessage(soapEnvelope, msg);
		logger.debug("Processed Message: " + result);

		return result;
	}

	/**
	 * @see com.tandbergtv.workflow.adaptor.handler.IHandler#postProcess(com.tandbergtv.workflow.message.IMessage)
	 */
	public IMessage postProcess(IMessage msg) throws HandlerException
	{
		logger.debug("Post processing message: " + msg);

		String response = this.removeSOAPEnvelope(msg.getPayload().getContent());
		
		IMessage result = this.constructMessage(response, msg);
		logger.debug("Post processed Response: " + msg);

		return result;
	}

	/*
	 * Converts the PathList parameter to Path* parameters
	 */
	private String convertCommaSepPathsToListOfPath(String content) throws Exception {
		logger.debug("Content: " + content);
		Document doc = XMLDocumentUtility.loadXml(content);
		XPath xpath = XPathFactory.newInstance().newXPath();
		Node parameterListNode = (Node) xpath.evaluate("//WFSMessage/MessageBody/ParameterList", doc, XPathConstants.NODE);
		if(parameterListNode != null) {
			Node pathListNode = (Node) xpath.evaluate("Parameter[@Name='PathList']", parameterListNode, XPathConstants.NODE);
			if(pathListNode != null) {
				String pathList = (String) xpath.evaluate("Value", pathListNode, XPathConstants.STRING);
				pathListNode.getParentNode().removeChild(pathListNode);
				int i=1;
				for(String path : convertCommaSepListToList(pathList)) {
					parameterListNode.appendChild(createWPCLParamElement(doc, "Path" + i++, path.trim()));	
				}
			}
		}
		
		return XMLDocumentUtility.convertToString(doc);
	}
	
	/*
	 * Creates a WPCL Parameter element from the given name and value
	 */
	private Element createWPCLParamElement(Document doc, String name, String value) {
		Element paramElement = doc.createElement(WFSMessageConstants.PARAMETER);
		paramElement.setAttribute(WFSMessageConstants.PARAMETER_DATA_TYPE, "String");
		paramElement.setAttribute(WFSMessageConstants.PARAMETER_NAME, name);
		Element valueElement = doc.createElement(WFSMessageConstants.PARAMETER_VALUE);
		valueElement.setTextContent(value);
		paramElement.appendChild(valueElement);
		return paramElement;
	}
	
	/*
	 * Converts a comma separated list to an array list of string tokens
	 */
	private List<String> convertCommaSepListToList(String str) {
		List<String> result = new ArrayList<String>();
		if(str != null) {
			StringTokenizer st = new StringTokenizer(str, ",");
			while(st.hasMoreTokens()) {
				result.add(st.nextToken());
			}
		}
		return result;
	}
	
	/*
	 * Constructs a HTTP Message given the original message, and the new message content
	 */
	private HTTPMessage constructMessage(String content, IMessage originalMessage)
	{
		HTTPPayload payload = new HTTPPayload(content);
		HTTPAttachment attachment = null;
		if(originalMessage.getAttachment() != null)
		{
			Object attachContent = originalMessage.getAttachment().getContent();
			String attachType = originalMessage.getAttachment().getContentType();
			attachment = new HTTPAttachment(attachContent, attachType);
		}

		return new HTTPMessage(payload, attachment);
	}
	
	/*
	 * Method to take the XML Document from the String and insert it into a SOAP Envelope
	 */
	private String createSOAPEnvelope(String xmlString) throws HandlerException
	{
		try
		{
			Document soapDocument = XMLDocumentUtility.loadXml(xmlString, true);
			Element documentElement = soapDocument.getDocumentElement();

			Element envelope = soapDocument.createElementNS(SOAP_NAMESPACE, SOAP_ENVELOPE_TAG);
			envelope.setPrefix(SOAP_PREFIX);
			envelope.setAttribute(SCHEMA_ATTR, XMLConstants.W3C_XML_SCHEMA_NS_URI);
			envelope.setAttribute(SCHEMA_INSTANCE_ATTR, XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);

			Element body = soapDocument.createElementNS(SOAP_NAMESPACE, SOAP_BODY_TAG);
			body.setPrefix(SOAP_PREFIX);
			body.appendChild(documentElement);
			envelope.appendChild(body);
			soapDocument.appendChild(envelope);

			return XMLDocumentUtility.convertToString(soapDocument);
		}
		catch (Exception ex)
		{
			String error = "Failed to create a SOAP Envelope around the XML message payload.";
			throw new HandlerException(error, ex);
		}
	}

	/*
	 * Method to take the XML Document from the String and remove the SOAP Envelope around it
	 */
	private String removeSOAPEnvelope(String xmlString) throws HandlerException
	{
		try
		{
			Document doc = XMLDocumentUtility.loadXml(xmlString, true);

			NodeList nodeList = doc.getElementsByTagNameNS(SOAP_NAMESPACE, SOAP_ENVELOPE_TAG);
			Element envelope = (Element) nodeList.item(0);

			nodeList = envelope.getElementsByTagNameNS(SOAP_NAMESPACE, SOAP_BODY_TAG);
			Element body = (Element) nodeList.item(0);

			nodeList = body.getChildNodes();
			doc.removeChild(envelope);

			for (int i = 0; i < nodeList.getLength(); i++)
			{
				Node node = nodeList.item(i);
				if (node.getNodeType() == Node.ELEMENT_NODE)
				{
					doc.appendChild(nodeList.item(i));
					break;
				}
			}

			return XMLDocumentUtility.convertToString(doc);
		}
		catch (Exception ex)
		{
			String error = "Failed to remove the SOAP Envelope around the XML message payload";
			throw new HandlerException(error, ex);
		}
	}
}
