/*******************************************************************************
 * Copyright © 2004-2005 EDS. All rights reserved
 ******************************************************************************/
package gov.va.med.esr.service.impl;

// Java Classes
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;

import gov.va.med.fw.cache.EntityCacheManager;
import gov.va.med.fw.cache.TriggerEventCacheManager;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.pagination.SearchQueryInfo;
import gov.va.med.fw.service.trigger.TriggerRouter;

import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.messaging.ArchivedMessageLogEntry;
import gov.va.med.esr.common.model.messaging.ArchivedMessageLogEntryLite;
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.messaging.SiteIdentity;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.persistent.messaging.ArchivedMessageLogEntryDAO;
import gov.va.med.esr.common.persistent.messaging.MessageLogEntryDAO;
import gov.va.med.esr.service.ArchivedMessagingService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.UniqueIdentifierGenerator;
import gov.va.med.esr.service.trigger.IncomeYearTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.RetransmitTriggerEvent;

/**
 * Provides business methods to process inbound and outbound messages</br>
 * </br>
 * <b>Project:</b> Common </br>
 * <b>Created on: </b> 3:51:14 PM </br>
 *
 * @author ESR Team
 * @history: cleaned up code format and use entity cache manager - VL
 */
public class ArchivedMessagingServiceImpl extends AbstractRuleAwareServiceImpl
   implements ArchivedMessagingService {
      
   /**
    * An instance of serialVersionUID
    */
   private static final long serialVersionUID = -1936162067559837119L;

   private static final String RETRANSMIT_EVENT = "messaging.retransmit";

   /**
    * An instance of generator 
    */
   private UniqueIdentifierGenerator generator = null;
   
   /**
    * An instance of messageLogEntryDAO 
    */
   private MessageLogEntryDAO messageLogEntryDAO = null;
   
   /**
    * An instance of messageLogEntryDAO 
    */
   private ArchivedMessageLogEntryDAO archivedMessageLogEntryDAO = null;

   /**
    * An instance of triggerRouter 
    */
   private TriggerRouter triggerRouter = null;
   
   /**
    * An instance of triggerEventCacheManager 
    */
   private TriggerEventCacheManager triggerEventCacheManager;
   
   /**
    * An instance of entityCacheManager 
    */
   private EntityCacheManager entityCacheManager;   
   

   /**
    * A default constructor
    */
   public ArchivedMessagingServiceImpl() {
      super();
   }

   /**
    * @return Returns the triggerEventCacheManager.
    */
   public TriggerEventCacheManager getTriggerEventCacheManager() {
      return triggerEventCacheManager;
   }

   /**
    * @param triggerEventCacheManager The triggerEventCacheManager to set.
    */
   public void setTriggerEventCacheManager( TriggerEventCacheManager triggerEventCacheManager ) {
      this.triggerEventCacheManager = triggerEventCacheManager;
   }
    
   public EntityCacheManager getEntityCacheManager() {
      return entityCacheManager;
   }
   
   /**
    * @param entityCacheManager The entityCacheManager to set.
    */
   public void setEntityCacheManager(EntityCacheManager entityCacheManager) {
      this.entityCacheManager = entityCacheManager;
   }
   
   /**
    * @return a TriggerRouter
    */
   public TriggerRouter getTriggerRouter() {
       return triggerRouter;
   }

   /**
    * @param triggerRouter
    */
   public void setTriggerRouter(TriggerRouter triggerRouter) {
       this.triggerRouter = triggerRouter;
   }
   /**
    * @return a UniqueIdentifierGenerator
    */
   public UniqueIdentifierGenerator getGenerator() {
       return this.generator;
   }

   /**
    * @param generator
    */
   public void setGenerator(UniqueIdentifierGenerator generator) {
       this.generator = generator;
   }

   /**
    * @return a MessageLogEntryDAO
    */
   public MessageLogEntryDAO getMessageLogEntryDAO() {
       return this.messageLogEntryDAO;
   }

   /**
    * @param messageLogEntryDAO
    */
   public void setArchivedMessageLogEntryDAO(ArchivedMessageLogEntryDAO archivedMessageLogEntryDAO) {
       this.archivedMessageLogEntryDAO = archivedMessageLogEntryDAO;
   }
   
   /**
    * @return a MessageLogEntryDAO
    */
   public ArchivedMessageLogEntryDAO getArchivedMessageLogEntryDAO() {
       return this.archivedMessageLogEntryDAO;
   }

   /**
    * @param messageLogEntryDAO
    */
   public void setMessageLogEntryDAO(MessageLogEntryDAO messageLogEntryDAO) {
       this.messageLogEntryDAO = messageLogEntryDAO;
   }
   
   
   /**
    * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
    */
   public void afterPropertiesSet() throws Exception {
      super.afterPropertiesSet();     
      Validate.notNull(messageLogEntryDAO, "MessageLogEntryDAO is required");
      Validate.notNull(triggerRouter, "triggerRouter is required");
      Validate.notNull(triggerEventCacheManager, "triggerEventCacheManager is required");
      Validate.notNull(entityCacheManager, "entityCacheManager is required");
   }

   /**
    * Implements method specified by MessagingService interface. (non-Javadoc)
    *
    * @see gov.va.med.esr.service.MessagingService#generateControlIdentifier()
    */
   public String generateControlIdentifier() throws ServiceException {
      return this.generator.generate().toString();
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getIdentity(gov.va.med.esr.common.model.person.Person,
    *      gov.va.med.esr.common.model.lookup.VAFacility)
    */
   public SiteIdentity getIdentity( Person person, VAFacility vaFacility )
      throws ServiceException {
       
      Validate.notNull(vaFacility, "VAFacility can not be null");
      
      SiteIdentity siteId = null;
      
      if( person != null ) {
         
         Set sites = findSitesOfRecord(person);
         for (Iterator iterator = sites.iterator(); iterator.hasNext();) {
            
            siteId = (SiteIdentity) iterator.next();
            if( siteId.getVaFacility().getCode().equals( vaFacility.getCode() ) ) {
               return siteId;
            }
         }
      }
      // no match found from PSIM, so no DFN will be identified from PSIM, 
      // simply create a SiteIdentity with incoming station#, and empty dfn
      siteId = new SiteIdentity();
      siteId.setVaFacility(vaFacility);
      return siteId;
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMatchingTraits(gov.va.med.esr.service.PersonIdentityTraits)
    */
   public Set getMatchingTraits(PersonIdentityTraits identityTraits) 
      throws ServiceException {
       
      return getPsDelegateService().unattendedSearch(identityTraits);
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#logMessage(gov.va.med.esr.common.model.messaging.MessageLogEntry)
    */
   public void logMessage(MessageLogEntry entry) throws ServiceException {

      // Persist the message log entry
      try {
          
         if (entry.getEntityKey() == null) {
            messageLogEntryDAO.saveObject(entry);
         }
         else {
            MessageLogEntry cachedEntry =
               (MessageLogEntry)messageLogEntryDAO.getByKey(entry.getEntityKey());

            cachedEntry.setAckDate(entry.getAckDate());
            cachedEntry.setAckType(entry.getAckType());
            cachedEntry.setErrorText(entry.getErrorText());
            cachedEntry.setInternalErrorText(entry.getInternalErrorText());
            cachedEntry.setStatus(entry.getStatus());
            
            messageLogEntryDAO.saveObject(cachedEntry);
         }
      }
      catch (DAOException ex) {
         throw new ServiceException("Failed to log message ", ex);
      }
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMessageLogEntryById(java.math.BigDecimal)
    */
   public ArchivedMessageLogEntry getMessageLogEntryById( BigDecimal id)
      throws ServiceException {
      
      ArchivedMessageLogEntry mle = null;
      try {    	  
         mle = archivedMessageLogEntryDAO.getById( id );
      } 
      catch( DAOException e ) {
         
         throw new ServiceException("Failed to get message log entry by id " + id, e);
      }
      return mle;
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMessageLogEntryLiteById(java.math.BigDecimal)
    */
   public ArchivedMessageLogEntryLite getMessageLogEntryLiteById( BigDecimal id )
      throws ServiceException {
      
      ArchivedMessageLogEntryLite mle = null;
      try {
         mle = archivedMessageLogEntryDAO.getMessageLogEntryLiteById( id );
      } 
      catch( DAOException e ) {
         
         throw new ServiceException( "Failed to get message log entry lite by id " + id, e );
      }
      return mle;
   }

   /**
    * @see gov.va.med.esr.service.MessagingService#getMessageLogEntry(java.lang.String)
    */
   public ArchivedMessageLogEntry getMessageLogEntry( String id )
      throws ServiceException {
      
      ArchivedMessageLogEntry mle = null;
      try {
         mle = archivedMessageLogEntryDAO.getByControlId( id );
      } 
      catch( DAOException e ) {
         
         throw new ServiceException( "Failed to get message log entry by id " + id, e );
      }
      return mle;
   }

    /**
     * @see gov.va.med.esr.service.MessagingService#getMessageLogEntry(java.lang.String)
     */
    public ArchivedMessageLogEntry getMessageLogEntryByBatchControlNumber( String id ) 
       throws ServiceException {
       
       ArchivedMessageLogEntry mle = null;
       try {
          mle = archivedMessageLogEntryDAO.getByBatchControlId( id );
       } 
       catch( DAOException e ) {
          throw new ServiceException( "Failed to get message log entry by batch id " + id, e );
       }
       return mle;
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#findSitesOfRecord(gov.va.med.esr.common.model.person.Person)
     */
    public Set findSitesOfRecord(Person person) throws ServiceException {
       return person == null ? new HashSet() : getPsDelegateService().getSites( person.getVPIDEntityKey() );
    }

    /**
     * Retrieves the messages matching the criteria.
     *
     * @param criteria the criteria object
     * @return
     * @throws ServiceException
     */
    public List search(SearchQueryInfo criteria) throws ServiceException {       
       List list = null;
       try {
          list = archivedMessageLogEntryDAO.find(criteria);
       }
       catch (DAOException ex) {
          throw new ServiceException("Failed to get the Messages matchong the criteria", ex);
       }
       return list;
    }

    /**
     * Retransmits a message.
     * 
     * @param identifier message log entry id.
     * @throws ServiceException
     */
    public void triggerRetransmission(BigDecimal identifier) throws ServiceException {

        RetransmitTriggerEvent event = new RetransmitTriggerEvent(identifier, true);
        event.setName(RETRANSMIT_EVENT);

        // Process the event
        triggerRouter.processTriggerEvent(event);        
    }
    
    /**
     * @see gov.va.med.esr.service.MessagingService#triggerQRYZ07(gov.va.med.esr.common.model.person.Person, gov.va.med.esr.common.model.lookup.VAFacility, java.lang.Integer, boolean)
     */
    public void triggerQRYZ07( Person person, VAFacility facility, Integer incomeYear, boolean handleAfterCommit) throws ServiceException {

       Validate.notNull(facility, "Facility can not be null");
       Validate.notNull(incomeYear, "Income Year can not be null");
       Validate.notNull(person, "Person can not be null");

       // Create the VAFacility Site List
       Set sites = new HashSet(1);
       sites.add(facility);
       
       // Create the trigger event
       IncomeYearTriggerEvent incomeEvent = 
          new IncomeYearTriggerEvent( PersonTrigger.DestinationType.MESSAGING,
                                      PersonTrigger.TargetType.VISTA,
                                      PersonTrigger.DispatchType.QUERY,
                                      PersonTrigger.DataType.FULL_DATA );

       incomeEvent.setIncomeYear(incomeYear);
       incomeEvent.setPersonId(person.getPersonEntityKey());
       incomeEvent.setIdentityTraits(person.getIdentityTraits());            
       incomeEvent.setTargetSites(sites);

       // Process the event
       if( handleAfterCommit ) {
          Set events = new HashSet(1);
          events.add(incomeEvent);
          triggerEventCacheManager.storeTriggerEvents(events);
       } 
       else {
          triggerRouter.processTriggerEvent(incomeEvent);
       }
    }
    
    /**
     * Trigger a QRYZ07 message instantly
     *
     * @param person The person whose data is being queries
     * @param facility The facility to send the message to
     * @param incomeYear The income year to query on 
     * @throws ServiceException
     */
    public void triggerQRYZ07(Person person, VAFacility facility, Integer incomeYear) throws ServiceException {
       triggerQRYZ07(person, facility, incomeYear, false);
    }

    /**
     * @see gov.va.med.esr.service.MessagingService#findSiteLastTransmittedMsg(gov.va.med.fw.model.EntityKey)
     */
    public VAFacility findSiteLastTransmittedMsg(EntityKey personId) throws ServiceException {
        
       try {
          String siteCode = archivedMessageLogEntryDAO.findSiteLastTransmittedMsg(personId);
          if (siteCode != null){
              return getLookupService().getVaFacilityByCode(siteCode);
          }
          return null;
       } 
       catch (DAOException e) {
          throw new ServiceException( "Failed to Retrieve the Site that last trsmitted the Message",e );
       }
    }    
   
   
	/** Specific check to determine if already processed an inbound message */
	public boolean hasProcessedInboundMessage(String messageControlNumber, String stationNumber) throws ServiceException {
		try {
			return archivedMessageLogEntryDAO.hasProcessedInboundMessage(messageControlNumber, stationNumber);
		} catch(DAOException e) {
			throw new ServiceException("Unable to execute DAO", e);
		}
	} 
	
    /**
     * 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 ServiceException
	{
		try {
			return archivedMessageLogEntryDAO.findVoaAaAckLogEntryByPersonId(personId);
		} catch(DAOException e) {
			throw new ServiceException("Unable to execute DAO", e);
		}
	}   
    
}