package gov.va.med.esr.common.batchprocess;

import java.math.BigDecimal;
import java.util.List;

import org.apache.commons.lang.Validate;


import gov.va.med.esr.common.model.lookup.SeedStatus;
import gov.va.med.esr.common.model.messaging.SeedingLogEntry;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.PersonIdEntityKeyImpl;
import gov.va.med.esr.common.persistent.messaging.SeedingLogEntryDAO;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.MilitaryInfoService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.fw.batchprocess.DataProcessExecutionContext;
import gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.support.AbstractSpawnedThreadTask;

public class GenerateQRYZ21ProcessSpawnedThreadTask extends
AbstractSpawnedThreadTask{

	private DataQueryProcessExecutionContext context;

	private BigDecimal acquiredData;
	
	private PersonService personService;

	private SeedingLogEntryDAO seedingLogEntryDAO = null;
    private LookupService lookupService = null;
    private MilitaryInfoService militaryService = null;
    
    private String processName;

	public String getProcessName() {
		return processName;
	}

	public void setProcessName(String processName) {
		this.processName = processName;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.service.support.SpawnedThreadTask#execute()
	 */
	public void executeTask() throws Throwable {
		
		PersonEntityKey key = new PersonIdEntityKeyImpl(acquiredData);		
		try {
			militaryService.processSeeding(getPersonService().getPerson(key));              
            context.getProcessStatistics().incrementNumberOfSuccessfulRecords();
            createSeedingLogEntry(acquiredData,SeedStatus.COMPLETE.getCode());            
         } catch (Exception ex) {
             context.getProcessStatistics().incrementNumberOfErrorRecords();
             String errMsg = "Error while executing " + getProcessName() + " for person "
                             + acquiredData;
             context.getExceptionData().add(errMsg+ " Reason: "+ ex.getMessage());                
				createSeedingLogEntry(acquiredData,SeedStatus.ERROR.getCode());
				
             if(logger.isWarnEnabled())
                 logger.warn(errMsg + " Reason: ", ex);
         }finally {
			if (logger.isDebugEnabled()) {
				logger.debug("GenerateQRYZ21ProcessSpawnedThreadTask ended");
			}
			GenerateQRYZ21Process.adjustTaskCount(
					context, -1);
			Object threadCreator = getThreadCreator(context);
			synchronized (threadCreator) {
				threadCreator.notifyAll();
			}
			if (logger.isDebugEnabled()) {
				logger.debug("GenerateQRYZ21ProcessSpawnedThreadTask notified creator");
			}
			
			/*
			 * force release of context as thay will hold the ThreadPool(->Threads)
			 * this is necessary to GC ThreadPool threads at end
			 */
			context = null;
		}
         
         
	}
	
	private Object getThreadCreator(DataProcessExecutionContext context) {
		return context
				.getContextData()
				.get(
						GenerateQRYZ21Process.CONTEXT_THREAD_CREATOR);
	}
	
	/**
	 * @return Returns the acquiredData.
	 */
	public BigDecimal getAcquiredData() {
		return acquiredData;
	}

	/**
	 * @param acquiredData
	 *            The acquiredData to set.
	 */
	public void setAcquiredData(BigDecimal acquiredData) {
		this.acquiredData = acquiredData;
	}

	/**
	 * @return Returns the context.
	 */
	public DataQueryProcessExecutionContext getContext() {
		return context;
	}

	/**
	 * @param context
	 *            The context to set.
	 */
	public void setContext(DataQueryProcessExecutionContext context) {
		this.context = context;
	}
	
	/**
	 * @return Returns the personService.
	 */
	public PersonService getPersonService() {
		return this.personService;
	}

	/**
	 * @param personService The personService to set.
	 */
	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}
	
	/**
	 * @return a SeedingLogEntryDAO
	 */
	public SeedingLogEntryDAO getSeedingLogEntryDAO() {
		return this.seedingLogEntryDAO;
	}

	/**
	 * @param messageLogEntryDAO
	 */
	public void setSeedingLogEntryDAO(SeedingLogEntryDAO seedingLogEntryDAO) {
		this.seedingLogEntryDAO = seedingLogEntryDAO;
	}
	
	/**
     * @return Returns the lookupService.
     */
    public LookupService getLookupService() {
        return lookupService;
    }

    /**
     * @param lookupService
     *           The lookupService to set.
     */
    public void setLookupService(LookupService lookupService) {
        this.lookupService = lookupService;
    }
    
    /**
	 * @return Returns the militaryService.
	 */
	public MilitaryInfoService getMilitaryService() {
		return this.militaryService;
	}

	/**
	 * @param militaryService The militaryService to set.
	 */
	public void setMilitaryService(MilitaryInfoService militaryService) {
		this.militaryService = militaryService;
	}
	
	private void updateSeedingLogEntry(BigDecimal person,String code)throws ServiceException {

		SeedingLogEntry sle = (SeedingLogEntry)getSeedingLogEntrybyPerson(person).get(0);
		sle.setOriginatingProcess("BatchProcess");		
		sle.setStatus(getLookupService().getSeedStatusByCode(code));
		try {
			seedingLogEntryDAO.saveObject(sle);			
		} catch (DAOException ex) {
			throw new ServiceException("Failed to get seedingDAO  ", ex);
		}		

	}
	
	private void createSeedingLogEntry(BigDecimal key, String code) {
    	try{  	
        	
        	List personLogEntry = getSeedingLogEntrybyPerson(key);
        	if(personLogEntry != null && !personLogEntry.isEmpty()){
        		updateSeedingLogEntry(key, code);    		
        	}
        	else{
        		SeedingLogEntry sle = new SeedingLogEntry();
        		sle.setOriginatingProcess("BatchProcess");    		
        		sle.setPersonId(key);
        		sle.setStatus(getLookupService().getSeedStatusByCode(code));		
        		logMessage(sle);    		
        	}
        	} catch (ServiceException e){
        		logger.error("Error with seeding table insert and update:" + key);
        	}
		
	}
	
	private List getSeedingLogEntrybyPerson(BigDecimal person)throws ServiceException{

		List seedingLogEntry = null;
		try {
			seedingLogEntry = seedingLogEntryDAO.findPersonId(person);			
		} catch (DAOException ex) {
			throw new ServiceException("Failed to log seeding  ", ex);
		}		
		return seedingLogEntry;		
	}
	
	/**
	 * @see gov.va.med.esr.service.MessagingService#logMessage(gov.va.med.esr.common.model.messaging.MessageLogEntry)
	 */
	public void logMessage(SeedingLogEntry entry) throws ServiceException {

		// Persist the seeding log entry
		try {

			if (entry.getEntityKey() == null) {
				seedingLogEntryDAO.saveObject(entry);
			}
		}
		catch (DAOException ex) {
			throw new ServiceException("Failed to log message ", ex);
		}
	}
	
	public void afterPropertiesSet() {        
        Validate.notNull(militaryService, "A MilitaryService is needed");
        Validate.notNull(personService, "A MilitaryService is needed");
        Validate.notNull(seedingLogEntryDAO, "A seedingLogEntryDAO is needed");
        Validate.notNull(lookupService, "A lookupService is needed");
    }

}
