package com.zi.mwms.solution.workflow;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;

import javax.annotation.PreDestroy;
import javax.xml.namespace.QName;

import com.cim.idm.framework.util.file.FileListenerEvent;
import com.cim.idm.framework.util.file.FileUtil;
import com.cim.idm.framework.util.file.FileWatcher;
import com.cim.idm.framework.util.resource.ResourceUtil;
import com.cim.idm.framework.fos.idmflow.BpelExRepository;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.logging.log4j.core.util.OptionConverter;
import org.w3c.dom.NamedNodeMap;

public class CustomBpelExRepository extends BpelExRepository {

	private static Log					log					= LogFactory.getLog(CustomBpelExRepository.class);
	/**
	 * @uml.property  name="useBpelsInInnerDirs"
	 */
	private boolean						useBpelsInInnerDirs	= false;
	/**
	 * @uml.property  name="bpelMapList"
	 * @uml.associationEnd  qualifier="path:java.lang.String java.lang.Long"
	 */
	private Map<String, Long>			bpelMapList			= new HashMap<String, Long>();
	/**
	 * @uml.property  name="bpelRootPath"
	 */
	private String						bpelRootPath		= "";
	/**
	 * @uml.property  name="bpelRootPathList"
	 */
	private List<String>				bpelRootPathList	= null;
	/**
	 * @uml.property  name="rootBpelName"
	 */
	private String						rootBpelName		= "dispatching.bpel";

	
	private String						bpelJarEntryPath	= "/bpels";

	/**
	 * @uml.property  name="fileWatcherList"
	 * @uml.associationEnd  qualifier="watchDir:java.lang.String com.cim.idm.framework.util.file.FileWatcher"
	 */
	private Map<String, FileWatcher>	FileWatcherList		= new HashMap<String, FileWatcher>();


	public CustomBpelExRepository()
	{
	}

	/**
	 * @return
	 * @uml.property  name="bpelRootPath"
	 */
	public String getBpelRootPath()
	{
		return bpelRootPath;
	}

	
	public void setBpelJarEntryPath(String bpelJarEntryPath) {
		this.bpelJarEntryPath = bpelJarEntryPath;
	}

	/**
	 * @param bpelRootPath
	 * @uml.property  name="bpelRootPath"
	 */
	public void setBpelRootPath(String bpelRootPath)
	{
		this.bpelRootPath = OptionConverter.substVars(bpelRootPath, null);
	}

	/**
	 * @return
	 * @uml.property  name="bpelRootPathList"
	 */
	public List getBpelRootPathList()
	{
		return bpelRootPathList;
	}

	/**
	 * @param bpelRootPathList
	 * @uml.property  name="bpelRootPathList"
	 */
	public void setBpelRootPathList(List bpelRootPathList)
	{
		String[] bpelDir = new String[bpelRootPathList.size()];
		for (int i = 0; i < bpelRootPathList.size(); i++)
		{
			bpelDir[i] = OptionConverter.substVars(bpelRootPathList.get(i).toString(), null);
		}
		bpelRootPathList.clear();

		for (int i = 0; i < bpelDir.length; i++)
		{
			bpelRootPathList.add(bpelDir[i]);
		}
		this.bpelRootPathList = bpelRootPathList;
	}

	/**
	 * @return
	 * @uml.property  name="rootBpelName"
	 */
	public String getRootBpelName()
	{
		return rootBpelName;
	}

	/**
	 * @param rootBpelName
	 * @uml.property  name="rootBpelName"
	 */
	public void setRootBpelName(String rootBpelName)
	{
		this.rootBpelName = rootBpelName;
	}

	public int getWFThreadSize()
	{
		return this.getBpelProcessManager().getWFThreadSize();
	}

	/**
	 * @return
	 * @uml.property  name="useBpelsInInnerDirs"
	 */
	public boolean isUseBpelsInInnerDirs()
	{
		return useBpelsInInnerDirs;
	}

	/**
	 * @param useBpelsInInnerDirs
	 * @uml.property  name="useBpelsInInnerDirs"
	 */
	public void setUseBpelsInInnerDirs(boolean useBpelsInInnerDirs)
	{
		this.useBpelsInInnerDirs = useBpelsInInnerDirs;
	}

	public void init() throws Exception
	{
		try
		{
			read();
		} catch (Exception e)
		{
			log.error(e, e);
		}
	}

	private void readBpels(String aBpelRootPath, List bpelAllList)
	{

		try
		{
			List bpelList = FileUtil.getFileList(new File(aBpelRootPath), ".BPEL", this.useBpelsInInnerDirs);
			log.info("Read BPEL(" + bpelList.size() + ") Files from [" + aBpelRootPath + "]");
			bpelAllList = ListUtils.sum(bpelList, bpelAllList);
		} catch (Exception e)
		{
			log.error(e);
		}



		if (bpelAllList == null || bpelAllList.size() == 0)
		{
			log.warn("Could not find application service files [" + aBpelRootPath + "]");
		}

		try
		{
			for (int i = 0; i < bpelAllList.size(); i++)
			{
				String path = ((File) bpelAllList.get(i)).getAbsolutePath();
				this.addBpelProcess(path);
			}
		} catch (Exception e)
		{
			log.error(e, e);
		}
		bpelAllList.clear();

	}
	
	private void readJarBpels( JarFile jar , String aBpelJarEntryPath, List bpelAllList ) {
//		Bundle bundle = null;
		try
		{
//			bundle = ResourceUtil.getBundle(bundleContext.getBundles(), (String) bundleSymbolicNameList.get(i));
			List<org.w3c.dom.Document> docList = ResourceUtil.getBundleBpels(jar, aBpelJarEntryPath, bpelAllList, new ArrayList<String>() );
			if (docList.size() > 0)
			{
				for (int j = 0; j < docList.size(); j++)
				{
					this.addBpelProcess(docList.get(j));
				}
			}
			else
			{
				log.warn("Could not find application service files [" +jar.getName()+":" + aBpelJarEntryPath +  "]");
			}
		}catch(Exception e) {
			log.error(e);
		}
		
		
	}

	public void read() throws RuntimeException
	{
		List bpelAllList = new ArrayList();

		if (this.bpelRootPathList == null)
		{
			JarFile jar = null;
			if(bpelRootPath.endsWith(".jar")) {
				try {
					jar = new JarFile(bpelRootPath);
					readJarBpels(jar, bpelJarEntryPath, bpelAllList);
				}catch(IOException e) {
					log.error("The JAR File:" + bpelRootPath + " Not found, when detect BPEL Files");
					log.error(e);
				}
			}else {
				readBpels(this.bpelRootPath, bpelAllList);
				startWatcher(this.bpelRootPath);
			}
			
		}
		else
		{
			for (int i = 0; i < bpelRootPathList.size(); i++)
			{
				JarFile jar = null;
				if(this.bpelRootPathList.get(i).endsWith(".jar")) {
					try {
						jar = new JarFile(this.bpelRootPathList.get(i));
						readJarBpels(jar, bpelJarEntryPath, bpelAllList);
					}catch(IOException e) {
						log.error("The JAR File:" + this.bpelRootPathList.get(i) + " Not found, when detect BPEL Files");
						log.error(e);
					}
				}else {
					readBpels(this.bpelRootPathList.get(i), bpelAllList);
					startWatcher(this.bpelRootPathList.get(i));
				}
			}
		}

	}

	private synchronized void removeBpelProcess(String bpelfile) throws Exception
	{
		File file = new File(bpelfile);
		String path = file.getAbsolutePath();
		bpelMapList.remove(path);
		QName qname = new QName(DEFAULT_TARGETNAMESPACE, file.getName());
		this.removeBpelProcess(qname);
	}

	private synchronized void addBpelProcess(String bpelfile) throws Exception
	{
		File file = new File(bpelfile);
		String path = file.getAbsolutePath();

		if (bpelMapList.containsKey(path))
		{
			if (file.lastModified() > bpelMapList.get(path).longValue())
			{
				bpelMapList.remove(path);
			}
			else
				return;
		}

		org.w3c.dom.Document doc = xutil4j.xml.DomUtils.load(path, true);

		this.addBpelProcess(doc);
		bpelMapList.put(path, file.lastModified());
	}

	private QName getQName(org.w3c.dom.Document doc) throws Exception
	{
		NamedNodeMap map = doc.getFirstChild().getAttributes();
		QName qname =
				new QName(map.getNamedItem("targetNamespace").getNodeValue(), map.getNamedItem("name").getNodeValue());
		return qname;
	}

	public void reload() throws RuntimeException
	{
		removeAll();
		read();
	}

	public void removeAll()
	{
		this.getRegisteredBpelProcesses().clear();
		super.removeAll();
	}

	private void startWatcher(String watchDir)
	{
		try
		{
			FileWatcher fileWatcher = FileWatcherList.get(watchDir);
			if (fileWatcher != null)
			{
				fileWatcher.stopWatching();
				fileWatcher.removeFileListener(this);
				FileWatcherList.remove(watchDir);
			}

			fileWatcher = new FileWatcher(watchDir, ".bpel", this.useBpelsInInnerDirs);
			fileWatcher.setInterval(10); // 10 seconds
			fileWatcher.addFileListener(this);
			FileWatcherList.put(watchDir, fileWatcher);
		} catch (Exception e)
		{
			log.warn("Could not create BpelWatcher");
		}
	}

	public void onFileChanged(FileListenerEvent e)
	{
		try
		{
			if (e.getEventType().equalsIgnoreCase(FileListenerEvent.FILE_EVENT_TYPE_ADD)
				|| e.getEventType().equalsIgnoreCase(FileListenerEvent.FILE_EVENT_TYPE_MODIFY))
				addBpelProcess(e.getFile());
			else if (e.getEventType().equalsIgnoreCase(FileListenerEvent.FILE_EVENT_TYPE_DELETE))
				removeBpelProcess(e.getFile());
		} catch (Exception ex)
		{
			log.error(ex);
		}
	}
	
	public void StopFileWatcherList(String serverName){
		String selfServerName = System.getProperty("Seq");
		log.info("***************************************************************************************");
		log.info("Receive ServerName = " + serverName);
		log.info("My ServerName = " + selfServerName);
		
		if(serverName.equals(selfServerName)){
			for(FileWatcher fileWatcher : FileWatcherList.values()){
				fileWatcher.stopWatching();
			}
			
			log.info("***************************************************************************************");
			log.info("********************              BPEL Read Stop             **************************");
			log.info("***************************************************************************************");
		}else{
			log.warn("Receive ServerName is not same my Name");
		}
	}
	@PreDestroy
	public void destroy() {
		log.info("Destroy BPEL Repository.");
	}
}
