package com.fitbank.common.conectivity;

import com.fitbank.common.RequestData;
import com.fitbank.common.logger.FitbankLogger;
import com.fitbank.dto.management.Detail;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

/**
 * Clase que permite enlazar una sesion auxiliar para conexiones alternas.
 * 
 * @author Soft Warehouse S.A.
 */
public final class HbSessionAuxiliar {

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

    private static boolean monitor = false;

    private static void createInstance() throws Exception {
        while (monitor) {
            Thread.sleep(100);
        }

        monitor = true;

        try {
            if (instance == null) {
                instance = new HbSessionAuxiliar();
            }
        } finally {
            monitor = false;
        }
    }

    /**
     * Entrega la unica instancia de HbSession.
     *
     * @return
     * @throws Exception
     */
    public static HbSessionAuxiliar getInstance() throws Exception {
        synchronized (HbSessionAuxiliar.class) {

            if (instance == null) {
                createInstance();
            }
        }
        return instance;
    }

    /**
     * Referencia al SessionFactory a usar.
     */
    private SessionFactory sessionFactory;

    /**
     * Session Factory principal usado.
     */
    private SessionFactory mainSessionFactory;

    /**
     * Session Factory alterno.
     */
    private SessionFactory alternateSessionFactory;

    /**
     * Regerencia al serviceRegistry principal.
     */
    private ServiceRegistry mainServiceRegistry;

    /**
     * Regerencia al serviceRegistry alterno.
     */
    private ServiceRegistry alternateServiceRegistry;

    /**
     * Referencia al Objeto de configuraci�n del SessionFactory.
     */
    private final Configuration mainCfg;

    /**
     * Referencia al Objeto de configuraci�n del SessionFactory Alterno.
     */
    private Configuration alternateCfg;

    /**
     * Crea una instancia de HbSession.
     *
     * @throws Exception
     */
    private HbSessionAuxiliar() throws Exception {
        this.mainCfg = new Configuration();
        this.mainCfg.configure("/HbAuxiliar.xml");
        this.mainServiceRegistry = new ServiceRegistryBuilder().applySettings(this.mainCfg.getProperties()).buildServiceRegistry();
        this.mainSessionFactory = this.mainCfg.buildSessionFactory(this.mainServiceRegistry);
        FitbankLogger.getLogger().info("SesionFactory auxiliar armado con exito");

        try {
            this.alternateCfg = new Configuration();
            this.alternateCfg.configure("/HbAuxiliar2.xml");
            this.alternateServiceRegistry = new ServiceRegistryBuilder().applySettings(this.mainCfg.getProperties()).buildServiceRegistry();
            this.alternateSessionFactory = this.mainCfg.buildSessionFactory(this.alternateServiceRegistry);
        } catch (HibernateException he) {
            FitbankLogger.getLogger().warn("No existe informacion para un SessionFactory auxiliar alternativo", he);
        }
    }

    /**
     * Cierra los SessionFactory.
     *
     * @throws java.lang.Exception
     */
    public void close() throws Exception {
        this.mainSessionFactory.close();

        try {
            this.alternateSessionFactory.close();
        } catch (HibernateException he) {
            FitbankLogger.getLogger().warn("No se pudo cerrar un SessionFactory auxiliar alternativo", he);
        }
    }

    /**
     * Entrega el valor de cfg.
     *
     * @return cfg.
     */
    public Configuration getCfg() {
        Detail detail = RequestData.getDetail();
        if ("XML".equals(detail.getChannel())) {
            return this.alternateCfg;
        }

        return this.mainCfg;
    }

    /**
     * Obtiene el dialecto actual del SessionFactory
     *
     * @return
     */
    public Dialect getDialect() {
        this.defineSessionFactory();
        SessionFactoryImpl imp = (SessionFactoryImpl) this.sessionFactory;
        return imp.getDialect();
    }

    /**
     * Entrega un session de hibernate.
     *
     * @return Session
     * @throws Exception
     * @deprecated Mejor usar HbSessionAuxiliar.openSession
     */
    public Session getSession() throws Exception {
        this.defineSessionFactory();
        return this.sessionFactory.openSession();
    }

    /**
     * Abre una nueva sesion bajo el thread actual.
     *
     * @return Session disponible
     */
    public Session openSession() {
        this.defineSessionFactory();
        return this.sessionFactory.openSession();
    }

    /**
     * Obtiene el SessionFactory actual.
     *
     * @return SessionFactory actual.
     */
    public SessionFactory getSessionFactory() {
        this.defineSessionFactory();
        return this.sessionFactory;
    }

    private void defineSessionFactory() {
        Detail detail = RequestData.getDetail();
        if ("XML".equals(detail.getChannel())) {
            this.sessionFactory = this.alternateSessionFactory;
        } else {
            this.sessionFactory = this.mainSessionFactory;
        }
    }
}
