package com.ttv.acs.util;

import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.tandbergtv.workflow.comm.HTTPDevice;
import com.tandbergtv.workflow.message.MessageUIDImpl;
import com.tandbergtv.workflow.message.WorkflowMessage;
import com.tandbergtv.workflow.message.WorkflowMessage.MessageType;
import com.tandbergtv.workflow.message.WorkflowPayload;
import com.tandbergtv.workflow.message.util.MarshalException;
import com.tandbergtv.workflow.message.util.Marshaller;
import com.tandbergtv.workflow.message.util.Unmarshaller;
import com.ttv.acs.stub.adi.AbstractWorkOrderRequest;

public class WorkflowAdaptor {

	private Logger logger = Logger.getLogger(WorkflowAdaptor.class);

	/**
	 * 
	 * @param workOrderRequest
	 * @return true if notifying process succeed, false otherwise
	 */
	public boolean notifyWorkFlow(AbstractWorkOrderRequest workOrderRequest) {
		boolean notifyResult = true;
		SOAPConnection connection = null;
		try {
			Document notificationReqDoc = prepareIngestMessage(workOrderRequest);
			String destinationUrl = getDestinationURL();

			logger.debug("Making SOAP call to " + destinationUrl);

			SOAPMessage message = javax.xml.soap.MessageFactory.newInstance().createMessage();
			MimeHeaders headers = message.getMimeHeaders();
			headers.addHeader("SOAPAction", "");

			SOAPBody requestBody = message.getSOAPBody();
			requestBody.addDocument(notificationReqDoc);

			connection = SOAPConnectionFactory.newInstance().createConnection();

			SOAPMessage reply = connection.call(message, destinationUrl);
			notifyResult = parseWorkflowResponse(reply);

		} catch (Exception e) {
			notifyResult = false;
			logger.error("Error sending message - " + e.getMessage(), e);
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (SOAPException e) {
					logger.error("Failed to close SOAPConnection - " + e.getMessage(), e);
				}
			}
		}

		return notifyResult;

	}

	private boolean parseWorkflowResponse(SOAPMessage reply) {
		boolean reqResult = true;
		SOAPBody body;
		try {
			body = reply.getSOAPBody();
			if (body.getFault() != null) {
				SOAPFault fault = body.getFault();
				throw new SOAPException("Got a soap fault in the response: " + fault.getFaultString());
			}
			
			Node wfsMessage = body.getFirstChild();
			System.out.println(wfsMessage);

			WorkflowMessage response = Unmarshaller.unmarshal(convertNodeToNewDocument(wfsMessage));
			logger.debug("Workflow response:" + response.getType());
			if(MessageType.nack.equals(response.getType())){
				reqResult = false;
			}
		} catch (Exception e) {
			reqResult = false;
			logger.error("Failed to parse SOAP response from WorkFlow", e);
		}

		return reqResult;
	}

	private Document convertNodeToNewDocument(Node wfsMessage) throws TransformerException {
		TransformerFactory tf = TransformerFactory.newInstance();
		Transformer xf = tf.newTransformer();
		DOMResult dr = new DOMResult();
		xf.transform(new DOMSource(wfsMessage), dr);
		Document newDoc = (Document) dr.getNode();

		return newDoc;
	}

	private Document prepareIngestMessage(AbstractWorkOrderRequest workOrderRequest) {
		WorkflowMessage notificationReq = new WorkflowMessage(new MessageUIDImpl(workOrderRequest.getMessageUID()));
		notificationReq.setType(MessageType.notification);
		WorkflowPayload payload = (WorkflowPayload) notificationReq.getPayload();

		populatePayload(payload, workOrderRequest);

		notificationReq.setSource(new HTTPDevice(ADIUtil.getNameServerAddress()));

		try {
			return Marshaller.newMarshaller().marshal(notificationReq);
		} catch (MarshalException e) {
			logger.error("Failed to marshal notificationReq obj: " + notificationReq + ". Error: " + e.getMessage());
			throw new RuntimeException(e);
		}

	}

	private String getDestinationURL() {
		try {
			return ApplicationProperties.getInstance().getProperty(PConstants.WORKFLOW_WEBSERVICE_UTL, PConstants.WORKFLOW_WEBSERVICE_UTL_DEFAULT);
		} catch (Exception e) {
			logger.error("Failed to get property " + PConstants.WORKFLOW_WEBSERVICE_UTL, e);
			throw new RuntimeException(e);
		}
	}

	private void populatePayload(WorkflowPayload payload, AbstractWorkOrderRequest workOrderRequest) {
		workOrderRequest.populatePayload(payload);		
	}
}
