/*
 * Created Apr 17, 2007
 */


package gov.va.med.cds.template;


import gov.va.med.cds.clinicaldata.TemplateMetaData;
import gov.va.med.cds.clinicaldata.TemplateMetaDataInterface;
import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.exceptionframework.ExceptionHandler;
import gov.va.med.cds.persistence.hibernate.tfs.AbstractTfsHibernatePersistenceManager;
import gov.va.med.cds.request.ValidationException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;


public class HibernateOracleTemplateCachePersistence
    extends
        AbstractTfsHibernatePersistenceManager
    implements
        TemplateCachePersistenceInterface
{

    //calling class needs to be synchronized to protect its class level variable
    public final void loadAllTemplatesIntoCache( TemplateCacheInterface aTemplateCache )
        throws TemplateCacheException
    {
        Session session = this.sessionFactory.getCurrentSession();
        loadTemplateCacheFromPersistence( aTemplateCache, null, session );
    }


    //calling class needs to be synchronized to protect its class level variable
    public final void loadTemplateIntoCache( TemplateCacheInterface aTemplateCache, String aTemplateId )
        throws TemplateCacheException
    {
        Session session = this.sessionFactory.getCurrentSession();
        loadTemplateCacheFromPersistence( aTemplateCache, aTemplateId, session );
    }


    /**
     * Loads templates from template_schema table into memory. The assumption is that the templates are stored as BLOBs.
     * The BLOB is a Java archive (jar) file.
     * 
     * @param aSession the hibernate session with which to make the database call.
     * @param aTemplateId the template id. If (@code aTemplateId) is null or an empty string, all templates will be
     *            retrieved.
     * @throws TemplateCacheException if an exception occurs in database template cache during the lookup of the
     *             template
     */
    @SuppressWarnings( "unchecked" )
    private List<TemplateMetaData> loadTemplatesFromPersistence( Session aSession )
    {
        List<String> templateIds = null;
        List<TemplateMetaData> templateMetaDatas = new ArrayList<TemplateMetaData>();
        try
        {
            Query query = buildTemplateStatusQuery( aSession );

            templateIds = query.list();

            for ( String templateId : templateIds )
            {
                templateMetaDatas.add( loadTemplateFromPersistence( aSession, templateId ) );
            }
        }
        catch ( Exception exception )
        {
            ExceptionHandler.handleException( exception, null, null, null );
        }

        return templateMetaDatas;
    }


    private Query buildTemplateStatusQuery( Session aSession )
    {
        String queryName = "TemplateSchemaIdsByStatus";
        Query query = aSession.getNamedQuery( queryName );

        query.setParameter( "status", "ACTIVE" );
        return query;
    }


    private Query buildTemplateIdQuery( Session aSession, String aTemplateId )
    {
        String queryName = "TemplateSchemaId";
        Query query = aSession.getNamedQuery( queryName );

        query.setParameter( "templateId", aTemplateId );
        return query;
    }


    private final void loadTemplateCacheFromPersistence( TemplateCacheInterface aTemplateCache, String aTemplateId, Session aSession )
        throws TemplateCacheException
    {

        if ( aTemplateCache == null )
        {
            throw new TemplateCacheException( ErrorCodeEnum.CANNOT_LOAD_TEMPLATE_CACHE, "Template cache passed in is null." );
        }

        try
        {
            if ( aTemplateId == null )
            {
                List<TemplateMetaData> hibernateQueryResults = loadTemplatesFromPersistence( aSession );

                for ( TemplateMetaData templateMetaData : hibernateQueryResults )
                {
                    loadTemplateCache( aTemplateCache, templateMetaData );
                }
            }
            else
            {
                TemplateMetaData templateMetaData = loadTemplateFromPersistence( aSession, aTemplateId );
                if ( templateMetaData != null )
                {
                    loadTemplateCache( aTemplateCache, templateMetaData );
                }
                //                else
                //                {
                //                    throw new TemplateCacheException( ErrorCodeEnum.CANNOT_LOAD_TEMPLATE_METADATA, aTemplateId,
                //                                    "No records matched for templateId = " + aTemplateId );
                //                }
            }
        }
        catch ( HibernateException e )
        {
            throw new TemplateCacheException( ErrorCodeEnum.CANNOT_LOAD_TEMPLATE_CACHE, e, e.getMessage() );
        }
        catch ( IOException e )
        {
            throw new TemplateCacheException( ErrorCodeEnum.CANNOT_LOAD_TEMPLATE_CACHE, e, e.getMessage() );
        }
        catch ( ValidationException e )
        {
            throw new TemplateCacheException( ErrorCodeEnum.CANNOT_LOAD_TEMPLATE_CACHE, e, e.getMessage() );
        }
    }


    /**
     * Loads templates from template_schema table into memory.
     * 
     * @param aSession the hibernate session with which to make the database call.
     * @param aTemplateId the template id.
     */
    private TemplateMetaData loadTemplateFromPersistence( Session aSession, String aTemplateId )
    {
        TemplateMetaData queryResults = null;
        try
        {
            Query query;

            if ( aTemplateId == null )
            {
                throw new TemplateCacheException( ErrorCodeEnum.CANNOT_LOAD_TEMPLATE_METADATA, aTemplateId, "aTemplateId cannot be null" );
            }
            else
            {
                query = buildTemplateIdQuery( aSession, aTemplateId );
            }

            queryResults = ( TemplateMetaData )query.uniqueResult();
        }
        catch ( Exception exception )
        {
            ExceptionHandler.handleException( exception, null, null, null );
        }

        return queryResults;
    }


    private void loadTemplateCache( TemplateCacheInterface aTemplateCache, TemplateMetaData aTemplateMetaData )
        throws IOException
    {
        aTemplateCache.loadSchemaIntoCache( aTemplateMetaData );
    }


    /**
     * Persists a single templates to the template_schema table.
     * 
     * @param aInputStream an {@code InputStream} of the template. This is expected to be a Java archive (jar) file.
     * @param aTemplateMetaData meta data referring to the template to be persisted. This information is also persisted
     *            in the template_schema table.
     * @throws TemplateCacheException if exception occurs during the database persistence of the template
     */
    public final void saveTemplateMetaDataToPersistence( TemplateMetaDataInterface aTemplateMetaData )
        throws TemplateCacheException
    {
        Session session = this.sessionFactory.getCurrentSession();

        try
        {
            persistifyVhimVersions( session, aTemplateMetaData.getVhimVersionWrappers() );

            persistifyDomainEntryPoints( session, aTemplateMetaData.getDomainEntryPointWrappers() );

            persistifyTemplate( session, aTemplateMetaData );
        }
        catch ( HibernateException e )
        {
            throw new TemplateCacheException( ErrorCodeEnum.CANNOT_SAVE_TEMPLATE, e, aTemplateMetaData.getTemplateId(), e.getMessage() );
        }
    }


    private void persistifyTemplate( Session aSession, TemplateMetaDataInterface aTemplateMetaData )
    {
        TemplateMetaData queryResult = null;

        // check to see if that template already exists in the database
        // if it does, replace the one in the TMD collection with the persistent version.
        Query query = aSession.getNamedQuery( "TemplateSchemaId" ).setParameter( "templateId", aTemplateMetaData.getTemplateId() );
        queryResult = ( TemplateMetaData )query.uniqueResult();

        if ( queryResult != null )
        {
            aSession.delete( queryResult );
            // we never give back the persistent object if it already existed - hope that's not a problem.
        }

        aSession.flush();
        aSession.save( aTemplateMetaData );
    }

}
