/**
 * 
 */


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


import gov.va.med.cds.common.person.correlation.PersonIdentifierInterface;
import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.filter.EntryFilterInterface;
import gov.va.med.cds.persistence.PersistenceException;
import gov.va.med.cds.persistence.QueryStrategyInterface;
import gov.va.med.cds.persistence.QueryWorkInterface;
import gov.va.med.cds.persistence.ReadException;
import gov.va.med.cds.persistence.hibernate.common.VistaPreQueryExecutionInterface;
import gov.va.med.cds.template.generated.JaxBMarshallerUnmarshallerInterface;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Required;


/**
 * @author vhaislegberb
 *
 */
public class SiteSpecificHibernateReadPersistenceManager
    extends
        DefaultHibernateReadPersistenceManager
{

    private Map<String, VistaPreQueryExecutionInterface> vistaPreQueryExecutionMap;


    /* (non-Javadoc)
     * @see gov.va.med.cds.persistence.ReadPersistenceManagerInterface#isApplicable(gov.va.med.cds.filter.EntryFilterInterface, java.util.List)
     */
    @Override
    public boolean isApplicable( EntryFilterInterface entryFilter, List<PersonIdentifierInterface> personIdentifiers )
        throws PersistenceException
    {
        return ( super.isApplicable( entryFilter, personIdentifiers ) && (containsSiteIdentifier( personIdentifiers, entryFilter ) ));
    }


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.persistence.hibernate.DefaultHibernateReadPersistenceManager#createQueryWork(gov.va.med.cds.persistence.QueryStrategyInterface, gov.va.med.cds.filter.EntryFilterInterface, java.util.List, java.lang.Class)
     */
    @SuppressWarnings("unchecked")
	@Override
    protected List<QueryWorkInterface> createQueryWork( QueryStrategyInterface queryStrategy, EntryFilterInterface entryFilter,
                    List<PersonIdentifierInterface> personIdentifiers, Class<?> pointInTimeUserType, String siteId, JaxBMarshallerUnmarshallerInterface marshallerUnmarshaller )
        throws PersistenceException
    {
    	List<String> patientIdentifiers = null;
    	SessionFactory sessionFactory = getSessionFactory( entryFilter.getTemplateId() );
        if(entryFilter.isPatientCentricFilter()){
        	List<String> sitePatients = findSitePatients( personIdentifiers );
        	patientIdentifiers = handlePatientIdentifiers( sessionFactory.openSession(), entryFilter, sitePatients );
        }
        return queryStrategy.createQueryWork( sessionFactory, entryFilter, patientIdentifiers, pointInTimeUserType, siteId, marshallerUnmarshaller );
    }


    /**
     * Iterates over the list until it comes across the person identifier with the assigning facility 
     * that matches the site.
     * @param personIdentifiers This list of person identifiers to look through.
     * @return The person identifier with an assigning facility that matches the 
     * site identifier of the 
     */
    private List<String> findSitePatients( List<PersonIdentifierInterface> personIdentifiers )
    {
        List<String> sitePatients = new ArrayList<String>();

        if ( personIdentifiers == null || personIdentifiers.size() == 0 )
        {
            throw new ReadException( ErrorCodeEnum.READ_PERSISTENCE_MGR_NULL_PERSON_IDENTIFIERS_LIST, this.managerIdentifier );
        }

        for ( PersonIdentifierInterface personIdentifier : personIdentifiers )
        {
            if ( this.siteIdentifier.equals( personIdentifier.getAssigningFacility() ) )
            {
                sitePatients.add( personIdentifier.getIdentity() );
            }
        }

        if ( sitePatients.size() == 0 )
        {
            throw new ReadException( ErrorCodeEnum.READ_PERSISTENCE_MGR_NO_PERSON_WITH_SITE_ID, this.siteIdentifier );
        }

        return sitePatients;
    }


    /**
     * Determines whether the list of 
     * @param personIdentifiers
     * @return
     */
    private boolean containsSiteIdentifier( List<PersonIdentifierInterface> personIdentifiers, EntryFilterInterface entryFilter )
    {
        //If PersonIdentifiers is populated - logic will be applied that this is a PatientCentric read/filter being processed
    	if(personIdentifiers != null){
    		for ( PersonIdentifierInterface personIdentifier : personIdentifiers )
    		{
    			if ( this.siteIdentifier.equals( personIdentifier.getAssigningFacility() ) )
    			{
    				return true;
    			}
    		}
    	}
    	
    	//Or this may be a filter that doesn't have a patients, but may be a siteCentric filter being processed/read
    	if(entryFilter.isSiteCentricFilter())
    	{
    		String siteIdentifier = getSiteIdentifier(); /// fortify null deference check
    		if (siteIdentifier !=null)
    		{
    			if(siteIdentifier.equals(entryFilter.getFacilityId())){
    				return true;
    			}	
    		}
    	}
    	//OR it was a read/filter that did not apply to this read manager's specific site
        return false;
    }


    public String getSiteIdentifier( )
    {
        return this.siteIdentifier;
    }


    @SuppressWarnings( "rawtypes" )
    protected List handlePatientIdentifiers( Session session, EntryFilterInterface entryFilter, List patientIdentifiers )
        throws ReadException
    {
        if ( vistaPreQueryExecutionMap != null )
        {
            VistaPreQueryExecutionInterface vistaPreQueryExecutor = vistaPreQueryExecutionMap.get( entryFilter.getTemplateId() + "-" + entryFilter.getDomainEntryPoint() );
            if ( vistaPreQueryExecutor != null )
            {
                return vistaPreQueryExecutor.getRowIds( session, entryFilter, patientIdentifiers );
            }
        }

        return patientIdentifiers;
    }


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

}
