/*
 * Created on Jul 26, 2006
 * 
 * (C) Copyright TANDBERG Television Ltd.
 */

package com.tandbergtv.watchpoint.communication.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


/**
 * Class to read the Message Handler Configuration from a fixed location.
 * 
 * @author Vijay Silva
 */
public final class MessageHandlerConfigReader
{
	private static final String MH_CONFIG_SCHEMA_FILE = "messagehandlerconfiguration.xsd";

	private static final String MH_CONFIG_NAMESPACE = "http://www.tandergtv.com/watchpoint/pmm/messagehandlerconfiguration";

	private static final String MESSAGE_HANDLER_CONFIGURATION_ELEMENT = "MessageHandlerConfiguration";

	private static final String MESSAGE_HANDLER_ELEMENT = "MessageHandler";

	private static final String MESSAGE_UID_ELEMENT = "MessageUID";

	private static final String MESSAGE_ASYNC_MODE_ELEMENT = "Async";

	private static final String MESSAGE_ASYNC_RESPONSE_UID_ATTR = "responseUID";

	private static final String CLASS_NAME_ELEMENT = "MessageHandlerClassName";

	private static final Logger logger = Logger.getLogger(MessageHandlerConfigReader.class);

	private static final DocumentBuilderFactory DOM_FACTORY = initializeDOMFactory();

	/**
	 * Default Constructor
	 */
	private MessageHandlerConfigReader()
	{
	}

	/*
	 * Method to parse the Message Handler Configuration and return the mappings between the Message
	 * UID and the Message Handler class names.
	 */
	static Map<String, MessageConfiguration> readConfiguration(InputStream configStream)
			throws MessageHandlerConfigurationException
	{
		Map<String, MessageConfiguration> configMap = new HashMap<String, MessageConfiguration>();

		try
		{
			DocumentBuilder domBuilder = DOM_FACTORY.newDocumentBuilder();
			Document configDoc = domBuilder.parse(configStream);

			Element messageConfigList = null;
			NodeList childNodes = configDoc.getElementsByTagNameNS(MH_CONFIG_NAMESPACE,
					MESSAGE_HANDLER_CONFIGURATION_ELEMENT);
			if (childNodes != null && childNodes.getLength() > 0)
				messageConfigList = (Element) childNodes.item(0);

			childNodes = messageConfigList.getElementsByTagName(MESSAGE_HANDLER_ELEMENT);
			for (int index = 0; index < childNodes.getLength(); index++)
			{
				Element childElement = (Element) childNodes.item(index);
				parseMessageHandler(childElement, configMap);
			}
		}
		catch (ParserConfigurationException ex)
		{
			String msg = "Failed to create the DocumentBuilder to parse the Message Handler Configuration File.";
			throw new MessageHandlerConfigurationException(msg, ex);
		}
		catch (SAXException ex)
		{
			String msg = "Failed to load the Message Handler Configuration File or Schema, the XML configuration is not valid.";
			throw new MessageHandlerConfigurationException(msg, ex);
		}
		catch (IOException ex)
		{
			String msg = "Failed to read the Message Handler Configuration File or Schema.";
			throw new MessageHandlerConfigurationException(msg, ex);
		}
		catch (Exception ex)
		{
			String msg = "Failed to parse the Message Handler Configuration File.";
			throw new MessageHandlerConfigurationException(msg, ex);
		}

		return configMap;
	}

	/*
	 * Method to parse a Single Message Handler Element
	 */
	private static void parseMessageHandler(Element handlerElement,
			Map<String, MessageConfiguration> configMap)
	{
		// Read the Message Handler configuration
		Element element = getChildElement(handlerElement, MESSAGE_UID_ELEMENT);
		String messageUID = element.getTextContent();
		if (messageUID != null)
			messageUID = messageUID.trim();

		// Read the Storage Capacity and Unit
		element = getChildElement(handlerElement, CLASS_NAME_ELEMENT);
		String className = element.getTextContent();
		if (className != null)
			className = className.trim();

		MessageConfiguration config = new MessageConfiguration(messageUID);
		config.setHandlerClassName(className);

		//Read the Response UID
		element = getChildElement(handlerElement, MESSAGE_ASYNC_MODE_ELEMENT);
		if (element != null)
		{
			String responseUID = element.getAttribute(MESSAGE_ASYNC_RESPONSE_UID_ATTR);
			config.setResponseUID(responseUID);
			config.setSynchronous(false);
		}
		configMap.put(messageUID, config);
	}

	/*
	 * Method to get a Child Element for an element given the name of the child. Assumes that the
	 * child element exists, and only considers the first child.
	 */
	private static Element getChildElement(Element element, String childElementName)
	{
		NodeList list = element.getElementsByTagName(childElementName);
		return (Element) list.item(0);
	}

	/*
	 * Method to create the Document Builder Factory
	 */
	private static DocumentBuilderFactory initializeDOMFactory()
	{
		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		domFactory.setNamespaceAware(true);

		try
		{
			SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			InputStream schemaStream = MessageHandlerConfigReader.class.getResourceAsStream(MH_CONFIG_SCHEMA_FILE);
			Source schemaFile = new StreamSource(schemaStream);
			Schema schema = schemaFactory.newSchema(schemaFile);
			domFactory.setSchema(schema);
		}
		catch (SAXException ex)
		{
			logger.warn("Failed to load the XML Schema for the Message Handler Configuration, "
					+ "will not validate the Message Handler Configuration XML document.", ex);
		}

		return domFactory;
	}
}
