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

import gov.va.med.esr.service.PreferredFacilityService;
import gov.va.med.fw.batchprocess.DataFileProcessExecutionContext;
import gov.va.med.fw.batchprocess.DataProcessExecutionContext;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.support.AbstractSpawnedThreadTask;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;

import org.springframework.dao.OptimisticLockingFailureException;

public class PCMMImportProcessSpawnedThreadTask extends
		AbstractSpawnedThreadTask {

	private static final int DEFAULT_OPTIMSTIC_RETRIES = 5;

	private DataFileProcessExecutionContext context;
	private List dataRecords;
	private PreferredFacilityService preferredFacilityService;

	private int optimisticLockRetries = DEFAULT_OPTIMSTIC_RETRIES;
	
	public DataFileProcessExecutionContext getContext() {
		return context;
	}
	public void setContext(DataFileProcessExecutionContext context) {
		this.context = context;
	}

	public int getOptimisticLockRetries() {
		return optimisticLockRetries;
	}
	public void setOptimisticLockRetries(int optimisticLockRetries) {
		this.optimisticLockRetries = optimisticLockRetries;
	}

	public List getDataRecords() {
		return dataRecords;
	}
	public void setDataRecords(List dataRecords) {
		this.dataRecords = dataRecords;
	}
	
	public PreferredFacilityService getPreferredFacilityService() {
		return preferredFacilityService;
	}
	
	public void setPreferredFacilityService(PreferredFacilityService preferredFacilityService) {
		this.preferredFacilityService = preferredFacilityService;
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.service.support.SpawnedThreadTask#execute()
	 */
	public void executeTask() throws Throwable {
		if (logger.isDebugEnabled()) {
			logger.debug("PCMMImportProcessSpawnedThreadTask started");
		}
		
		int numberOfRetries;
		boolean optimisticLockSuccess;
		
		for (int i = 0; dataRecords != null && i < dataRecords.size(); i++) {
			if (context.isInterrupted()) {
				break;
			}

			try {
				PCMMImportData dataRecord = (PCMMImportData) dataRecords.get(i);

				if (logger.isDebugEnabled()) {
					logger.debug("Processing dataRecord #" + i);
				}
				
				numberOfRetries = optimisticLockRetries;
				optimisticLockSuccess = false;
			
				while (numberOfRetries > 0 && !optimisticLockSuccess) {
				
					try {
						preferredFacilityService.processPCMMData(dataRecord);
						optimisticLockSuccess = true;
						context.getProcessStatistics().incrementNumberOfSuccessfulRecords();
					} catch (OptimisticLockingFailureException ole) {
						numberOfRetries--;
						if (logger.isDebugEnabled()) {
							logger.debug("Encountered OptimisticLockingFailureException, # of retries remaining = " + numberOfRetries);
						}
						if (numberOfRetries == 0) {
							// out of re-tries, rethrow exception
							throw new ServiceException("Exhausted number of retries (" + optimisticLockRetries + ") for OptimisticLockingFailureException.");
						}
					} 
				} 
			}
			catch (Exception e) {
				handleFailure(context, dataRecords.get(i), e);
			}
		}
		
		PCMMImportProcess.adjustTaskCount(
				context, -1);
		Object threadCreator = getThreadCreator(context);
		synchronized (threadCreator) {
			threadCreator.notifyAll();
		}
		if (logger.isDebugEnabled()) {
			logger.debug("PCMMImportProcessSpawnedThreadTask ended, 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(
						PCMMImportProcess.CONTEXT_THREAD_CREATOR);
	}
	
	private void handleFailure(DataFileProcessExecutionContext context,
			Object dataRecord, Exception e) {
		String exceptionText = null;
		if (e == null) {
			exceptionText = "Error: Unable to process PCMM data: " + dataRecord;
		}
		else {
				exceptionText = "Error: Unable to process PCMM data: "
						+ dataRecord + " because of exception: " + e;		
		}
		if (logger.isErrorEnabled()) {
			logger.error(exceptionText);
		}
		context.getProcessStatistics().incrementNumberOfErrorRecords();
		context.getExceptionData().add(dataRecord);
		context.getExceptionData().add(exceptionText + "\n\n");
		
        if ( e != null )
        {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            String trackTrace =  sw.toString();
            context.getExceptionData().add(trackTrace);
        }
	}
	
	
	
}
