/**
 * ADI11Parser.java
 * Created Feb 6, 2007
 * Copyright (C) Tandberg Television 2007
 */
package com.tandbergtv.workflow.util.adi;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * ADI 1.1 parser utility
 * 
 * @author Sahil Verma
 */
public class ADI11Parser {

	private Document adi;
	
	private static final String ASSET_CLASS_XPATH = "//Asset/Asset/Metadata/AMS";
	
	private static final String TITLE_BRIEF_XPATH = "//App_Data[@Name='Title_Brief']";
	
	private static final String RUN_TIME_XPATH = "//App_Data[@Name='Run_Time']";
		
	private static final String APP_DATA_VALUE = "Value";
	
	private static final String PACKAGE_XPATH = "//ADI/Metadata/AMS";
	
	private static final String PROVIDER_ID = "Provider_ID";
	
	private static final String PROVIDER = "Provider";
	
	private static final String ASSET_ID = "Asset_ID";
	
	private static final String ASSET_CLASS = "Asset_Class";
	
	private static final String ADI_XSD = "com/tandbergtv/workflow/util/adi/adi.xsd";
	
	private static final String PKG_DESC_XPATH = "//AMS[@Asset_Class='package']/@Description";
	
	private static final Logger logger = Logger.getLogger(ADI11Parser.class);
	
	/**
	 * Creates an ADI11Parser
	 */
	private ADI11Parser() {
		super();
	}
	
	/**
	 * Returns an instance of the ADI parser
	 * @return
	 */
	public static ADI11Parser newInstance() {
		return new ADI11Parser();
	}
	
	/**
	 * Reads an ADI metadata file from the specified path
	 * 
	 * @param path
	 */
	public void load(String path) {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			Schema schema = loadSchema();
			
			factory.setSchema(schema);
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			docBuilder.setEntityResolver(new ADIDTDEntityResolver());
			this.adi = docBuilder.parse(new File(path));
			
		} catch (Exception e) {
			throw new RuntimeException("Failed to read ADI file " + path, e);
		}
	}
	
	/**
	 * Reads an ADI metadata file from the specified input stream
	 * 
	 * @param path
	 */
	public void load(InputStream in) {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			Schema schema = loadSchema();
			
			factory.setSchema(schema);
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			docBuilder.setEntityResolver(new ADIDTDEntityResolver());
			this.adi = docBuilder.parse(in);
			
		} catch (Exception e) {
			throw new RuntimeException("Failed to read the input stream for ADI file", e);
		}
	}

	/**
	 * Returns the list of Asset objects, mentioned in adi
	 * 
	 * 
	 */
	public List<Asset> extractAssets(){
		List<Asset> assets = new ArrayList<Asset>();
		try {
			XPath xpath = XPathFactory.newInstance().newXPath();
			NodeList nodes = (NodeList)xpath.evaluate(ASSET_CLASS_XPATH, adi, XPathConstants.NODESET);
			
			for (int i = 0; i < nodes.getLength(); i++){
				Asset asset = new Asset();
				asset.setContentType(nodes.item(i).getAttributes().getNamedItem(ASSET_CLASS).getTextContent());
				asset.setAssetId(nodes.item(i).getAttributes().getNamedItem(ASSET_ID).getTextContent());
				assets.add(asset);
			}
		} catch (Exception e) {
			logger.error("Failed to load the set of assets from adi.xml", e);
		}
		return assets;
	}
	
	/**
	 * Returns the brief title of the package
	 * @return
	 */
	public String getBriefTitle() {
		return getAttributeValue(TITLE_BRIEF_XPATH, APP_DATA_VALUE);
	}
	
	/**
	 * Returns the Description of the package
	 * @return the package description
	 */
	public String getPackageDescription() {
		return evaluate(PKG_DESC_XPATH);
	}
	
	/**
	 * Returns the title of the package
	 * @return
	 */
	public String getTitle() {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Convenience method to return the id of the package
	 * @return
	 */
	public String getPackageAssetID() {
		return getAttributeValue (PACKAGE_XPATH, ASSET_ID);
	}
	
	/**
	 * Returns the id of the specified asset
	 * @param asset
	 * @return
	 */
	public String getAssetID(String asset) {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Returns the asset class e.g. preview, movie etc
	 * 
	 * @param asset
	 * @return
	 */
	public String getAssetType(String asset) {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Returns the providerID
	 * 
	 * @param asset
	 * @return
	 */
	public String getProviderID() {
		return getAttributeValue (PACKAGE_XPATH, PROVIDER_ID);
	}
	
	public String getProvider(){
		return getAttributeValue (PACKAGE_XPATH, PROVIDER);
	}
	
	public String getRunTime(){
		return getAttributeValue(RUN_TIME_XPATH, APP_DATA_VALUE);
	}
	
	public NodeList getNodes(String xpathStr) throws XPathExpressionException {
		XPath xpath = XPathFactory.newInstance().newXPath();
		NodeList nodes = (NodeList)xpath.evaluate(xpathStr, adi, XPathConstants.NODESET);
		return nodes;
	}
	
	private String getAttributeValue (String xpath, String attribute){
		try {
			Node node = (Node) XPathFactory.newInstance().newXPath().evaluate(xpath, adi, XPathConstants.NODE);

			if (node != null)
				return node.getAttributes().getNamedItem(attribute).getTextContent();
		} catch (XPathExpressionException e) {
			throw new RuntimeException();
		}
		
		return null;
	}
	
	private String evaluate(String xpath) {
		try {
			return (String) XPathFactory.newInstance().newXPath().evaluate(xpath, adi, XPathConstants.STRING);
		} catch (XPathExpressionException e) {
			throw new RuntimeException();
		}
	}
	
	private Schema loadSchema() throws Exception {
		InputStream stream = this.getClass().getClassLoader().getResourceAsStream(ADI_XSD);
		return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new StreamSource(stream));
	}
	
	class ADIDTDEntityResolver implements EntityResolver {
		private static final String DTD_FILE_PATH = "com/tandbergtv/workflow/util/adi/ADI.DTD";

		public InputSource resolveEntity(String publicId, String systemId) throws SAXException, java.io.IOException {
		
			InputStream s = this.getClass().getClassLoader().getResourceAsStream(DTD_FILE_PATH);
			return new InputSource(s);
		}
	}
}
