package gov.va.med.esr.common.builder.msds;

import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.infra.ImpreciseDateUtils;
import gov.va.med.esr.common.model.lookup.ConflictLocation;
import gov.va.med.esr.common.model.lookup.SpecialOperation;
import gov.va.med.esr.jms.vadir.outboundResponse.model.militaryHistory.Deployment;
import gov.va.med.fw.rule.SimpleRuleMethod;
import gov.va.med.fw.util.DateUtils;

/**
 * Implemetation of a building strategy for 
 * Combat Episodes derived from Vadir data.
 * 
 * @author DNS   ruizc
 *
 */
public class VadirCombatEpisodeBuilderStrategy implements
		CombatEpisodeBuilderStrategy {

	private List deployments = new ArrayList();
	private Date combatPayStart = null;
	private Date combatPayEnd = null;
	

	public VadirCombatEpisodeBuilderStrategy() {
		super();

	}	
	

	public String determineConflictLocationCode() {
		/**
		 * BasicStep SUC1013.12.3 Determine Combat Location For Combat Episode
		 * The special operations codes within each deployment contained within
		 * the combat pay date range will be used to determine the combat
		 * location (OEF, OIF, Unknown OEF/OIF, or Conflict Unspecified) based
		 * on the following mapping criteria:
		 * Examine all special operations codes from the VADIR Deployment Records
		 * OTHERWISE IF NO DEPLOYMENTS ---builder strategy default
		 * //AlternateStep SUC1013.4.10 //In Step “Retrieve Deployment Records,”
		 * If a Military Pay Record with a start date after 11/11/1998 and a Pay
		 * Type of Hostile Fire/Imminent Danger or Combat Zone Tax Exclusion is
		 * received but no Deployment Records exist within the combat pay period
		 * THEN 
		 * Set A Combat Episode Start Date equal to the Military Pay Start
		 * Date, 
		 * set the Combat Episode End Date equal to the Military Pay End Date, 
		 * and Combat Location to “Conflict Unspecified” and 
		 * the Use Case resumes at step “Set Source and Pay Type Fields” 
		 * ENDIF
		 */
		List filteredList = this.filterIncomingDeployments(this.getCombatPayStart(), this.getCombatPayEnd(), this.getDeployments());
		if (filteredList == null || filteredList.isEmpty()) {
			return ConflictLocation.CODE_CONFLICT_UNSPECIFIED.getCode();
		}

		List specialOpCodes = new ArrayList();
		for (Iterator itr = filteredList.iterator(); itr.hasNext();) {
			Deployment deployment = (Deployment)itr.next();
			/*
			 * The special operations codes within each deployment contained
			 * within the combat pay date range will be used to determine the
			 * combat location (OEF, OIF, Unknown OEF/OIF, or Conflict
			 * Unspecified)
			 */
			if (deployment.getSpecialOperation() != null){
				specialOpCodes.add(deployment.getSpecialOperation());				
			}
		}

		String NOBLE_EAGLE = SpecialOperation.OPERATION_NOBLE_EAGLE.getCode();
		String IRAQI_FREEDOM = SpecialOperation.OPERATION_IRAQI_FREEDOM.getCode();
		String ENDURING_FREEDOM = SpecialOperation.OPERATION_ENDURING_FREEDOM.getCode();
		
		/*
		 * If all Special operations codes are 09 (Enduring Freedom) OR (there
		 * are multiple codes, one or more of which is 09 and none of the
		 * remaining are 08 or 10) THEN Set Combat Location to “OEF” ENDIF
		 * 
		 */	
		if (specialOpCodes.contains(ENDURING_FREEDOM)
				&& !specialOpCodes.contains(NOBLE_EAGLE)
				&& !specialOpCodes.contains(IRAQI_FREEDOM)) {
			return ConflictLocation.CODE_OEF.getCode();
		}
		
		/*
		 * 
		 * If all Special operations codes are 10 (Iraqi Freedom) OR (there are
		 * multiple codes, one or more of which is 10 and none of the remaining
		 * are 08 or 09) THEN Set Combat Location to “OIF” ENDIF
		 * 
		 */		
		if (specialOpCodes.contains(IRAQI_FREEDOM)
				&& !specialOpCodes.contains(NOBLE_EAGLE)
				&& !specialOpCodes.contains(ENDURING_FREEDOM)) {
			return ConflictLocation.CODE_OIF.getCode();
		}
		
		/*
		 * 
		 * If all Special operations codes are 08 (Noble Eagle) OR (there are
		 * multiple codes, one or more of which is 08 and none of the remaining
		 * are 09, or 10) THEN Set Combat Location to “Conflict Unspecified”
		 * ENDIF
		 * 
		 * 
		 */		
		if (specialOpCodes.contains(NOBLE_EAGLE)
				&& !specialOpCodes.contains(IRAQI_FREEDOM)
				&& !specialOpCodes.contains(ENDURING_FREEDOM)) {
			return ConflictLocation.CODE_CONFLICT_UNSPECIFIED.getCode();
		}
		
		/*
		 * If there are multiple special operations codes and 2 or more special
		 * operations codes 08, 09 or 10 are included THEN Set Combat Location
		 * to “Unknown OEF/OIF” ELSEIF set the Combat Location to “Conflict
		 * Unspecified” ENDIF
		 * 
		 * Notice all the cases where ONE particular code is present and the others
		 * are absent get covered above.
		 */		
		if (specialOpCodes.size() > 1) {
			boolean case1 = specialOpCodes.contains(NOBLE_EAGLE) && 
			specialOpCodes.contains(IRAQI_FREEDOM);
			boolean case2 = specialOpCodes.contains(NOBLE_EAGLE) && 
			specialOpCodes.contains(ENDURING_FREEDOM);
			boolean case3 = specialOpCodes.contains(IRAQI_FREEDOM) && 
			specialOpCodes.contains(ENDURING_FREEDOM);
			
			if (case1 || case2 || case3) {
				return ConflictLocation.CODE_UNKNOWN_OEF_OIF.getCode();				
			}
		}
		// no qualifying Deployment Records exist within the combat pay period
		return ConflictLocation.CODE_CONFLICT_UNSPECIFIED.getCode();
	}

	public ImpreciseDate determineEndDate() {
		/**
		 * BasicStep SUC1013.12 Retrieve Military Pay Records FOREACH For
		 * each of the remaining or combined Military Pay Records,
		 * 
		 * BasicStep SUC1013.12.1 Retrieve Deployment Records 
		 * Retrieve the start date, end date, and special operations code for all Deployment
		 * Records where the Begin Date and End Date of the deployment fall
		 * fully within the Begin Date and End Date of the combat pay of the Pay
		 * Record.
		 * 
		 * BasicStep SUC1013.12.2 Determine Combat Start and End Dates Examine
		 * all Start and End Dates of the retrieved Deployment Records setting a
		 * Combat Start Date to the earliest Deployment Start Date and setting a
		 * Combat End Date to the latest Deployment End Date.
		 * 
		 * AlternateStep SUC1013.4.10 In Step “Retrieve Deployment Records,”
		 * If a Military Pay Record with a start date after 11/11/1998 and a Pay
		 * Type of Hostile Fire/Imminent Danger or Combat Zone Tax Exclusion is
		 * received but no Deployment Records exist within the combat pay period
		 * THEN 
		 * Set A Combat Episode Start Date equal to the Military Pay Start
		 * Date, 
		 * set the Combat Episode End Date equal to the Military Pay End Date, 
 		 */		

		return this.determineEndOrStartDate(this.getDeployments(), 
				this.getCombatPayStart(), this.getCombatPayEnd(), false);
	}

	public ImpreciseDate determineStartDate() {
		/**
		 * BasicStep SUC1013.12 Retrieve Military Pay Records FOREACH For
		 * each of the remaining or combined Military Pay Records,
		 * 
		 * BasicStep SUC1013.12.1 Retrieve Deployment Records 
		 * Retrieve the start date, end date, and special operations code for all Deployment
		 * Records where the Begin Date and End Date of the deployment fall
		 * fully within the Begin Date and End Date of the combat pay of the Pay
		 * Record.
		 * 
		 * BasicStep SUC1013.12.2 Determine Combat Start and End Dates Examine
		 * all Start and End Dates of the retrieved Deployment Records setting a
		 * Combat Start Date to the earliest Deployment Start Date and setting a
		 * Combat End Date to the latest Deployment End Date.
		 * 
		 * AlternateStep SUC1013.4.10 In Step “Retrieve Deployment Records,”
		 * If a Military Pay Record with a start date after 11/11/1998 and a Pay
		 * Type of Hostile Fire/Imminent Danger or Combat Zone Tax Exclusion is
		 * received but no Deployment Records exist within the combat pay period
		 * THEN 
		 * Set A Combat Episode Start Date equal to the Military Pay Start
		 * Date, 
		 * set the Combat Episode End Date equal to the Military Pay End Date, 
 		 */		
		
		return this.determineEndOrStartDate(this.getDeployments(), 
				this.getCombatPayStart(), this.getCombatPayEnd(), true);
	}
	
	private ImpreciseDate determineEndOrStartDate (List deployments, Date payStartDt, Date payEndDt, boolean isStart) {
		Date result = null;
		
		ImpreciseDate defaultDate = isStart ? ImpreciseDateUtils.createImpreciseDateWithoutTime(payStartDt) :
			ImpreciseDateUtils.createImpreciseDateWithoutTime(payEndDt);
		
		List workingList = filterIncomingDeployments(payStartDt, payEndDt, deployments);
		for (Iterator itr = workingList.iterator(); itr.hasNext();) {
			Deployment deployment = (Deployment)itr.next();
			Date startDate = deployment.getStartDate();
			Date endDate = deployment.getEndDate();
			if (startDate == null || endDate == null) {
				continue;
			}
			if (isStart) {
				if (result == null)	result = startDate;
				if (DateUtils.isBeforeIgnoreTime(startDate, result)) result = startDate;				
			} else {
				if (result == null) result = endDate;
				if (DateUtils.isAfterIgnoreTime(endDate, result)) result = endDate;				
			}
		}
		 
		return result != null ? ImpreciseDateUtils.createImpreciseDateWithoutTime(result) : defaultDate;
	}
	private List filterIncomingDeployments(Date payStartDt, Date payEndDt, List incomingDeployments) {
		SimpleRuleMethod util = new SimpleRuleMethod();
		try {
			if (incomingDeployments == null || payStartDt == null ||
					payEndDt == null ||
					DateUtils.isEqualOrBeforeIgnoreTime(payStartDt, util.toDate("11/11/1998"))) {
				return new ArrayList();
			}			
		} catch(ParseException e) {
			// ignore since should not happen but return null
			return new ArrayList();
		}

		List filteredDeployments = new ArrayList();
		for (Iterator itr = incomingDeployments.iterator(); itr.hasNext();) {
			Deployment deployment = (Deployment)itr.next();
			Date startDate = deployment.getStartDate();
			Date endDate = deployment.getEndDate();
			/*
			 * The special operations codes within each deployment contained
			 * within the combat pay date range will be used to determine the
			 * combat location (OEF, OIF, Unknown OEF/OIF, or Conflict
			 * Unspecified)
			 */
			if (startDate != null && endDate != null && 
					isInRange(startDate, endDate, payStartDt, payEndDt)) {
				filteredDeployments.add(deployment);
			}
		}
		return filteredDeployments;
	}
	private boolean isInRange(Date startDate,Date endDate,Date lower_range,Date upper_range) {
		if( startDate != null && endDate != null && lower_range != null &&  upper_range != null && 
				( DateUtils.isEqualOrBeforeIgnoreTime(startDate,endDate) )&& 
				( DateUtils.isEqualOrBeforeIgnoreTime(lower_range, upper_range) )) 
		{
			if( (  DateUtils.isEqualOrAfterIgnoreTime(startDate, lower_range) ) && 
					( DateUtils.isEqualOrBeforeIgnoreTime(startDate, upper_range) ) &&
					( DateUtils.isEqualOrAfterIgnoreTime(endDate, lower_range) ) && 
					( DateUtils.isEqualOrBeforeIgnoreTime(endDate, upper_range) )) 
			{
				return true;
			}
		}
		return false;
	}

	public void setCombatPayEnd(Date combatPayEnd) {
		this.combatPayEnd = combatPayEnd;
	}

	public void setCombatPayStart(Date combatPayStart) {
		this.combatPayStart = combatPayStart;
	}

	public void setDeployments(List deployments) {
		this.deployments = deployments;
	}	  

	private Date getCombatPayEnd() {
		return combatPayEnd;
	}

	private Date getCombatPayStart() {
		return combatPayStart;
	}

	private List getDeployments() {
		return deployments;
	}
	
}
