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

// Java Classes
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.Validate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateSystemException;
import org.springframework.orm.hibernate3.HibernateTemplate;

import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.MaxRecordsExceededException;
import gov.va.med.fw.persistent.hibernate.AbstractDAOAction;
import gov.va.med.fw.persistent.hibernate.GenericDAOImpl;
import gov.va.med.fw.persistent.hibernate.PaginatedQueryExecutor;
import gov.va.med.fw.service.pagination.SearchQueryInfo;
import gov.va.med.fw.service.ServiceException;

import gov.va.med.esr.common.model.lookup.MessageType;
import gov.va.med.esr.common.model.messaging.MessageLogEntry;
import gov.va.med.esr.common.model.messaging.MessageLogEntryLite;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO;
import gov.va.med.esr.service.MessageLogCriteria;

/**
 * Provides methods to retrieve a message log entry entity
 *
 * Project: Common</br>
 * Created on: 9:47:22 AM </br>
 *
 * @author DNS   LEV, Martin Francisco
 */
public class MessageLogEntryDAOImpl extends GenericDAOImpl implements MessageLogEntryDAO {
	/**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = -4381743890002053737L;

    public MessageLogEntry getById(BigDecimal identifier) throws DAOException {
        try {
            HibernateTemplate tpl = this.getHibernateTemplate();
            List results = tpl.findByNamedQueryAndNamedParam( FIND_BY_IDENTIFIER,PARAM_IDENTIFIER,identifier );

            // Should only return one value
            return results.isEmpty() ? null : (MessageLogEntry) results.get(0);
        } 
        catch (DataAccessException e) {
            throw new DAOException("Failed to get the message log entry by identifier", e);
        }
    }
    
    public MessageLogEntryLite getMessageLogEntryLiteById(BigDecimal identifier) throws DAOException {
        try {
            HibernateTemplate tpl = this.getHibernateTemplate();
            List results = tpl.findByNamedQueryAndNamedParam( FIND_MESSAGE_LOG_ENTRY_LITE_BY_IDENTIFIER,PARAM_IDENTIFIER,identifier );

            // Should only return one value
            return results.isEmpty() ? null : (MessageLogEntryLite) results.get(0);
        } 
        catch (DataAccessException e) {
            throw new DAOException("Failed to get the message log entry lite by identifier", e);
        }
    }
    
	/**
	 * @see gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO#getByControlId(java.lang.String)
	 */
	public MessageLogEntry getByControlId(String controlIdentifier) throws DAOException {
		try {
			HibernateTemplate tpl = this.getHibernateTemplate();
			List results = tpl.findByNamedQueryAndNamedParam( FIND_BY_CONTROL_IDENTIFIER, 
																			  PARAM_CONTROL_IDENTIFIER,
																			  controlIdentifier );

			// Should only return one value
			return results.isEmpty() ? null : (MessageLogEntry) results.get(0);
		} 
		catch (DataAccessException e) {
			throw new DAOException("Failed to get the message log entry", e);
		}
	}

	/**
	 * @see gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO#getByBatchControlId(java.lang.String)
	 */
	public MessageLogEntry getByBatchControlId(String controlIdentifier) throws DAOException {
		try {
			HibernateTemplate tpl = this.getHibernateTemplate();
			List results = tpl.findByNamedQueryAndNamedParam( FIND_BY_BATCH_CONTROL_IDENTIFIER, 
																			  PARAM_CONTROL_IDENTIFIER,
																			  controlIdentifier );

			// Should only return one value
			return results.isEmpty() ? null : (MessageLogEntry) results.get(0);
		} 
		catch (DataAccessException e) {
			throw new DAOException("Failed to get the message log entry", e);
		}
	}
	
	/**
	 * @see gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO#find(gov.va.med.esr.common.model.person.Person)
	 */
	public List find(Person person) throws DAOException
    {
        try
        {
            HibernateTemplate tpl = this.getHibernateTemplate();
            List results = tpl.findByNamedQueryAndNamedParam(FIND_BY_PERSON,
                    PARAM_PERSON_ID, (BigDecimal) person.getEntityKey()
                            .getKeyValue());
            return results;
        } catch (DataAccessException e)
        {
            throw new DAOException("Failed to load the message log entries", e);
        }
    }

    /**
     * Finds the message log entries for a criteria.
     * 
     * @param criteria The criteria for retrieving the message log entries will be returned.
     * @return A list of message log entries for the specified criteria.
     * @throws DAOException Thrown when a persistence exception occurs.
     */
    public List find(SearchQueryInfo criteria) throws DAOException, MaxRecordsExceededException {
        
        MessageLogCriteria searchCriteria = (MessageLogCriteria)criteria;

		Map contextData = new HashMap();
		contextData.put("searchCriteria", searchCriteria);				        		        	
		HibernateCallback callback = new AbstractDAOAction(contextData) { 
			public Object execute(Session session) throws DAOException {
	            // Build the count and data retrieval queries
	            List paramList = new ArrayList();
	            List valueList = new ArrayList();
	            MessageLogCriteria targetQueryInfo = (MessageLogCriteria) this.getContextData().get("searchCriteria");
	            String whereClause = createWhereClause(targetQueryInfo,paramList,valueList);
	            Query countQuery = session.createQuery("select count(*) from MessageLogEntryLite as logEntry " + whereClause);
	            Query dataQuery = session.createQuery("from MessageLogEntryLite as logEntry " + whereClause);

	            for(int i=0; i<paramList.size(); i++) {
	                countQuery.setParameter((String)paramList.get(i),valueList.get(i));
	                dataQuery.setParameter((String)paramList.get(i),valueList.get(i));
	            }

	            // Execute the query to retrieve the results
	            PaginatedQueryExecutor queryExecutor = new PaginatedQueryExecutor(countQuery,dataQuery,targetQueryInfo);
	            try {
	            	return queryExecutor.executeQuery();
	            } catch(MaxRecordsExceededException e) {
	            	throw new DAOException("max records exceeded", e);
	            }	            
			}
		};
		
		try {
			return this.getHibernateTemplate().executeFind(callback);
		} catch(HibernateSystemException e) {
			MaxRecordsExceededException rootCause = (MaxRecordsExceededException) getRootExceptionOfType(e, MaxRecordsExceededException.class);
			if(rootCause != null)
				throw rootCause;
			throw e;
		}    		
    }
    
    private String createWhereClause(MessageLogCriteria searchCriteria, List paramList, List valueList) {

        //TODO Time portion needs to be fixed.
        Date startDate = null;
        Date endDate = null;
        Calendar cal = Calendar.getInstance();
        if(searchCriteria.getTransmissionDate() != null) {
            startDate = searchCriteria.getTransmissionDate();
            cal.setTime(searchCriteria.getTransmissionDate());
            cal.add(Calendar.DATE,1);
            endDate = cal.getTime();
        } else if(searchCriteria.getTransmissionStartDate() != null && searchCriteria.getTransmissionEndDate() != null) {
            startDate = searchCriteria.getTransmissionStartDate();
            cal.setTime(searchCriteria.getTransmissionEndDate());
            cal.add(Calendar.DATE,1);
            endDate = cal.getTime();
        } 
      
        List criteriaList = new ArrayList();
        if(startDate != null && endDate != null) {
            criteriaList.add(" logEntry.transmissionDate >= :transmissionStartDate and logEntry.transmissionDate <  :transmissionEndDate");
            paramList.add("transmissionStartDate");
            paramList.add("transmissionEndDate");
            valueList.add(startDate);
            valueList.add(endDate);
        }
    
        if(searchCriteria.getPersonId() != null) {
            criteriaList.add("logEntry.personId = :personId");
            paramList.add("personId");
            valueList.add(searchCriteria.getPersonId());
        }
        
        if(searchCriteria.getFacility() != null) {
            criteriaList.add("logEntry.vaFacility = :vaFacility");
            paramList.add("vaFacility");
            valueList.add(searchCriteria.getFacility());
        }

        if(searchCriteria.getType() != null) {
            criteriaList.add("logEntry.type = :type");
            paramList.add("type");
            valueList.add(searchCriteria.getType());
        }

        if(searchCriteria.getStatus() != null) {
            criteriaList.add("logEntry.status = :status");
            paramList.add("status");
            valueList.add(searchCriteria.getStatus());
        }
        
        StringBuffer whereClause = new StringBuffer();
        if(!criteriaList.isEmpty()) {
            whereClause.append(" where ").append(criteriaList.get(0));
            for(int index = 1; index < criteriaList.size(); index++) {
                whereClause.append(" and ").append(criteriaList.get(index));
            }
        }
        return whereClause.toString();
    }
    
	/**
	 * @see gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO#findByReceivedEligiblityStatus(gov.va.med.esr.common.model.person.Person, java.lang.String)
	 */
	public MessageLogEntry findByReceivedEligiblityStatus(Person person, String eligibilityStatus) throws DAOException {
		try {
			
			HibernateTemplate tpl = this.getHibernateTemplate();
			
			String[] params = new String[]{ PARAM_PERSON_ID, PARAM_ELGBTY_STATUS };
			Object[] values = new Object[]{ (BigDecimal)person.getEntityKey().getKeyValue(), eligibilityStatus };
			List results = tpl.findByNamedQueryAndNamedParam( FIND_BY_ELGBTY_STATUS_AND_PERSON, 
													  		  params,
															  values );
			// Should only return one value
			return results.isEmpty() ? null : (MessageLogEntry) results.get(0);
		} 
		catch (DataAccessException e) {
			throw new DAOException("Failed to load the message log entries", e);
		}
	}
	
	/**
	 * @see gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO#findSiteLastTransmittedMsg(gov.va.med.fw.model.EntityKey)
	 */
	public String findSiteLastTransmittedMsg(EntityKey personId) throws DAOException
	{
		try {
			
			HibernateTemplate tpl = this.getHibernateTemplate();
			
			String[] params = new String[]{ PARAM_PERSON_ID};
			Object[] values = new Object[]{ (BigDecimal)personId.getKeyValue()};
			List results = tpl.findByNamedQueryAndNamedParam( 
					FIND_SITE_LAST_TRANSMITTED_MSG, params, values );
			// Should only return one value
			return results.isEmpty() ? null : (String) results.get(0);
		} 
		catch (DataAccessException e) {
			throw new DAOException("Failed to load the message log entries", e);
		}		
	}
	
	/* (non-Javadoc)
	 * @see gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO#findByRetransmissionWaitPeriodAndCount(java.lang.Integer, java.lang.Integer)
	 */
	public List findByRetransmissionWaitPeriodAndCount(
            Integer retransmissionWaitPeriod, Integer retransmissionCount,
            String messageStatusCode, boolean isEGTRetransmission) throws DAOException
    {
        try
        {

            HibernateTemplate tpl = this.getHibernateTemplate();
            String[] params = new String[] { PARAM_MESSAGE_STATUS,
                    PARAM_RETRANSMISSION_WAIT_PERIOD,
                    PARAM_RETRANSMISSION_COUNT,
                    PARAM_MESSAGE_TYPE};
            
            Object[] values = new Object[] { messageStatusCode,
                    retransmissionWaitPeriod, retransmissionCount, MessageType.CODE_MFNZEG_TO_SITE.getCode() };
            
            List results = tpl.findByNamedQueryAndNamedParam(
                    isEGTRetransmission ? 
                            FIND_BY_RETRANSMISSION_WAIT_PERIOD_AND_COUNT_FOR_EGT:FIND_BY_RETRANSMISSION_WAIT_PERIOD_AND_COUNT, 
                            params, values);
            
            return results;
        } catch (DataAccessException e)
        {
            throw new DAOException(
                    "Failed to load the message log entries by retransmission wait period and count",
                    e);
        }

    }
	
	/** Specific query to determine if already processed an inbound message */
	public boolean hasProcessedInboundMessage(String messageControlNumber, String stationNumber) throws DAOException {
		Validate.notNull(messageControlNumber, "NULL messageControlNumber not allowed.");
		Validate.notNull(stationNumber, "NULL stationNumber not allowed.");
		
		boolean hasProcessedInboundMessage = false;

		try
        {
			String[] paramNames = new String[2];
			paramNames[0] = PARAM_CONTROL_IDENTIFIER;
			paramNames[1] = PARAM_STATION_NUMBER;
			Object[] values = new Object[2];
			values[0] = messageControlNumber;
			values[1] = stationNumber;
			List results = findByNamedQueryAndNamedParam(FIND_INBOUND_MESSAGES,
					paramNames, values);

            if (results != null && !results.isEmpty()) {
            	hasProcessedInboundMessage = true;
            }

            return hasProcessedInboundMessage;
		}
        catch (DataAccessException ex)
        {
			throw new DAOException("Failed to execute hasProcessedInboundMessage", ex);
		}
	}
	
    /**
     * find message log entries for VOA that has AA Ack sent, given a person id, in decending datetime order (latest first)
     * 
     * @param identifier person id.
     * @return list of log entries
     * @throws ServiceException
     */	
	public List findVoaAaAckLogEntryByPersonId(EntityKey personId) throws DAOException
	{
        try {
            HibernateTemplate tpl = this.getHibernateTemplate();
            List results = tpl.findByNamedQueryAndNamedParam(FIND_VOA_AA_ACK_BY_PERSON_ID,
                    PARAM_PERSON_ID, (BigDecimal) personId.getKeyValue());
            // Should only return one value
            return results;
        } 
        catch (DataAccessException e) {
            throw new DAOException("Failed to get the VOA AA Ack message log entries by identifier", e);
        }
	}
}