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

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Map;

import gov.va.med.fw.util.builder.Builder;
import gov.va.med.fw.util.builder.BuilderException;
import gov.va.med.esr.common.builder.entity.EntityBuilder;
import gov.va.med.esr.common.builder.entity.ActivationMetaData;
import gov.va.med.esr.common.builder.entity.CombatServiceMetaData;
import gov.va.med.esr.common.builder.entity.CombatEpisodeMetaData;
import gov.va.med.esr.common.builder.msds.MilitaryServiceMetaDataMSDS;
import gov.va.med.esr.common.model.ee.MilitaryService;
import gov.va.med.esr.common.model.ee.Activation;
import gov.va.med.esr.common.model.ee.CombatService;
import gov.va.med.esr.common.model.ee.CombatEpisode;
import gov.va.med.esr.service.MsdsResponseInfo;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.jms.vadir.outboundResponse.VadirServiceResponseWrapper;
import gov.va.med.esr.jms.vadir.outboundResponse.model.militaryHistory.MilitaryHistory;
import gov.va.med.esr.jms.vadir.outboundResponse.model.militaryHistory.VADIR;
import gov.va.med.esr.jms.vadir.outboundResponse.model.militaryHistory.BIRLS;

/**
 * This builder is meant specifically for MSDS. It builds using non-hl7
 * meta data from the broker. It delegates building of common objects 
 * to the MilitaryServiceBuilder and only builds objects that interest 
 * MSDS rules. Inheriting from MilitaryServiceBuilder is not used because 
 * it confuses Spring when autowiring Junit tests.
 * 
 * 
 * @author DNS   ruizc
 *
 */
public class MilitaryServiceBuilderForMSDS extends EntityBuilder {
	/**
	 * 
	 */
	private static final long serialVersionUID = -5564709731817935860L;
	
    public static final String YES_INDICATOR = "Y";
    public static final String NO_INDICATOR = "N";	
	private Map responseStatusMap;
	private Builder combatEpisodeBuilder;	
    private Builder combatServiceBuilder;
    private Builder activationBuilder;
    private Builder militaryServiceBuilder;    

    public MsdsResponseInfo build(VadirServiceResponseWrapper wrapper) throws BuilderException {
    	if (wrapper == null) {
    		return null;
    	}

    	MsdsResponseInfo info = new MsdsResponseInfo();
    	MilitaryHistory history = (wrapper != null && wrapper.getResult() instanceof MilitaryHistory) ?
    			(MilitaryHistory)wrapper.getResult() : null;

    	if (history != null) {
    		VADIR vadir = history.getVadir();
    		BIRLS birls = history.getBirls();

    		if (vadir != null) {
    			MilitaryServiceMetaDataMSDS metaData = new MilitaryServiceMetaDataFromVADIR(wrapper);
    			if (MsdsResponseInfo.INVALID_CHARACTER_OF_SERVICE_VADIR.equals(metaData.getMetaDataError())) {
    				info.setMsdsReceivedStatus(MsdsResponseInfo.INVALID_CHARACTER_OF_SERVICE_VADIR);
    				info.setVadirError(MsdsResponseInfo.INVALID_CHARACTER_OF_SERVICE_VADIR);
    				// Just continue and collect data even though it won't be used
    			}
    			
    			// Delegate to existing builder				
    			MilitaryService output = (MilitaryService)this.getMilitaryServiceBuilder().build(metaData);
    			// Handle the MSDS specific transfers
    			this.transfer(output, metaData);
    			info.setVadirMilitaryService(this.shouldKeep(output) ? output : null);
    			info.setOefoifInd(vadir.getOefOifIndicator());
    			// Do not transfer Disability ind
    			//info.setHasDisabilityRetirement(Boolean.valueOf(vadir.getRetirementStatusIndicator()).booleanValue());
    		}

    		if (birls != null) {
    			MilitaryServiceMetaDataMSDS metaData = new MilitaryServiceMetaDataFromBIRLS(wrapper);
    			if (MsdsResponseInfo.INVALID_CHARACTER_OF_SERVICE_BIRLS.equals(metaData.getMetaDataError())) {
    				info.setMsdsReceivedStatus(MsdsResponseInfo.INVALID_CHARACTER_OF_SERVICE_BIRLS);
    				info.setBirlsError(MsdsResponseInfo.INVALID_CHARACTER_OF_SERVICE_BIRLS);
    				// Just continue and collect data even though it won't be used    				
    			}
    			
    			// Delegate to existing builder				
    			MilitaryService output = (MilitaryService)this.getMilitaryServiceBuilder().build(metaData);
    			// Handle the MSDS specific transfers
    			this.transfer(output, metaData);
    			info.setBirlsMilitaryService(this.shouldKeep(output) ? output : null);
    			// Do not transfer Disability ind    			
    			//info.setHasDisabilityRetirement(Boolean.valueOf(birls.getDisabilityInd()).booleanValue());
    			info.setHasPurpleHeart(this.buildIndicator(birls.getPurpleHeartInd()));
    			info.setHasMedalOfHonor(this.buildIndicator(birls.getMedalOfHonorInd()));
    		}

    		
    	}

    	if (info.getMsdsReceivedStatus() == null) {
    		// Check low level status first - it has priority
    		String status = (history != null) ? 
    				(String)this.getResponseStatusMap().get(history.getReturnStatus()) : null;
    		if (status == null && wrapper.getErrorMessage() != null) {
        		// Check high level status - this part translates from VADIR/BIRLS world to ESR world    			
    			status = (String)this.getResponseStatusMap().get(wrapper.getErrorMessage());
    		}
        	info.setMsdsReceivedStatus(status);    		
    	}
    	
    	info.setPersonEntityKey(CommonEntityKeyFactory.createPersonIdEntityKey(String.valueOf(wrapper.getPersonId())));
    	
    	return info;
    }    

    private void transfer(MilitaryService input,
    		MilitaryServiceMetaDataMSDS metaData) throws BuilderException {

    	// Transfer CV end date?
    	
      	buildActivations(input, metaData.getActivations());
    	buildCombatServices(input, metaData.getCombatServices());
    	buildCombatEpisodes(input, metaData.getCombatEpisodes());
    }
    
    private void buildActivations(MilitaryService input, ActivationMetaData[] metaData) 
    	throws BuilderException {
    	Set toAdd = new HashSet();

		for (int index = 0; index < ((metaData == null) ? 0 : metaData.length); index++) {
			this.processActivation(toAdd, metaData[index]);
		}    	
		input.removeAllActivations();

		for (Iterator i = toAdd.iterator(); i.hasNext();) {
			input.addActivation((Activation) i.next());
		}    	
    }
    
	private void processActivation(Set toAdd,
			ActivationMetaData metaData) throws BuilderException {

		Activation value = this.buildActivation(null, metaData);

		if (value != null) {
			toAdd.add(value);
		}
	}    
    
	private Activation buildActivation(Activation input,
			ActivationMetaData metaData) throws BuilderException {
		if (metaData == null) {
			return null;
		}

		metaData.setEntity(input);
		return (Activation) this.getActivationBuilder().build(metaData);

	}	
	
	private void buildCombatServices(MilitaryService input, 
			CombatServiceMetaData[] metaData) throws BuilderException {
		Set toAdd = new HashSet();

		for (int index = 0; index < ((metaData == null) ? 0 : metaData.length); index++) {
			this.processCombatService(toAdd, metaData[index]);
		}    	
		input.removeAllCombatServices();

		for (Iterator i = toAdd.iterator(); i.hasNext();) {
			input.addCombatService((CombatService) i.next());
		}    	
	}

	private void buildCombatEpisodes(MilitaryService input, 
			CombatEpisodeMetaData[] metaData) throws BuilderException {
		Set toAdd = new HashSet();

		for (int index = 0; index < ((metaData == null) ? 0 : metaData.length); index++) {
			this.processCombatEpisode(toAdd, metaData[index]);
		}    	
		input.removeAllCombatEpisodes();

		for (Iterator i = toAdd.iterator(); i.hasNext();) {
			input.addCombatEpisode((CombatEpisode) i.next());
		}    	
	}	
	
	private void processCombatService(Set toAdd,
			CombatServiceMetaData metaData) throws BuilderException {
		CombatService value = this.buildCombatService(null, metaData);

		if (value != null) {
			toAdd.add(value);
		}
	}    
	private void processCombatEpisode(Set toAdd,
			CombatEpisodeMetaData metaData) throws BuilderException {
		CombatEpisode value = this.buildCombatEpisode(null, metaData);

		if (value != null) {
			toAdd.add(value);
		}
	}    
	
	private CombatService buildCombatService(CombatService input,
			CombatServiceMetaData metaData) throws BuilderException {
		if (metaData == null) {
			return null;
		}

		metaData.setEntity(input);
		return (CombatService) this.getCombatServiceBuilder().build(metaData);
	}	
	private CombatEpisode buildCombatEpisode(CombatEpisode input,
			CombatEpisodeMetaData metaData) throws BuilderException {
		if (metaData == null) {
			return null;
		}

		metaData.setEntity(input);
		return (CombatEpisode) this.getCombatEpisodeBuilder().build(metaData);
	}	    
	
    /**
	 * @return the activationBuilder
	 */
	public Builder getActivationBuilder() {
		return activationBuilder;
	}
	/**
	 * @param activationBuilder the activationBuilder to set
	 */
	public void setActivationBuilder(Builder activationBuilder) {
		this.activationBuilder = activationBuilder;
	}
	/**
	 * @return the combatServiceBuilder
	 */
	public Builder getCombatServiceBuilder() {
		return combatServiceBuilder;
	}
	/**
	 * @param combatServiceBuilder the combatServiceBuilder to set
	 */
	public void setCombatServiceBuilder(Builder combatServiceBuilder) {
		this.combatServiceBuilder = combatServiceBuilder;
	}

	/**
	 * @return the militaryServiceBuilder
	 */
	public Builder getMilitaryServiceBuilder() {
		return militaryServiceBuilder;
	}

	/**
	 * @param militaryServiceBuilder the militaryServiceBuilder to set
	 */
	public void setMilitaryServiceBuilder(Builder militaryServiceBuilder) {
		this.militaryServiceBuilder = militaryServiceBuilder;
	}

	public Builder getCombatEpisodeBuilder() {
		return combatEpisodeBuilder;
	}

	public void setCombatEpisodeBuilder(Builder combatEpisodeBuilder) {
		this.combatEpisodeBuilder = combatEpisodeBuilder;
	}

	/**
	 * @return the responseStatusMap
	 */
	public Map getResponseStatusMap() {
		return responseStatusMap;
	}

	/**
	 * @param responseStatusMap the responseStatusMap to set
	 */
	public void setResponseStatusMap(Map responseStatusMap) {
		this.responseStatusMap = responseStatusMap;
	}
	
	private Boolean buildIndicator(String ind) {
		if (ind != null && !" ".equals(ind)) {
			if (YES_INDICATOR.equalsIgnoreCase(ind)) return Boolean.TRUE;
			if (NO_INDICATOR.equalsIgnoreCase(ind)) return Boolean.FALSE;
		}
		return null;
	}
}
