package com.tandbergtv.cms.action.processing;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Calendar;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.jbpm.graph.exe.ExecutionContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.tandbergtv.watchpoint.cms.action.SetTokenIdAction;

public class AddPosterOrPreviewToMetadata {
	
	private static final String XPATH_ADI_ASSET_NODE = "/ADI/Asset";
	private static final String XPATH_PACKAGE_PROVIDER = "/ADI/Metadata/AMS/@Provider";
	private static final String XPATH_PACKAGE_PRODUCT = "/ADI/Metadata/AMS/@Product";
	
	private static final String POSTER = "poster";
	private static final String PREVIEW = "preview";
	private static final String ASSET_ID = "assetId";
	private static final String APP = "App";
	private static final String NAME = "Name";
	private static final String VALUE = "Value";
	private static final String CONTENT_CHECKSUM = "Content_CheckSum";
	private static final String CONTENT_FILESIZE= "Content_FileSize";
	
	private static final String ADI_XML_FILE_PATH = "outputMetadataFile";
	private static final String POSTER_FILE_PATH = "posterFilePath";
	private static final String PREVIEW_FILE_PATH = "previewFilePath";
	
	private static final Logger logger = Logger.getLogger(SetTokenIdAction.class);
	
	private String packageProvider = null;
	private String packageProduct = null;
	private String ppFilePath = null;
	private Document doc = null;
	private String checksum = null;
	private String fileSize = null;
	
	
	public void addPoster(ExecutionContext context) throws Exception {
		add(POSTER, 
				(String) context.getVariable(POSTER_FILE_PATH),
				(String) context.getVariable("posterChecksum"),
				(String) context.getVariable("posterFileSize"), 
				context);
	}
	
	public void addPreview(ExecutionContext context) throws Exception {
		add(PREVIEW, 
				(String) context.getVariable(PREVIEW_FILE_PATH),
				(String) context.getVariable("previewChecksum"),
				(String) context.getVariable("previewFileSize"), 
				context);
	}
	
	private void add(String posterOrPreview, 
			String ppFilePath, 
			String checksum, 
			String fileSize, 
			ExecutionContext context)throws Exception {

				//TODO add more tests for null and log messages
		
		try{
			
		logger.debug("Add_PP_Test: versaion 1003: " ); //debug
			
		//TODO Put this inside a method.
		/*Opens the ADI.XML file and parses it into a DOM Document.*/
			DocumentBuilderFactory docFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			docBuilder.setEntityResolver(null);  //TODO change to custom resolver
			Document doc = docBuilder.parse(new File((String) context.getVariable(ADI_XML_FILE_PATH)));
		
		/* Give this ubiquitous variable object-level visibility to simplify method signatures. */
		this.doc = doc;
		
		/* Gives these variables object-level visibility to simplify method signatures. */
		this.ppFilePath = ppFilePath;
		this.checksum  = checksum;
		this.fileSize = fileSize;

		/*
		 * Use xpath to get the main ADI/Asset node under which the preview and poster assets
		 * will be added.
		 */
		XPath xPath = XPathFactory.newInstance().newXPath();
		Node adiAsset = (Node) xPath.evaluate(XPATH_ADI_ASSET_NODE, doc,
				XPathConstants.NODE);
		
		//TODO Put these inside a method and catch and log the exception
		/* Run some XPath expressions now and use the results later. */
		packageProvider = (String) xPath.evaluate(XPATH_PACKAGE_PROVIDER, doc,
				XPathConstants.STRING);
		
		packageProduct = (String) xPath.evaluate(XPATH_PACKAGE_PRODUCT, doc,
				XPathConstants.STRING);

		//logger.debug("Add_PP_Test: node name: " + adiNode.getNodeName()); // debug

		/*
		 * Use the DOM interfaces to add a new preview and poster asset nodes and then add data.
		 */
		
		Element newAMS = createAMSElement(doc, adiAsset, posterOrPreview);
		createContentElement(newAMS.getParentNode().getParentNode()); //Creating Content element as a child of the Asset element.
		createApp_DataElements(newAMS.getParentNode(), posterOrPreview); //Creating App_Data elements as a child of the Metadata element.
		
		writeDOMtoXmlFile(doc, (String)context.getVariable(ADI_XML_FILE_PATH));
		
		}catch(Exception e){
			logger.debug("Add_PP_Test: exception thrown in method add(): " + e.getMessage());
			throw(e);
		}	
	}
	
	/* Creates an empty Metadata xml element which will be populated with data later. */
	private Element createAMSElement(Document doc, Node parentNode, 
			String posterOrPreivew){
		Node newAsset = doc.createElement("Asset");
		parentNode.appendChild(newAsset);
		Node newMetadata = doc.createElement("Metadata");
		newAsset.appendChild(newMetadata);
		Element newAMS = doc.createElement("AMS");
		newMetadata.appendChild(newAMS);
		setAMSAttributes(newAMS, posterOrPreivew);
		
		return newAMS;
	}
	
	private void setAMSAttributes(Node ams, String posterOrPreivew) {
		((Element)ams).setAttribute("Asset_Class", posterOrPreivew);
		((Element)ams).setAttribute("Asset_ID", generateAssetID(packageProvider, posterOrPreivew));
		((Element)ams).setAttribute("Product", packageProduct);
		((Element)ams).setAttribute("Provider", packageProvider);
	}
	
	private void createContentElement(Node parentNode){
		Node newContent = doc.createElement("Content");
		parentNode.appendChild(newContent);
		((Element)newContent).setAttribute(VALUE, getFileName(this.ppFilePath));
	}
	
	private String getFileName(String filePath){
		String [] tokens = filePath.split("/");
		if (tokens != null && tokens.length > 0)
			return tokens[tokens.length - 1];
		//TODO throw unchecked exception
		return "";
	}
	
	private void createApp_DataElements(Node parentNode, String posterOrPreview){
		createApp_DataElement(parentNode, this.packageProduct, "Type", posterOrPreview);
		createApp_DataElement(parentNode, this.packageProduct, CONTENT_FILESIZE, this.fileSize);
		createApp_DataElement(parentNode, this.packageProduct, CONTENT_CHECKSUM, this.checksum);
	}
	

	private void createApp_DataElement(Node parentNode, String appAttr, String nameAttr, String valueAttr) {
		Node newApp_Data = doc.createElement("App_Data");
		parentNode.appendChild(newApp_Data);  //Writes new nodes at end of App_Data elements.
		((Element)newApp_Data).setAttribute(APP, appAttr);
		((Element)newApp_Data).setAttribute(NAME, nameAttr);
		((Element)newApp_Data).setAttribute(VALUE, valueAttr);
	}
	
	/* Generates unique asset IDs for posters and previews. */
	private String generateAssetID(String base, String posterOrPreview){
	    Calendar cal = Calendar.getInstance();
	    String timeInMills = String.valueOf(cal.getTimeInMillis());
		String assetID = base + timeInMills;
		if (posterOrPreview == POSTER)
			assetID += "1"; else
				assetID += "2";
		return assetID;
	}
	
	
	/*
	 * Use XSLT to transform the updated DOM tree to a markup style of
	 * representation and then stream that to an XML file.
	 */
	private void writeDOMtoXmlFile(Document doc, String outputFilePath) throws Exception{
		Transformer transformer = TransformerFactory.newInstance().newTransformer();
		DOMSource domSource = new DOMSource(doc);
		StreamResult markupResult = new StreamResult(new FileOutputStream(outputFilePath));
		transformer.transform(domSource, markupResult);
	}
}
