package com.tandbergtv.watchpoint.communication;

import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;

import com.tandbergtv.watchpoint.communication.config.MessageHandlerConfigurationException;
import com.tandbergtv.watchpoint.communication.config.MessageHandlerFactory;
import com.tandbergtv.watchpoint.pmm.communication.HandlerErrorCode;
import com.tandbergtv.watchpoint.pmm.communication.MessageHandler;
import com.tandbergtv.watchpoint.pmm.communication.MessageHandlerException;
import com.tandbergtv.watchpoint.pmm.communication.MessageParameters;
import com.tandbergtv.watchpoint.pmm.util.XMLDocumentUtility;
import com.tandbergtv.workflow.message.IMessage;
import com.tandbergtv.workflow.message.IMessageKey;
import com.tandbergtv.workflow.message.IMessageUID;
import com.tandbergtv.workflow.message.MessageImpl;
import com.tandbergtv.workflow.message.MessageUIDImpl;
import com.tandbergtv.workflow.message.StringPayload;
import com.tandbergtv.workflow.message.WorkflowMessage;
import com.tandbergtv.workflow.message.WorkflowMessage.MessageType;
import com.tandbergtv.workflow.message.util.Marshaller;
import com.tandbergtv.workflow.message.util.Unmarshaller;

/**
 * Implementation of PMM communication interface.
 * 
 * @author Raj Prakash
 */
public abstract class MessageService implements IMessageService {
	private static final Logger logger = Logger.getLogger(MessageService.class);
	private static final String UNKNOWN_UID = "Unknown UID";

	protected String configurationFileName;
		
	protected MessageService(String configurationFileName) {
		this.configurationFileName = configurationFileName;
	}
	
	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.ServiceLifecycle#start()
	 */
	public void start() {}

	/* (non-Javadoc)
	 * @see com.tandbergtv.workflow.core.service.ServiceLifecycle#stop()
	 */
	public void stop() {}

	/* (non-Javadoc)
	 * @see com.tandbergtv.watchpoint.pmm.communication.IMessageService#receive(com.tandbergtv.workflow.message.IMessage)
	 */
	public IMessage receive(IMessage message) {
		WorkflowMessage wfMessage = null;
		WorkflowMessage result = null;
		String messageUID = null;
		IMessage responseMessage = null;
		
		try {
			try {
				//unmarshal the request message to workflow message
				wfMessage = getWorkflowMessage(message);

				//get the message uid
				messageUID = wfMessage.getMessageUID().getUID();
				logger.info("Received valid Workflow Message with UID: " + messageUID);

				//get the message handler for this uid
				InputStream inStream = getClass().getClassLoader().getResourceAsStream(configurationFileName);
				MessageHandlerFactory factory = new MessageHandlerFactory(inStream);
				MessageHandler handler = factory.getMessageHandler(messageUID);

				//handle the message
				result = handler.handleMessage(wfMessage);
			}
			catch (MessageHandlerConfigurationException mhce) {
				logger.error("Failed to load the Message Handler for the UID: " + messageUID, mhce);
				result = generateNackMessage(wfMessage, mhce);
			}
			catch(Exception e) {
				logger.error("Exception while processing the message", e);
				result = generateNackMessage(wfMessage, e);
			}

			//marshal response workflow message to IMessage object
			try {
				responseMessage = getIMessage(result);
			} catch (Exception e) {
				logger.error("Exception while marshalling the response workflow message", e);
			}
			
		} catch(Exception e) {
			logger.error("Unexpected error when processing the message.", e);
		}
		
		return responseMessage;
	}

	/**
	 * Unmarshal the given message to Workflow Message.
	 */
	protected WorkflowMessage getWorkflowMessage(IMessage message) throws Exception {
		Document doc = XMLDocumentUtility.loadXml(message.getPayload().getContent());
		return Unmarshaller.unmarshal(doc);
	}
	
	/**
	 * Marshal the given Workflow Message to IMessage.
	 * @throws Exception 
	 */
	protected IMessage getIMessage(WorkflowMessage message) throws Exception {
		Document document = Marshaller.newMarshaller().marshal(message);
		String documentText = XMLDocumentUtility.convertToString(document);
		return new MessageImpl(new StringPayload(documentText));
	}
	
	/*
	 * Method to generate a default NACK response
	 */
	protected WorkflowMessage generateNackMessage(WorkflowMessage message, Throwable ex) {
		IMessageUID uid = (message != null) ? message.getMessageUID() : new MessageUIDImpl(
				UNKNOWN_UID);
		IMessageKey key = (message != null) ? message.getKey() : null;
		WorkflowMessage response = new WorkflowMessage(uid, key, MessageType.nack);

		String errorCode = HandlerErrorCode.RUNTIME_ERROR;
		if (ex instanceof MessageHandlerException) {
			String code = ((MessageHandlerException) ex).getErrorCode();
			if (code != null)
				errorCode = code;
		}
		
		/* Set payload parameters */
		response.putValue(MessageParameters.ERROR_CODE, errorCode);
		response.putValue(MessageParameters.ERROR_MESSAGE, ex.getMessage());

		StringWriter writer = new StringWriter();
		ex.printStackTrace(new PrintWriter(writer));
		response.putValue(MessageParameters.ERROR_STACK, writer.toString());

		return response;
	}

}
