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


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

import gov.va.med.esr.common.model.lookup.CombatPayType;
import gov.va.med.esr.jms.vadir.outboundResponse.model.militaryHistory.MilitaryPay;
import gov.va.med.fw.rule.SimpleRuleMethod;

/**
 * Implementation of a building strategy for 
 * Combat Services derived from Vadir data.
 * 
 * @author DNS   ruizc
 *
 */
public class VadirCombatServiceBuilderStrategy implements
		CombatServiceBuilderStrategy {

	
	public List determineMilitaryPayToKeep(List militaryPays) {
		List processingList = new ArrayList();
		if (militaryPays == null) {
			return processingList;
		}

		// Phase 1
		for (Iterator itr = militaryPays.iterator(); itr.hasNext();) {
			MilitaryPay pay = (MilitaryPay)itr.next();
			Date start = pay.getStartDate();
			Date end = pay.getEndDate();
			String payType =  pay.getPayType();
			if (start == null || end == null || payType == null) continue; // ignore for this purpose
			/*
			 * BasicStep SUC1013.9 Create Combat Episodes From Military Pay and
			 * Combat Episodes 
			 * FOREACH Military Pay Records received from the Broker
			 * --IF start date after 11/11/1998 and Pay Type of Hostile Fire/Imminent
			 * -----Danger or Combat Zone Tax Exclusion and End Date that is not NULL THEN
			 * ---add to processing list 
			 * --ENDIF 
			 * ENDFOR
			 */
			try {
				SimpleRuleMethod util = new SimpleRuleMethod();
				if (util.isAfter(start, util.toDate("11/11/1998"))) {
					if (CombatPayType.CODE_COMBAT_ZONE_TAX_EXCLUSION.getCode().equals(payType) ||
							CombatPayType.CODE_HOSTILE_FIRE_IMMINENT_DANGER.getCode().equals(payType)) {
						processingList.add(pay);						
					}
				}
			} catch(ParseException e) {
				// ignore since should not happen
			}
		}

		// Check if need to continue
		if (processingList.isEmpty()){
			return processingList; 
		}
		
		List processingList2 = new ArrayList();		
		List toRemove = new ArrayList();
		for (Iterator itr = processingList.iterator(); itr.hasNext();) {
			// By the time we reach this point, Pay records have all needed
			// values. The idea is to fill processingList2 with data from
			// processingList. The resulting data will have overlaps resolved.
			MilitaryPay pay = (MilitaryPay)itr.next();
			/*
			 * BasicStep SUC1013.10 Examine Military Pay Records’ Dates of Same
			 * Pay Types
			 * 
			 * FOREACH pay type of same pay type 
			 * --IF Military Pay Records has start/end dates overlap THEN 
			 * ----When an overlap is encountered, combine the multiple Military
			 * ----Pay Records that overlap into a single Military Pay Record with the
			 * ----start date of the earlier start date and an end date with the latest
			 * ----end date. 
			 * --ENDIF 
			 * ENDFOR
			 */
			List overLapped = getOverlappingMiltaryPaySameType(pay, processingList);
			if (overLapped.size() > 0) {
				combineMilitaryPays(pay, overLapped, toRemove);
			}
			if (!toRemove.contains(pay)) {
				processingList2.add(pay);				
			}
		}		

		
		/*
		 * 
		 * BasicStep SUC1013.11 Examine Military Pay Records’ Date of
		 * Different Types
		 * 
		 * FOREACH Military Pay Records 
		 * --IF any start/end dates overlap THEN 
		 * ----retain record with a pay type of “Combat Zone Tax
		 * ----Exclusion” and ignore the other 
		 * --ENDIF 
		 * ENDFOR
		 */
		List processingListFinal = new ArrayList();		
		toRemove.clear(); // reuse
		for (Iterator itr = processingList2.iterator(); itr.hasNext();) {
			// By the time we reach this point, Pay records have all needed
			// values. The idea is to fill processingList2 with data from
			// processingList. The resulting data will have overlaps resolved.
			MilitaryPay pay = (MilitaryPay)itr.next();
			/*
			 * BasicStep SUC1013.11 Examine Military Pay Records’ Date of
			 * Different Types
			 * 
			 * FOREACH Military Pay Records 
			 * --IF any start/end dates overlap THEN 
			 * ----retain record with a pay type of “Combat Zone Tax
			 * ----Exclusion” and ignore the other 
			 * --ENDIF 
			 * ENDFOR

			 */
			List overLapped = getOverlappingMiltaryPay(true, pay, processingList);
			if (overLapped.size() > 0) {
				resolveMilitaryPayOverlaps(pay, overLapped, toRemove);
			}
			if (!toRemove.contains(pay)) {
				processingListFinal.add(pay);				
			}
		}		

		return processingListFinal;
	}

	private void combineMilitaryPays(MilitaryPay pay, List overLapped, List toRemove) {
		Date minStart = pay.getStartDate();
		Date maxEnd = pay.getEndDate();
		SimpleRuleMethod util = new SimpleRuleMethod(); 
		for (Iterator itr = overLapped.iterator(); itr.hasNext();) {
			MilitaryPay overLap = (MilitaryPay)itr.next();
			boolean remove = false;

			if (overLap.getStartDate() == null || overLap.getEndDate() == null) continue;
			
			if (util.isBefore(overLap.getStartDate(), minStart)) {
				minStart = overLap.getStartDate();
				remove = true;
			}
			if (util.isAfter(overLap.getEndDate(), maxEnd)) {
				maxEnd = overLap.getEndDate();
				remove = true;				
			}
			if (remove) {
				toRemove.add(overLap);
			}
		}
		
		pay.setStartDate(minStart);
		pay.setEndDate(maxEnd);
	}
	
	
	private void resolveMilitaryPayOverlaps(MilitaryPay pay, List overLapped, List toRemove) {
 
		for (Iterator itr = overLapped.iterator(); itr.hasNext();) {
			MilitaryPay overLap = (MilitaryPay)itr.next();
			if (!CombatPayType.CODE_COMBAT_ZONE_TAX_EXCLUSION.getCode().equals(overLap.getPayType()) &&
					CombatPayType.CODE_COMBAT_ZONE_TAX_EXCLUSION.getCode().equals(pay.getPayType())) {
				toRemove.add(overLap);				
			}
		}
	}	
	
	private boolean isOverlapped(Date startDate, 
			Date endDate, 
			Date lower_range, 
			Date upper_range ) {

		boolean overlapped = false;

		if (startDate != null && endDate != null && lower_range != null
				&& upper_range != null) {
			// If the start = lower or end = upper or start = upper or end =
			// lower
			if (startDate.equals(lower_range) || startDate.equals(upper_range)
					|| endDate.equals(lower_range)
					|| endDate.equals(upper_range)) {
				overlapped = true;
			}
			// start < lower < upper < end
			else if (startDate.before(lower_range)
					&& lower_range.before(upper_range)
					&& upper_range.before(endDate)) {
				overlapped = true;
			}
			// start < lower < end < upper
			else if (startDate.before(lower_range)
					&& lower_range.before(endDate)
					&& endDate.before(upper_range)) {
				overlapped = true;
			}
			// lower < start < upper < end
			else if (lower_range.before(startDate)
					&& startDate.before(upper_range)
					&& upper_range.before(endDate)) {
				overlapped = true;
			}
			// lower < start < end < upper
			else if (lower_range.before(startDate) && startDate.before(endDate)
					&& endDate.before(upper_range)) {
				overlapped = true;
			}
		}
		return overlapped;
	}

	private List getOverlappingMiltaryPaySameType(MilitaryPay militaryPay, List pays) {
		return this.getOverlappingMiltaryPay(false, militaryPay, pays);
	}		
	
	private List getOverlappingMiltaryPay(boolean ignoreType, MilitaryPay militaryPay, List pays) {
		List overLapList = new ArrayList();

		if (militaryPay == null || (!ignoreType && militaryPay.getPayType() == null))
			return overLapList;

		// Loop through a collection of conflicts
		for( Iterator i = pays.iterator(); i.hasNext(); ) {

			MilitaryPay pay = (MilitaryPay)i.next();

			// This check is to skip the same record and for a collection with
			// only one record of pay
			if( militaryPay == pay || (!ignoreType && pay.getPayType() == null)) {
				continue;
			}

			// Get a start and end date
			Date lower_range = pay.getStartDate();
			Date upper_range = pay.getEndDate();
			Date startDate = militaryPay.getStartDate();
			Date endDate = militaryPay.getEndDate();
			if(!ignoreType) {
				if (militaryPay.getPayType().equals(pay.getPayType()) && 
						this.isOverlapped(startDate,endDate,lower_range,upper_range )){                    
					overLapList.add(pay);
				}				
			}
			else {
				if (this.isOverlapped(startDate,endDate,lower_range,upper_range )){                    
					overLapList.add(pay);
				}								
			}

		}        
		return overLapList;
	}	
}
