

package gov.va.med.cds.persistence.hibernate;


import gov.va.med.cds.clinicaldata.vhim400.ClinicalDataResponse400;
import gov.va.med.cds.persistence.JNDIDataSourceBindingsLocatorInterface;
import gov.va.med.cds.persistence.PersistenceManagerInterface;
import gov.va.med.cds.persistence.QueryStrategyInterface;
import gov.va.med.cds.persistence.hibernate.common.VistaPreQueryExecutionInterface;
import gov.va.med.cds.rules.BooleanRuleInterface;
import gov.va.med.cds.template.generated.JaxBMarshallerUnmarshallerInterface;
import gov.va.med.cds.transaction.WorkManagerInterface;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Required;

import commonj.work.WorkManager;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.Binding;
import javax.sql.DataSource;


/**
 * 
 * @author vhaislroberk
 *
 */
public class SiteSpecificHibernateReadPersistenceManagerFactory
{

    private Log logger = LogFactory.getLog( SiteSpecificHibernateReadPersistenceManagerFactory.class );

    private Map<String, SpringHibernateLocalSessionFactoryPropertyHolder> propertyHolderMap;
    private JNDIDataSourceBindingsLocatorInterface dataSourceBindingsLocator;
    private List<BooleanRuleInterface> rules;
    protected Map<String, QueryStrategyInterface> templateQueryStrategyMap;
    private long readTimeout = WorkManager.INDEFINITE;
    protected Class<?> pointInTimeUserTypeName;
    private WorkManagerInterface workManager;
    private WorkManagerInterface queryThreadWorkManager;
    private String managerIdentifierFormat;
    private Map<String, String> templateTimeoutMap = null;
    private List<String> vistaExcludedTemplateIdsList;
    private ClinicalDataResponse400 defaultCDSResponse;
    private Map<String, VistaPreQueryExecutionInterface> vistaPreQueryExecutionMap;
    protected JaxBMarshallerUnmarshallerInterface marshallerUnmarshaller;


	public Collection<PersistenceManagerInterface> create( )
        throws Exception
    {
        Collection<PersistenceManagerInterface> persistenceManagers = new ArrayList<PersistenceManagerInterface>();
        Collection<Binding> dataSourceBindings = dataSourceBindingsLocator.getDataSourceBindings();
        for ( Binding binding : dataSourceBindings )
        {
            SiteSpecificHibernateReadPersistenceManager persistenceManager = createNewPersistenceManager( binding.getName() );
            Map<String, SessionFactory> newSessionFactoryMap = createNewSessionFactoryMap( ( DataSource )binding.getObject() );
            persistenceManager.setSessionFactoryMap( newSessionFactoryMap );
            persistenceManagers.add( persistenceManager );
        }
        return persistenceManagers;
    }


    /**
     * Sets the session factory map with maps template identifiers to session factory instances.
     * 
     * @param sessionFactoryMap session factory passed in from spring injection.
    */
    @Required
    final public void setSessionFactoryPropertyHolderMap( Map<String, SpringHibernateLocalSessionFactoryPropertyHolder> propertyHolderMap )
    {
        this.propertyHolderMap = propertyHolderMap;
    }


    @Required
    final public void setDataSourceBindingsLocator( JNDIDataSourceBindingsLocatorInterface dataSourcesLocator )
    {
        this.dataSourceBindingsLocator = dataSourcesLocator;
    }


    /**
     * Sets the rules that govern the behavior of the isApplicable method.
     * @param rules The rules to evaluate.
     */
    @Required
    public void setRules( List<BooleanRuleInterface> rules )
    {
        this.rules = rules;
    }


    /**
     * 
     * @param templateQueryStrategyMap
     */
    @Required
    public void setTemplateQueryStrategyMap( Map<String, QueryStrategyInterface> templateQueryStrategyMap )
    {
        this.templateQueryStrategyMap = templateQueryStrategyMap;
    }


    /**
     * Set the work manager to be used to run concurrent operations by the read persistence manager.
     * @param workManager The work manager.
     */
    @Required
    public void setWorkManager( WorkManagerInterface workManager )
    {
        this.workManager = workManager;
    }


    @Required
    public void setQueryThreadWorkManager( WorkManagerInterface queryThreadWorkManager )
    {
        this.queryThreadWorkManager = queryThreadWorkManager;
    }


    @Required
    public void setTemplateTimeoutMap( Map<String, String> templateTimeoutMap )
    {
        this.templateTimeoutMap = templateTimeoutMap;
    }


    /**
     * Set the read timeout or the number of milliseconds to wait for the reads 
     * before timing out.
     * @param readTimeout
     */
    public void setReadTimeout( long readTimeout )
    {
        this.readTimeout = readTimeout;
    }


    @Required
    public void setPointInTimeUserType( Class<?> pointInTimeUserType )
        throws ClassNotFoundException
    {
        this.pointInTimeUserTypeName = pointInTimeUserType;
    }


    /**
     * Sets the manager idetifier for the persistence manager.
     * 
     * @param managerIdentifier The identifier.
     */
    @Required
    public void setManagerIdentifierFormat( String managerIdentifier )
    {
        this.managerIdentifierFormat = managerIdentifier;
    }


    private Map<String, SessionFactory> createNewSessionFactoryMap( DataSource dataSource )
        throws Exception
    {
        Map<String, SessionFactory> newMap = new HashMap<String, SessionFactory>();
        for ( Map.Entry<String, SpringHibernateLocalSessionFactoryPropertyHolder> entry : this.propertyHolderMap.entrySet() )
        {
            SpringHibernateLocalSessionFactoryPropertyHolder curPropertyHolder = entry.getValue();
            org.springframework.orm.hibernate4.LocalSessionFactoryBean newFactory = new org.springframework.orm.hibernate4.LocalSessionFactoryBean();
            newFactory.setMappingResources( curPropertyHolder.getMappingResources() );
            newFactory.setHibernateProperties( curPropertyHolder.getHibernateProperties() );
            newFactory.setDataSource( dataSource );

            newFactory.afterPropertiesSet();
            SessionFactory sessionFactory = newFactory.getObject();
            newMap.put( entry.getKey(), sessionFactory );
        }
        return newMap;

    }


    private SiteSpecificHibernateReadPersistenceManager createNewPersistenceManager( String name )
        throws ClassNotFoundException
    {
        SiteSpecificHibernateReadPersistenceManager persistenceManager = new SiteSpecificHibernateReadPersistenceManager();
        String managerIdentifier = MessageFormat.format( this.managerIdentifierFormat, name );
        persistenceManager.setManagerIdentifier( managerIdentifier );
        persistenceManager.setPointInTimeUserType( this.pointInTimeUserTypeName );
        persistenceManager.setRules( this.rules );
        persistenceManager.setSiteIdentifier( name );
        persistenceManager.setTemplateQueryStrategyMap( this.templateQueryStrategyMap );
        persistenceManager.setQueryThreadWorkManager( this.queryThreadWorkManager );
        persistenceManager.setTemplateTimeoutMap( this.templateTimeoutMap );
        persistenceManager.setVistaExcludedTemplateIdsList( this.vistaExcludedTemplateIdsList );
        persistenceManager.setDefaultCDSResponse( this.defaultCDSResponse );
        persistenceManager.setVistaPreQueryExecutionMap( vistaPreQueryExecutionMap );
        persistenceManager.setMarshallerUnmarshaller(marshallerUnmarshaller);

       // if ( logger.isInfoEnabled() )
        //{
           // logger.info( "Created site specific persistence Manager:" + " siteId=" + name + " managerIdentifier=" + managerIdentifier );
        //}
       if ( logger.isInfoEnabled() )
       {
            String msg = "Created site specific persistence Manager:" + " siteId=" + name + " managerIdentifier=" + managerIdentifier;

            logger.info( gov.va.med.cds.util.LogMessageUtil.buildMessage( null, null, null, msg ), null );
        }
        return persistenceManager;
        
    }


    public void setVistaExcludedTemplateIdsList( List<String> 	vistaExcludedTemplateIdsList )
    {
        this.vistaExcludedTemplateIdsList = vistaExcludedTemplateIdsList;
    }

    public void setDefaultCDSResponse( ClinicalDataResponse400 defaultCDSResponse )
    {
        this.defaultCDSResponse = defaultCDSResponse;
    }
    
    public JaxBMarshallerUnmarshallerInterface getMarshallerUnmarshaller() {
		return marshallerUnmarshaller;
	}


	public void setMarshallerUnmarshaller(
			JaxBMarshallerUnmarshallerInterface marshallerUnmarshaller) {
		this.marshallerUnmarshaller = marshallerUnmarshaller;
	}


    @Required
    public void setPreQueryExecutionMap( Map<String, VistaPreQueryExecutionInterface> vistaPreQueryExecutionMap )
    {
        this.vistaPreQueryExecutionMap = vistaPreQueryExecutionMap;
    }

    //    private SiteSpecificRpcHibernateReadPersistenceManager createNewRpcPersistenceManager(String name) throws ClassNotFoundException
    //    {
    //        SiteSpecificRpcHibernateReadPersistenceManager persistenceManager = new SiteSpecificRpcHibernateReadPersistenceManager();
    //        String managerIdentifier =  MessageFormat.format(this.managerIdentifierFormat,name);
    //        persistenceManager.setManagerIdentifier( managerIdentifier );
    //        persistenceManager.setPointInTimeUserType( this.pointInTimeUserTypeName );
    //        persistenceManager.setReadTimeout( this.readTimeout );
    //        persistenceManager.setRules( this.rules );
    //        persistenceManager.setSiteIdentifier( name );
    //        persistenceManager.setTemplateQueryStrategyMap( this.templateQueryStrategyMap );
    //        persistenceManager.setWorkManager( this.workManager );
    //        if (logger.isInfoEnabled())
    //        {
    //            logger.info("Created RPC site specific persistence Manager:"+
    //                 " siteId="+name+
    //                 " managerIdentifier="+managerIdentifier
    //             );
    //        }
    //        return persistenceManager;
    //    }
}
