package com.fp.hbm.helper;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.jar.JarFile;

import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.stat.Statistics;

import org.apache.log4j.Logger;
import com.fp.common.logger.APPLogger;
import com.fp.persistence.commondb.helper.FormatDates;
public class PersistenceManagerHQL {
	/**
	 * Key para genrar un map de los beans mapeados en el archivo de
	 * hbconfiguration.
	 */
	public static final String NAME = "NAMES";

	/**
	 * Key que alamacena el numero de sessiones abiertas en el sessionfactory.
	 */
	public static final String OPEN = "OPEN";

	/**
	 * Key que alamacena el numero de sessiones cerradas en el sessionfactory.
	 */
	public static final String CLOSED = "CLOSED";

	/**
	 * Key que almacenal hora en la que se genero el sessionfactory .
	 */
	public static final String START_TIME = "START TIME";

	/**
	 * Instancia a la que hace referencia el Sigleton
	 */
	private static PersistenceManagerHQL instance = null;

	private String basePath = null;

	private Map<String, String> testBeans = null;

	/**
	 * Referencia al SessionFactory
	 */
	private SessionFactory sessionFactory;

	/**
	 * Referencia al Objeto de configuraci�n del SessionFactory
	 */
	//private Configuration cfg;
	private AnnotationConfiguration cfg;

	private PersistenceMetadata metadata;

	private Logger log = APPLogger.getLogger();

	/**
	 * Entrega el valor de cfg.
	 *
	 * @return cfg.
	 */
	public Configuration getCfg() {
		return cfg;
	}

	public PersistenceMetadata getMetadata() {
		return metadata;
	}

	private PersistenceManagerHQL(boolean pServlet, String pBasePath) {
		if (pServlet) {
			this.basePath = pBasePath;
		}
		this.prepare((String[]) null);
	}

	/**
	 * Crea una instancia de PersistenceManager.
	 *
	 * @throws Exception
	 */
	private PersistenceManagerHQL(String... pEntity) {
		this.prepare(pEntity);
	}

	private void prepare(String... pEntity) {
		if (pEntity != null && pEntity.length > 0) {
			this.testBeans = new HashMap<String, String>();
			for (String string : pEntity) {
				testBeans.put(string.trim(), string);
			}
			log.warn("Beanes de trabajo " + testBeans.keySet());
		}

		SimpleDateFormat sdf = FormatDates.getInstance().getTimeCountFormat();
		long ini = System.currentTimeMillis();
		cfg = new AnnotationConfiguration();
		if (System.getProperties().containsKey("hibernate.cfg")) {
			File f = new File(System.getProperty("hibernate.cfg"));
			cfg.configure(f);
		} else {
			cfg.configure("/hibernateFlip.cfg.xml");
		}
		//this.findBeans(this.basePath);
		log.debug("Tiempo de configuracion " + sdf.format(new Date(System.currentTimeMillis() - ini)));
		ini = System.currentTimeMillis();
		sessionFactory = cfg.buildSessionFactory();
		log.debug("Tiempo de construccion del Session Factory " + sdf.format(new Date(System.currentTimeMillis() - ini)));
		this.metadata = new PersistenceMetadata(cfg);
	}

	/**
	 * Busca y anade los beanes encontrados a la configuracion del Hibernate
	 *
	 * @throws Exception
	 */
	private void findBeans(String pPath) {
		String classPath = System.getProperty("java.class.path");
		String pathSeparator = System.getProperty("path.separator");
		if (pPath == null && System.getProperties().get("persistence.path") != null) {
			pPath = System.getProperties().getProperty("persistence.path");
		}
		if (pPath != null) {
			classPath = pPath;
			System.out.println("Classpath de busqueda de Entities: " + classPath);
		}
		StringTokenizer st = new StringTokenizer(classPath, pathSeparator);
		Map<String, Object> files = new HashMap<String, Object>();
		while (st.hasMoreElements()) {
			String jname = (String) st.nextElement();
			File f = new File(jname);
			if (!f.isDirectory()) {
				try {
					this.manageJarFile(f, files);
				} catch (Exception e) {
					continue;
				}
			} else {
				File s[] = f.listFiles();
				for (File file : s) {
					if (file.isDirectory()) {
						List<String> data = this.getDirectory(file, file.getPath());
						for (String string : data) {
							if (string.indexOf("hbm.xml") > -1) {
								files.put("" + string, "");
							}
						}
					} else {
						if (("" + file).indexOf("hbm.xml") > -1) {
							files.put("" + file, "");
						} else {
							try {
								if (("" + file).indexOf(".jar") > -1) {
									this.manageJarFile(file, files);
								}
							} catch (Exception err) {
								APPLogger.getLogger().warn("Error: " + file + " " + err.getMessage());
								continue;
							}
						}
					}
				}

			}

		}
		for (String f1 : files.keySet()) {
			String name = f1;
			// if (name.indexOf("hbm.xml") > -1) {
			try {
				if (this.testBeans != null) {
					if (!this.testBeans.containsKey(name.substring(0, name.indexOf(".")).replaceAll("/", "."))) {
						continue;
					}
				}
				this.cfg.addResource(name);
				log.info(name);
			} catch (Exception e) {
				e.printStackTrace();
				log.warn(e.getMessage());
				continue;
			}
			// }
		}
	}

	@SuppressWarnings("unchecked")
	private void manageJarFile(File pFile, Map<String, Object> files) throws IOException {
		JarFile jr = new JarFile(pFile);
		Enumeration en = jr.entries();
		while (en.hasMoreElements()) {
			String name = "" + en.nextElement();
			if (name.indexOf("hbm.xml") > -1) {
				files.put("" + name, "");
			}
		}
	}

	/**
	 * Busqueda Recursiva de Beanes por Directorio
	 *
	 * @param pDirectory
	 *            Referencia al Directorio
	 * @param name
	 *            Nombre del Directorio
	 * @return
	 * @throws Exception
	 */
	List<String> getDirectory(File pDirectory, String name) {
		name = name.replaceAll("\\\\", "/");
		List<String> files = new ArrayList<String>();
		File s[] = pDirectory.listFiles();
		for (File file : s) {
			if (file.isDirectory()) {
				files.addAll(this.getDirectory(file, ""));
			} else {
				String f = ("" + file);
				f = f.replaceAll("\\\\", "/");
				files.add(("" + f).replaceAll(name, ""));
			}
		}
		if (name.compareTo("") != 0) {
			List<String> fnames = new ArrayList<String>();
			for (String f : files) {
				f = f.replaceAll("\\\\", "/");
				String sub = name.substring(0, name.lastIndexOf('/'));
				fnames.add(f.replaceAll(sub, "").substring(1));
			}
			return fnames;
		} else {
			return files;
		}
	}

	public static PersistenceManagerHQL getInstance() {
		return getInstance((String[]) null);
	}

	/**
	 * Entrega la unica instancia de PersistenceManager.
	 *
	 * @return
	 * @throws Exception
	 */
	public static PersistenceManagerHQL getInstance(String... pBeans) {
		if (pBeans != null && instance != null) {
			instance.close();
			instance = null;
		}
		if (instance == null) {
			instance = new PersistenceManagerHQL(pBeans);
		}
		return instance;
	}

	public static PersistenceManagerHQL getInstance(boolean pServlet, String pBasePath) {
		if (instance == null) {
			System.out.println("Servlet " + pServlet + " path " + pBasePath);
			instance = new PersistenceManagerHQL(pServlet, pBasePath);
		}
		return instance;
	}

	/**
	 * Entrega un session de hibernate.
	 *
	 * @param pConnecion
	 *            Connecion jdbc.
	 * @return s
	 * @throws Exception
	 */
	public Session getSession(Connection pConnecion) {
		Session s = sessionFactory.openSession(pConnecion);
		return s;
	}

	public Session getSession() {
		Session s = sessionFactory.openSession();
		return s;
	}

	public Session getCurrentSession() {
		Session s = sessionFactory.getCurrentSession();
		return s;
	}

	/**
	 * Devuelve el estado del session factory, Ejemplo numero de sesiones
	 * abiertas, entities creados, numero de sessiones cerradas,.
	 *
	 * @return
	 * @throws Exception
	 */
	public Map<String, Object> getSessionFactoryStatus() {
		Statistics stat = this.sessionFactory.getStatistics();
		Map<String, Object> m = new HashMap<String, Object>();
		m.put(NAME, stat.getEntityNames());
		m.put(OPEN, "" + stat.getSessionOpenCount());
		m.put(CLOSED, "" + stat.getSessionCloseCount());
		m.put(START_TIME, new Date(stat.getStartTime()));
		return m;
	}

	/** Cierra el SessionFactory */
	public void close() {
		this.sessionFactory.close();
	}

	/**
	 * Permite Fijar el estado de Trace de la Conexión
	 *
	 * @param pSession
	 *            Session de hibernate de Referencia
	 * @param pStatus
	 *            Estado del Trace.
	 * @throws SQLException
	 * @throws Exception
	 */
	public void setTrace(Session pSession, boolean pStatus)  {
		SQLQuery sql = pSession.createSQLQuery("alter session set sql_trace =" + pStatus);
		sql.executeUpdate();

	}
}
