/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.common.util;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;

import javax.sql.DataSource;

import junit.framework.TestCase;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cache.CacheFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.Oracle9Dialect;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;

import gov.va.med.fw.persistent.hibernate.EntityInterceptor;

/**
 * @author Martin Francisco
 */
public abstract class HibernateTestCase
    extends AbstractCommonTestCase
{
    private static SessionFactory sessions;
    private static Configuration cfg;
    private static Dialect dialect;
    private static Class lastTestClass;
    private static DataSource ds;
    private Session session;

    /**
     *  
     */
    /*
     * public MappingTestCase() { super(); }
     */

    /**
     * @param name
     */
    public HibernateTestCase(String name)
    {
        super(name);

        HibernateTestCase.ds = (DataSource)applicationContext.getBean("dataSource");
    }

    private void buildSessionFactory(String[] files) throws Exception
    {

        if(this.getSessions() != null)
            this.getSessions().close();

        try
        {
            HibernateTestCase.setCfg(new Configuration());

            //MappingTestCase.cfg.setProperty(Environment.HBM2DDL_AUTO,
            //    "create-drop");

            //MappingTestCase.cfg.setNamingStrategy(ImprovedNamingStrategy.INSTANCE);

            HibernateTestCase.cfg.setProperty(Environment.DIALECT,
                "org.hibernate.dialect.Oracle9Dialect");
            HibernateTestCase.cfg.setProperty(Environment.SHOW_SQL, "true");
            HibernateTestCase.cfg.setProperty(Environment.POOL_SIZE, "20");
            HibernateTestCase.cfg.setProperty(Environment.QUERY_SUBSTITUTIONS, "true 1, false 0");

            for(int i = 0; i < files.length; i++)
            {
                //if(!files[i].startsWith("net/"))
                //    files[i] = "org/hibernate/test/" + files[i];
                HibernateTestCase.getCfg().addResource(files[i],
                    TestCase.class.getClassLoader());
            }

            if(this.getCacheConcurrencyStrategy() != null)
            {

                Iterator iter = HibernateTestCase.cfg.getClassMappings();
                while(iter.hasNext())
                {
                    PersistentClass clazz = (PersistentClass)iter.next();
                    Iterator props = clazz.getPropertyClosureIterator();
                    boolean hasLob = false;
                    while(props.hasNext())
                    {
                        Property prop = (Property)props.next();
                        if(prop.getValue().isSimpleValue())
                        {
                            String type = ((SimpleValue)prop.getValue())
                                .getType().getName();
                            if("blob".equals(type) || "clob".equals(type))
                                hasLob = true;
                        }
                    }
                    if(!hasLob && !clazz.isInherited())
                    {
                        HibernateTestCase.cfg.setCacheConcurrencyStrategy(clazz
                            .getMappedClass().getName(), this
                            .getCacheConcurrencyStrategy());
                    }
                }

                iter = HibernateTestCase.cfg.getCollectionMappings();
                while(iter.hasNext())
                {
                    Collection coll = (Collection)iter.next();
                    HibernateTestCase.cfg.setCacheConcurrencyStrategy(coll
                        .getRole(), this.getCacheConcurrencyStrategy());
                }

            }

            this.setDialect(new Oracle9Dialect());

            this.setSessions(HibernateTestCase.getCfg()
                .buildSessionFactory());

        }
        catch(Exception e)
        {
            e.printStackTrace();
            throw e;
        }

    }

    public String getCacheConcurrencyStrategy()
    {
        return CacheFactory.READ_WRITE;
    }

    protected void customSetUp() throws Exception
    {
        if(this.getSessions() == null
            || HibernateTestCase.lastTestClass != super.getClass())
        {
            this.buildSessionFactory(this.getMappings());
            HibernateTestCase.lastTestClass = super.getClass();
        }
    }

    protected void runTest() throws Throwable
    {
        try
        {
            super.runTest();
            if(this.session != null && this.session.isOpen())
            {
                if(this.session.isConnected())
                    this.session.connection().rollback();
                this.session.close();
                this.session = null;
                fail("unclosed session");
            }
            else
            {
                this.session = null;
            }
        }
        catch(Throwable e)
        {
            try
            {
                if(this.session != null && this.session.isOpen())
                {
                    if(this.session.isConnected())
                        this.session.connection().rollback();
                    this.session.close();
                }
            }
            catch(Exception ignore)
            {
            }
            try
            {
                if(HibernateTestCase.sessions != null)
                {
                    HibernateTestCase.sessions.close();
                    HibernateTestCase.sessions = null;
                }
            }
            catch(Exception ignore)
            {
            }
            throw e;
        }
    }

    public Session openSession() throws HibernateException, SQLException
    {
        Connection connection = HibernateTestCase.ds.getConnection();

        this.session = HibernateTestCase.sessions.openSession(connection, new EntityInterceptor());
        return this.session;
    }

    protected abstract String[] getMappings();

    private void setSessions(SessionFactory sessions)
    {
        HibernateTestCase.sessions = sessions;
    }

    protected SessionFactory getSessions()
    {
        return HibernateTestCase.sessions;
    }

    private void setDialect(Dialect dialect)
    {
        HibernateTestCase.dialect = dialect;
    }

    protected Dialect getDialect()
    {
        return HibernateTestCase.dialect;
    }

    protected static void setCfg(Configuration cfg)
    {
        HibernateTestCase.cfg = cfg;
    }

    protected static Configuration getCfg()
    {
        return HibernateTestCase.cfg;
    }
}