package com.tandbergtv.metadatamanager.web;

import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.tandbergtv.metadatamanager.ITTVDataModelHandler;
import com.tandbergtv.metadatamanager.JPFActivator;
import com.tandbergtv.metadatamanager.MetadataManagerDAO;
import com.tandbergtv.metadatamanager.conf.SpecificationBuilder;
import com.tandbergtv.metadatamanager.consumer.Consumer;
import com.tandbergtv.metadatamanager.exception.MetadataException;
import com.tandbergtv.metadatamanager.exception.SearchException;
import com.tandbergtv.metadatamanager.exception.TranslationException;
import com.tandbergtv.metadatamanager.factoryImpl.SpecHandlerFactory;
import com.tandbergtv.metadatamanager.model.Asset;
import com.tandbergtv.metadatamanager.model.Field;
import com.tandbergtv.metadatamanager.model.FieldName;
import com.tandbergtv.metadatamanager.model.Group;
import com.tandbergtv.metadatamanager.model.RootAssetRevision;
import com.tandbergtv.metadatamanager.model.Group.GroupType;
import com.tandbergtv.metadatamanager.search.AssetSearchKey;
import com.tandbergtv.metadatamanager.search.AssetSearchService;
import com.tandbergtv.metadatamanager.search.FieldInfo;
import com.tandbergtv.metadatamanager.search.MetadataValueFieldInfo;
import com.tandbergtv.metadatamanager.search.SearchInfo;
import com.tandbergtv.metadatamanager.spec.ISpecHandler;
import com.tandbergtv.metadatamanager.specimpl.ttv.TTVId;
import com.tandbergtv.metadatamanager.util.XmlUtil;
import com.tandbergtv.workflow.driver.search.SearchOperator;
import com.tandbergtv.workflow.util.SearchCriteria;

public class MethodInvocationServlet extends HttpServlet {

	private static final long serialVersionUID = 446335046840681084L;
	private static final String JPF_PLUGIN_REPOSITORY_PROPERTY = "org.java.plugin.boot.pluginsRepositories";

	private static final String INSTALL_FOLDER_PROPERTY = "com.tandbergtv.cms.product.dir";

	@Override
	public void init() throws ServletException {
		super.init();
		ServletContext servletContext = this.getServletContext();
		WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(servletContext);
		initMDM(wac);
	}
	
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		
		ServletContext servletContext = this.getServletContext();
		WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(servletContext);

		String methodName = (String) request.getParameter("method");
		PrintWriter out = response.getWriter();

		Consumer c = new Consumer(wac);

		// invoke appropriate method
		if (methodName.equalsIgnoreCase("save")) {
			String filePath = (String) request.getParameter("filePath");
			String spec = (String) request.getParameter("spec");
			long id = c.saveDocument(filePath, spec);
			out.println("Saved asset with ttv id: " + id);
		} else if (methodName.equalsIgnoreCase("validate")) {
			String filePath = (String) request.getParameter("filePath");
			String spec = (String) request.getParameter("spec");
			String locale = (String) request.getParameter("locale");
			String result = c.validateDocument(filePath, spec, locale);
			out.println("Validation Results: ");
			out.println(result);
		} else if (methodName.equalsIgnoreCase("get")) {
			String id = (String) request.getParameter("id");
			String version = (String) request.getParameter("version");
			try {
				String assetDoc = "";
				if(version != null){
					assetDoc = c.get(Long.parseLong(id), version);
				}else{
					assetDoc = c.get(Long.parseLong(id));
				}
				out.println(assetDoc);
			} catch (NumberFormatException e) {
				out.println(e);
			} catch (MetadataException e) {
				out.println(e);
			} catch (SearchException e) {
				out.println(e);
				e.printStackTrace();
			} catch (TranslationException e) {
				out.println(e);
			}
		} else if (methodName.equalsIgnoreCase("delete")) {
			String id = (String) request.getParameter("id");
			boolean deletedAsset = c.delete(Long.parseLong(id));
			if (deletedAsset) {
				out.println("Succesfully deleted asset with TTV id: " + id);
			}
		} else if (methodName.equalsIgnoreCase("deleteAll")) {
			String ids = (String) request.getParameter("ids");
			String deletedAssetIds = c.deleteAssets(ids);
			out.println("Deleted assets with id: " + deletedAssetIds);
		} else if (methodName.equalsIgnoreCase("deleteAsset")) {
			String providerId = (String) request.getParameter("providerId");
			String assetId = (String) request.getParameter("assetId");
			String spec = (String) request.getParameter("spec");
			boolean deletedAsset = false;
			try {
				deletedAsset = c.deleteAsset(providerId, assetId, spec);
			} catch (SearchException e) {
				out.println(e);
			}
			if (deletedAsset) {
				out.println("Deleted asset with providerId=" + providerId
						+ " assetId=" + assetId + " in " + spec + " spec.");
			}
		} else if (methodName.equalsIgnoreCase("search")) {
			String providerId = (String) request.getParameter("providerId");
			String assetId = (String) request.getParameter("assetId");
			String spec = (String) request.getParameter("spec");

			try {
				String assetDoc = c.searchAsset(providerId, assetId, spec);
				out.println(assetDoc);
			} catch (MetadataException e) {
				out.println(e);
			} catch (SearchException e) {
				out.println(e);
			} catch (TranslationException e) {
				out.println(e);
			}
		} else if (methodName.equalsIgnoreCase("checkTransaction")) {
			MetadataManagerDAO daoImpl = (MetadataManagerDAO) wac
					.getBean("metadataManagerDAOImpl");

			PlatformTransactionManager txMgr = (PlatformTransactionManager) wac
					.getBean("transactionManager");
			txMgr.getTransaction(new DefaultTransactionDefinition(
					PROPAGATION_REQUIRED));

			Group g1 = new Group();
			try {
				g1.addField(createField("/tns:Fields/tns:AssetName",
						"CaptainCorellisMandolinpackage12432"));
				g1
						.addField(createField("/tns:Fields/tns:AssetClass",
								"package"));

				g1.setType(GroupType.PACKAGE);
				daoImpl.saveAsset(g1);

				Group g2 = new Group();
				g2.setType(GroupType.PACKAGE);
				g2.addField(createField("/tns:Fields/tns:AssetName", ""));
				g2.addField(createField("/tns:Fields/tns:AssetClass", "title"));
				g2.addField(createField("/tns:Fields/tns:Description/tns:Text",
						"package1 title asset"));
				daoImpl.saveAsset(g2);
				throw new RuntimeException("Simply throwing an exception.");
			} catch (MetadataException e) {
				out.println(e);
			}
		}else if (methodName.equalsIgnoreCase("getVersions")) {
			String id = (String) request.getParameter("id");
			try {
				List<RootAssetRevision> revisions = c.getVersions(Long.parseLong(id));
				for(RootAssetRevision revision: revisions){
					out.println("Version: "+ revision.getVersion());
				}
			} catch (Exception e) {
				out.println(e);
			} 
		}else if (methodName.equalsIgnoreCase("rollback")) {
			String id = (String) request.getParameter("id");
			String version = (String) request.getParameter("version");
			try {
				String assetDoc = c.rollback(Long.parseLong(id), version);
				out.print(assetDoc);
			} catch (Exception e) {
				out.println(e);
			} 
		} else if(methodName.equals("checkPMM")) {

			PlatformTransactionManager txMgr = (PlatformTransactionManager) wac
					.getBean("transactionManager");
			TransactionStatus status = txMgr.getTransaction(new DefaultTransactionDefinition(
					PROPAGATION_REQUIRED));
			
			
			String filePath = (String) request.getParameter("filePath");
			String spec = (String) request.getParameter("spec");

			ISpecHandler handler = SpecHandlerFactory.getInstance(spec);

			File file = new File(filePath);
			DocumentBuilder builder = null;
			Document input = null;
			try {
				builder = XmlUtil.createDocumentBuilder();
				builder.setEntityResolver(new ADIDTDEntityResolver());
				input = builder.parse(file);
				
				List<Asset> mergedAssets = handler.mergeWithoutSave(input);
				Asset mergedAsset = mergedAssets.get(0);
				
				com.tandbergtv.metadatamanager.model.File fileItem = c.createfile();
				
				mergedAsset.addChild(fileItem);
				
				
				ITTVDataModelHandler ttvhandler = (ITTVDataModelHandler) SpecHandlerFactory.getInstance("TTV");
				
				ttvhandler.saveAsset(mergedAsset, true);
				
			} catch (SAXException e1) {
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			} catch (ParserConfigurationException e2) {
				e2.printStackTrace();
			} catch (MetadataException e) {
				e.printStackTrace();
			}
			
			txMgr.commit(status);

		} else if(methodName.equals("addFile")) {

			AssetSearchService searchService = (AssetSearchService) wac.getBean("assetSearch");

			PlatformTransactionManager txMgr = (PlatformTransactionManager) wac
					.getBean("transactionManager");
			TransactionStatus status = txMgr.getTransaction(new DefaultTransactionDefinition(
					PROPAGATION_REQUIRED));

			SearchInfo info = buildSearchInfo("Mandolin.mpg");
			SearchCriteria criteria = searchService.getCriteria("asset", info, null,
					1);
			Collection<Asset> assets = searchService.search(criteria);		
			Asset a = assets.iterator().next();

			com.tandbergtv.metadatamanager.model.File fileItem = c.createfile();
			a.addChild(fileItem);
			
			ITTVDataModelHandler ttvhandler = (ITTVDataModelHandler) wac.getBean("TTVSpecHandler");
			
			try {
				ttvhandler.saveAsset(a.getRoot(), true);
			} catch (MetadataException e) {
				e.printStackTrace();
			}

			txMgr.commit(status);
		} else if(methodName.equals("checkGetVersion")) {
			String id = (String) request.getParameter("id");
			String version = (String) request.getParameter("version");
			
			PlatformTransactionManager txMgr = (PlatformTransactionManager) wac
			.getBean("transactionManager");
			DefaultTransactionDefinition def = new DefaultTransactionDefinition(PROPAGATION_REQUIRED);
			//def.setReadOnly(true);
			TransactionStatus status = txMgr.getTransaction(def);

	
			ITTVDataModelHandler ttvhandler = (ITTVDataModelHandler) wac.getBean("TTVSpecHandler");
			Asset asset = null;
			try {
			asset =	ttvhandler.getAsset(new TTVId(Long.parseLong(id)), version);
			} catch (SearchException e) {
				e.printStackTrace();
			}
		
			System.out.println("Asset Version == " + asset.getVersion());
			
			
			txMgr.commit(status);
		} else if(methodName.equals("saveDraft")) {

			PlatformTransactionManager txMgr = (PlatformTransactionManager) wac
					.getBean("transactionManager");
			TransactionStatus status = txMgr.getTransaction(new DefaultTransactionDefinition(
					PROPAGATION_REQUIRED));
			
			
			String filePath = (String) request.getParameter("filePath");
			String spec = (String) request.getParameter("spec");

			ISpecHandler handler = SpecHandlerFactory.getInstance(spec);

			File file = new File(filePath);
			DocumentBuilder builder = null;
			Document input = null;
			try {
				builder = XmlUtil.createDocumentBuilder();
				builder.setEntityResolver(new ADIDTDEntityResolver());
				input = builder.parse(file);
				
				List<Asset> mergedAssets = handler.mergeWithoutSave(input);
				Asset mergedAsset = mergedAssets.get(0);
				
				ITTVDataModelHandler ttvhandler = (ITTVDataModelHandler) SpecHandlerFactory.getInstance("TTV");
				
				Asset a = ttvhandler.saveAsset(mergedAsset, true);
				
				out.println("Saved (draft) asset with ttv id: " + a.getTTVId().getId());
			} catch (SAXException e1) {
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			} catch (ParserConfigurationException e2) {
				e2.printStackTrace();
			} catch (MetadataException e) {
				e.printStackTrace();
			}
			
			txMgr.commit(status);

		}
	}

	/**
	 * @param wac 
	 * 
	 */
	private void initMDM(WebApplicationContext wac) {
		Properties properties = new Properties();
		
		String installFolder =  getInstallDirectory();
		
		File pluginFolder = new File(installFolder, "plugins");
			
		properties.put(JPF_PLUGIN_REPOSITORY_PROPERTY, pluginFolder
				.getAbsolutePath());
		JPFActivator activator = new JPFActivator();
		activator.start(properties);
		SpecificationBuilder builder = new SpecificationBuilder(activator.getPluginManager(), wac);
		builder.buildSpecifications();
	}

	public String getInstallDirectory() {
		return System.getProperty(INSTALL_FOLDER_PROPERTY);
	}
	
	private Field createField(String xpath, String value) {
		Field f = new Field();
		f.setTtvXPath(xpath);
		f.setValue(value);

		String[] a = xpath.split("/");
		ArrayList<Integer> indices = new ArrayList<Integer>(a.length);
		for (int i = 0; i < a.length; i++) {
			indices.add(new Integer(1));
		}

		f.setIndices(indices);

		return f;
	}
	
	public class ADIDTDEntityResolver implements EntityResolver {
		public InputSource resolveEntity(String publicId, String systemId) throws SAXException, java.io.IOException {
			InputStream s = this.getClass().getClassLoader().getResourceAsStream("ADI.DTD");
			return new InputSource(s);
		}
	}

	private SearchInfo buildSearchInfo(String filePath) {
		SearchInfo info = new SearchInfo();		
		info.setProperty(AssetSearchKey.FIELDS.toString());
		FieldInfo searchFields = new MetadataValueFieldInfo(
				FieldName.URL.toString(),
				SearchOperator.EQUAL, filePath);
		info.setFields(searchFields);
		return info;		
	}
}
