/********************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ********************************************************************/
//Package
package gov.va.med.esr.messaging.builder.message;

//Java Classes
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import gov.va.med.fw.hl7.Segment;
import gov.va.med.fw.hl7.builder.HL7MetaData;
import gov.va.med.fw.hl7.constants.SegmentConstants;
import gov.va.med.fw.hl7.segment.ZEL;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.fw.util.builder.Builder;
import gov.va.med.fw.util.builder.BuilderException;
import gov.va.med.esr.common.model.ee.AgentOrangeExposure;
import gov.va.med.esr.common.model.ee.CampLejeuneVerification;
import gov.va.med.esr.common.model.ee.Eligibility;
import gov.va.med.esr.common.model.ee.EligibilityVerification;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.ee.EnvironmentalContaminationExposure;
import gov.va.med.esr.common.model.ee.MilitaryService;
import gov.va.med.esr.common.model.ee.MilitarySexualTrauma;
import gov.va.med.esr.common.model.ee.MonetaryBenefit;
import gov.va.med.esr.common.model.ee.MonetaryBenefitAward;
import gov.va.med.esr.common.model.ee.RadiationExposure;
import gov.va.med.esr.common.model.ee.SpecialFactor;
import gov.va.med.esr.common.model.lookup.EligibilityType;
import gov.va.med.esr.common.model.lookup.Indicator;
import gov.va.med.esr.common.model.lookup.MonetaryBenefitType;
import gov.va.med.esr.common.model.person.Person;

/**
 * Class to build the ZEL segment.
 *
 * @author Alex Yoon
 * @version 1.0
 */
public class ZELBuilder extends AbstractSegmentBuilder {
	/**
    * An instance of serialVersionUID
    */
   private static final long serialVersionUID = 8788812161779825904L;

   private static final String INDICATOR_YES = "1";

   private static final String INDICATOR_NO = "0";

   private Builder indicatorBuilder;

	/**
	 * Default contructor.
	 */
	public ZELBuilder() {
		super();
	}


	/**
     * @return Returns the indicatorBuilder.
     */
    public Builder getIndicatorBuilder()
    {
        return indicatorBuilder;
    }
    /**
     * @param indicatorBuilder The indicatorBuilder to set.
     */
    public void setIndicatorBuilder(Builder indicatorBuilder)
    {
        this.indicatorBuilder = indicatorBuilder;
    }


	/**
	 * Method to build the ZEL segment.
	 *
	 * @param metaData
	 *            The parameter object used to build the a ZEL segment.
	 * @return The ZEL segment.
	 */
	public Object build(HL7MetaData metaData, boolean includeCLV) throws BuilderException {
		if ((metaData == null) || (metaData.getEntity() == null)
				|| !(metaData.getEntity() instanceof Person)) {
			throw new BuilderException(
					"Invalid input parameter to build a segment");
		}

		List segments = new ArrayList();
		Person person = (Person) metaData.getEntity();

		try {
			// Set segment attributes
			ZEL primarySegment = new ZEL();
			primarySegment.setSetID(INDICATOR_YES);

			if (person == null) {
				segments.add(primarySegment);
			} else {
				// Build ZEL Set 1 - Primary Eligibility
			    setPrimaryEligibility(person, primarySegment, includeCLV);

				segments.add(primarySegment);

				EnrollmentDetermination enrollmentDetermination = person
						.getEnrollmentDetermination();
				if (enrollmentDetermination != null) {
					Set secondaryEligibilities = enrollmentDetermination
							.getSecondaryEligibilities();

					if (secondaryEligibilities != null) {
						Iterator iterEligibilities = secondaryEligibilities
								.iterator();

						// Primary Eligibility is Set ID 1
						// Secondary Eligibility starts from Set ID 2 and on.
						int setID = 2;
						while (iterEligibilities.hasNext()) {
							Eligibility eligibility = (Eligibility) iterEligibilities
									.next();
							if ((eligibility != null)
									&& (eligibility.getType() != null)) {
								ZEL segment = new ZEL();

								// Set segment attributes
								segment.setSetID(String.valueOf(setID));
								segment
										.setEligibilityCode(buildEligibilityCode(eligibility
												.getType()));
								segments.add(segment);
								setID += 1;
							}
						}
					}
				}
			}
		} catch (Exception e) {
			throw new BuilderException(
					"Failed to build ZEL Segment due to an exception ", e);
		}

		return segments;
	}


	/**
	 * 6,7,23,24,25,34 and 35 will not be defaulted to double quotes. Send them as empty if null
	 */
	protected void setDefaultValues(Segment segment, boolean includeCLV) {
		ZEL zel = (ZEL) segment;

		zel.setDisabilityRetirementFromMilitary(SegmentConstants.DEFAULT_VALUE);

		zel.setVeteran(SegmentConstants.DEFAULT_VALUE);
		zel.setEligibilityStatus(SegmentConstants.DEFAULT_VALUE);
		zel.setEligibilityStatusDate(SegmentConstants.DEFAULT_VALUE);
		zel.setEligibilityVerificationMethod(SegmentConstants.DEFAULT_VALUE);

		//Do not set default values for indicators(seq 14-20). Some of the indicators
		//could have values of UNKNOWN which needs to be send as null and not double quotes

		zel.setTotalAnnualVaCheckAmount(SegmentConstants.DEFAULT_VALUE);
		zel.setRadiationExposureMethod(SegmentConstants.DEFAULT_VALUE);
		zel.setAgentOrangeExposureLocation(SegmentConstants.DEFAULT_VALUE);
		zel.setCombatVeteranEligibilityIndicator(SegmentConstants.DEFAULT_VALUE);
		zel.setCombatVeteranEligibilityEndDate(SegmentConstants.DEFAULT_VALUE);
		zel.setDischargeDueToDisability(SegmentConstants.DEFAULT_VALUE);
		zel.setShadIndicator(SegmentConstants.DEFAULT_VALUE);

		if(includeCLV) {
			zel.setCampLejeuneIndicator(StringUtils.EMPTY);
	    	zel.setCampLejeuneRegDate(StringUtils.EMPTY);
			zel.setCampLejeuneChangeSite(StringUtils.EMPTY);
			zel.setCampLejeuneSourceOfChange(StringUtils.EMPTY);
		}
	}

	/**
	 * Method that sets ZEL Set 1 - Primary Eligibility.
	 *
	 * @param person
	 *            The Person Object.
	 * @param zel
	 *            The ZEL segment.
	 */
	private void setPrimaryEligibility(Person person, ZEL segment, boolean includeCLV)
			throws Exception {
		setDefaultValues(segment, includeCLV);

		// Set ClaimFolderNumber, ClaimFolderLocation, UserEnrolleeValidThrough,
		// UserEnrolleeSite, and EligibilityCode
		this.setBeneficiary(person, segment);

		// Set Eligibility Status
		this.setEligibility(person, segment);

		// Set DisabilityRetirementFromMilitary,
		// CombatVeteranEligibilityIndicator,
		// and CombatVeteranEligibilityEndDate
		this.setMilitaryService(person, segment);

		// Set ReceivingAABenefits, ReceivingHouseboundBenefits(),
		// ReceivingVaPension, ReceivingVaDisability, and
		// TotalAnnualVaCheckAmount
		this.setMonetaryBenefit(person, segment);

		// Set AgentOrangeExposure, RadiationExposure,
		// EnvironmentalContaminants,
		// RadiationExposureMethod, AgentOrangeRegistrationDate,
		// AgentOrangeExamDate,
		// AgentOrangeRegistrationNumber, AgentOrangeExposureLocation,
		// RadiationRegistrationDate, EnvironmentalContaminantsExamDate,
		// and EnvironmentalContaminantsRegistrationDate
		// Set CampLejeuneVerification accordingly per the system parameter
		this.setSpecialFactors(person, segment, includeCLV);

		// Set EligibilityVerificationMethod and status date
		this.setEligibilityVerification(person, segment);

		// Set MstStatus, MstStatusChangeDate, and MstStatusDeterminingSite
		this.setMilitarySexualTrauma(person, segment);

		// Set Veteran Indicator
		segment.setVeteran(buildBooleanForVA001(person.isVeteran()));

		segment.setShadIndicator(person.getShad() == null ? null
                : buildBooleanFor0136FromIndicator(person.getShad()
                        .getShadIndicator()));

	}

	/**
	 * Method that sets ClaimFolderNumber, ClaimFolderLocation,
	 * UserEnrolleeValidThrough, UserEnrolleeSite, and EligibilityCode
	 *
	 * @param person
	 *            The Person Object.
	 * @param segment
	 *            The ZEL segment.
	 */
	private void setBeneficiary(Person person, ZEL segment) throws Exception {
		String claimFolderNumber = null;
		String claimFolderLocation = null;
		String userEnrolleeValidThru = null;
		String userEnrolleeSite = null;
		String eligibilityCode = null;

		if (person != null) {
			claimFolderNumber = person.getClaimFolderNumber();

			if (claimFolderNumber != null) {
				claimFolderLocation = person.getClaimFolderLocation() == null ? null
						: person.getClaimFolderLocation().getStationNumber();
			}

			if (person.getUserEnrolleeValidThrough() != null) {
				userEnrolleeValidThru = person.getUserEnrolleeValidThrough()
						.toString();
				// userEnrolleeValidThru is four digit year, pad with four
				// zeros.
				if (userEnrolleeValidThru.length() == 4) {
					userEnrolleeValidThru += "0000";
				} else { // Invalid year
					userEnrolleeValidThru = null;
				}
			}

			if (person.getUserEnrolleeSite() != null) {
				userEnrolleeSite = person.getUserEnrolleeSite()
						.getStationNumber();
			}

			// Set Primary Eligibility
			EnrollmentDetermination determination = person
					.getEnrollmentDetermination();
			if (determination != null) {
				Eligibility primaryEligilibilty = determination
						.getPrimaryEligibility();

				if (primaryEligilibilty != null) {
					eligibilityCode = buildEligibilityCode(primaryEligilibilty
							.getType());
				}
			}
		}

		//Set empty string if null
		segment.setClaimFolderNumber(buildEmptyIfNull(claimFolderNumber));
		segment.setClaimFolderLocation(buildEmptyIfNull(claimFolderLocation));
		segment.setUserEnrolleeValidThrough(buildEmptyIfNull(userEnrolleeValidThru));
		segment.setUserEnrolleeSite(buildEmptyIfNull(userEnrolleeSite));

		segment.setEligibilityCode(eligibilityCode);

	}

	/**
	 * Method that sets Eligibility Status
	 *
	 * @param person
	 *            The Person Object.
	 * @param segment
	 *            The ZEL segment.
	 */
	private void setEligibility(Person person, ZEL segment) throws Exception {
		String eligStatus = null;
		EligibilityVerification eligibilityVerification = person
				.getEligibilityVerification();

		// Assuming that a person might not have an eligibility verification and
		// status
		if (eligibilityVerification != null
				&& eligibilityVerification.getEligibilityStatus() != null) {
			eligStatus = eligibilityVerification.getEligibilityStatus()
					.getCode();
		}
		segment.setEligibilityStatus(eligStatus);
	}

	/**
	 * Method that sets DisabilityRetirementFromMilitary,
	 * CombatVeteranEligibilityIndicator, and CombatVeteranEligibilityEndDate
	 *
	 * @param person
	 *            The Person Object.
	 * @param segment
	 *            The ZEL segment.
	 */
	private void setMilitaryService(Person person, ZEL segment)
			throws Exception {
		String disabilityRetirementIndicator = null;
		String combatVetEligibilityInd = null;
		String combatVetEligibilityEndDate = null;
		String dischargeDueToDisability = null;

		MilitaryService militaryService = person.getMilitaryService();
		if (militaryService != null) {
			disabilityRetirementIndicator = buildBooleanFor0136(militaryService
					.getDisabilityRetirementIndicator());

			Date cvEligibilityEndDate = militaryService
					.getCombatVeteranEligibilityEndDate();
			if (cvEligibilityEndDate != null) {
				combatVetEligibilityEndDate = DateFormatter
						.formatDate(cvEligibilityEndDate);
			}
			Boolean dischargeDueToDisabilityBoolean = militaryService
					.getDischargeDueToDisability();
			dischargeDueToDisability = buildBooleanFor0136(dischargeDueToDisabilityBoolean);
		}

		segment
				.setDisabilityRetirementFromMilitary(disabilityRetirementIndicator);
		segment.setCombatVeteranEligibilityIndicator(combatVetEligibilityInd);
		segment.setCombatVeteranEligibilityEndDate(combatVetEligibilityEndDate);
		segment.setDischargeDueToDisability(dischargeDueToDisability);
	}

	/**
	 * Method that sets ReceivingAABenefits, ReceivingHouseboundBenefits(),
	 * ReceivingVaPension, ReceivingVaDisability, and TotalAnnualVaCheckAmount
	 *
	 * @param person
	 *            The Person Object.
	 * @param segment
	 *            The ZEL segment.
	 */
	private void setMonetaryBenefit(Person person, ZEL segment)
			throws Exception {
		String receivingAABenefits = null;
		String receivingHouseboundBenefits = null;
		String receivingVaPension = null;
		String receivingVaDisability = null;
		String totalCheckAmount = null;

		MonetaryBenefitAward mbAward = person.getMonetaryBenefitAward();

		if (mbAward != null) {
			if (mbAward.getCheckAmount() != null) {
				totalCheckAmount = mbAward.getCheckAmount().toString();
			}

			Set monetaryBenefits = mbAward.getMonetaryBenefits();

			receivingAABenefits = getMonetaryBenefitIndicatorByType(
                    monetaryBenefits,
                    MonetaryBenefitType.CODE_AID_AND_ATTENDANCE);
            receivingHouseboundBenefits = getMonetaryBenefitIndicatorByType(
                    monetaryBenefits, MonetaryBenefitType.CODE_HOUSEBOUND);
            receivingVaPension = getMonetaryBenefitIndicatorByType(
                    monetaryBenefits, MonetaryBenefitType.CODE_VA_PENSION);
            receivingVaDisability = getMonetaryBenefitIndicatorByType(
                    monetaryBenefits,
                    MonetaryBenefitType.CODE_DISABILITY_COMPENSATION);
		}

		segment.setReceivingAABenefits(receivingAABenefits);
		segment.setReceivingHouseboundBenefits(receivingHouseboundBenefits);
		segment.setReceivingVaPension(receivingVaPension);
		//If receivingVaDisability is null, that means the value is UNKNOWN.
		//Do not set a null value because a double quote will be sent. The other
		//monetary benefit indicators will have values of Yes or No only
		if(receivingVaDisability != null)
		    segment.setReceivingVaDisability(receivingVaDisability);
		segment.setTotalAnnualVaCheckAmount(totalCheckAmount);
	}

	/**
	 * If no MonetaryBenefits, or Monetary Benefit Indicator by that type is null or
	 * if Indicator is No, then send NO (0).
	 * If Indicator is Yes, then send YES(1).
	 * If Indicator is Unknown, send null.
	 *
	 * @param monetaryBenefits
	 * @param type
	 * @return
	 * @throws BuilderException
	 */
	private String getMonetaryBenefitIndicatorByType(Set monetaryBenefits,
            MonetaryBenefitType.Code type) throws BuilderException
    {
        String indicator = INDICATOR_NO;
        if (monetaryBenefits != null)
        {
            for (Iterator iter = monetaryBenefits.iterator(); iter.hasNext();)
            {
                MonetaryBenefit monetaryBenefit = (MonetaryBenefit) iter.next();
                if (type.getName().equals(monetaryBenefit.getType().getCode()))
                {
                    Indicator mbIndicator = monetaryBenefit
                            .getMonetaryBenefitIndicator();
                    //If null, then use default value of NO
                    if (mbIndicator != null)
                    {
                        indicator = super.build(indicatorBuilder,
                                mbIndicator);
                    }
                }

            }
        }


	    return indicator;
	}


	/**
	 * Method that sets AgentOrangeExposure, RadiationExposure,
	 * EnvironmentalContaminants, RadiationExposureMethod, AgentOrangeExposureLocation,
	 * RadiationRegistrationDate, EnvironmentalContaminantsExamDate, and
	 * EnvironmentalContaminantsRegistrationDate
	 *
	 * @param person
	 *            The Person Object.
	 * @param segment
	 *            The ZEL segment.
	 */
	private void setSpecialFactors(Person person, ZEL segment, boolean includeCLV) throws Exception {
		String agentOrangeInd = INDICATOR_NO;
		String agentOrangeExpLocation = null;
		String radiationExpInd = INDICATOR_NO;
		String radiationExpMethod = null;
		String envContaminantsInd = INDICATOR_NO;
		String campLejeuneInd = INDICATOR_NO;
		String campLejeuneRegDate = null;
		String campLejeuneChangeSite = null;
		String campLejenueChangeSource = null;

		Set specialFactors = person.getSpecialFactors();

		if (specialFactors != null) {
			AgentOrangeExposure agentOrange = null;
			RadiationExposure radiation = null;
			EnvironmentalContaminationExposure envCont = null;
			CampLejeuneVerification campLejeune = null;

			Iterator iterSpecialFactors = specialFactors.iterator();
			while (iterSpecialFactors.hasNext()) {
				SpecialFactor theSpecialFactor = (SpecialFactor) iterSpecialFactors
						.next();

				//For all indicators, set value to No if there are no special factors or if there is no
				//specific special factor like AgentOrangeExposure or if the Indicator is No.
				if (theSpecialFactor != null) {
					if (theSpecialFactor instanceof AgentOrangeExposure) {
						agentOrange = (AgentOrangeExposure) theSpecialFactor;

						if(agentOrange != null)
						{
					        agentOrangeInd = buildIndicator(agentOrange.getAgentOrangeExposureIndicator());
						    agentOrangeExpLocation = (agentOrange.getLocation() != null) ? agentOrange
									.getLocation().getCode()
									: null;
						}
					} else if (theSpecialFactor instanceof RadiationExposure) {
						radiation = (RadiationExposure) theSpecialFactor;

						if (radiation != null) {
							radiationExpInd = buildIndicator(radiation.getRadiationExposureIndicator());
							radiationExpMethod = (radiation.getExposureMethod() != null) ? radiation.getExposureMethod().getCode() : null;
						}

					}  else if (theSpecialFactor instanceof EnvironmentalContaminationExposure) {
						envCont = (EnvironmentalContaminationExposure) theSpecialFactor;

						if (envCont != null) {
							envContaminantsInd = buildIndicator(envCont.getEnvironmentalContaminationExposureIndicator());
						}

					  } else if ((theSpecialFactor instanceof CampLejeuneVerification) && includeCLV) {
					        campLejeune = (CampLejeuneVerification) theSpecialFactor;

					        if (campLejeune != null && campLejeune.getSpecialFactorIndicator() != null && (campLejeune.getSpecialFactorIndicator().getCode().equals(Indicator.YES.getCode()) || campLejeune.getSpecialFactorIndicator().getCode().equals(Indicator.NO.getCode()))) {
								campLejeuneInd = buildIndicator(campLejeune.getSpecialFactorIndicator());
								campLejeuneRegDate = (campLejeune.getRegistrationDate() != null) ? campLejeune.getRegistrationDate().getyyyyMMddFormat() : null;
								campLejeuneChangeSite = (campLejeune.getChangeSite() != null) ? campLejeune.getChangeSite().getStationNumber() : null;
								campLejenueChangeSource = (campLejeune.getChangeSource() != null) ? campLejeune.getChangeSource().getName() : null;
								segment.setCampLejeuneIndicator(campLejeuneInd);
								segment.setCampLejeuneRegDate(campLejeuneRegDate);
								segment.setCampLejeuneChangeSite(campLejeuneChangeSite);
								segment.setCampLejeuneSourceOfChange(campLejenueChangeSource);
						    }
					        else {
					        	segment.setCampLejeuneIndicator(StringUtils.EMPTY);
					        	segment.setCampLejeuneRegDate(StringUtils.EMPTY);
								segment.setCampLejeuneChangeSite(StringUtils.EMPTY);
								segment.setCampLejeuneSourceOfChange(StringUtils.EMPTY);
					        }

				      }

				}
			}
		}

		segment.setAgentOrangeExposure(agentOrangeInd);
		segment.setRadiationExposure(radiationExpInd);
		segment.setEnvironmentalContaminants(envContaminantsInd);
		segment.setRadiationExposureMethod(radiationExpMethod);
		segment.setAgentOrangeExposureLocation(agentOrangeExpLocation);


	}

	private String buildIndicator(Indicator indicator) throws BuilderException
	{
	    String indicatorValue = INDICATOR_NO;

		if(indicator != null)
	    {
		    indicatorValue = super.build(indicatorBuilder, indicator);
	    }

	    return indicatorValue;
	}

	/**
	 * Method that sets EligibilityVerificationMethod
	 *
	 * @param person
	 *            The Person Object.
	 * @param segment
	 *            The ZEL segment.
	 */
	private void setEligibilityVerification(Person person, ZEL segment)
			throws Exception {
		String eligVeriMethod = null;
		String statusDate = null;
		String eligVerifSource = null;
		EligibilityVerification verification = person
				.getEligibilityVerification();

		if (verification != null) {
			eligVeriMethod = verification.getVerificationMethod();

			eligVerifSource = verification.getVerificationSource()== null ? null :
				verification.getVerificationSource().getDescription();

			statusDate = DateFormatter.formatDate(verification
					.getEligibilityStatusDate());

		}

		//Add if eligVerifSource or eligVeriMethod is present.
		if(eligVerifSource != null || eligVeriMethod != null)
		{
			StringBuffer eligibilityVerificationMethod =
				new StringBuffer(eligVerifSource == null ? StringUtils.EMPTY:eligVerifSource).append("/").
					append(eligVeriMethod == null ? StringUtils.EMPTY: eligVeriMethod);
			segment.setEligibilityVerificationMethod(eligibilityVerificationMethod.toString());
		}
		segment.setEligibilityStatusDate(statusDate);
	}

	/**
	 * Method that sets MstStatus, MstStatusChangeDate, and
	 * MstStatusDeterminingSite
	 *
	 * @param person
	 *            The Person Object.
	 * @param segment
	 *            The ZEL segment.
	 */
	private void setMilitarySexualTrauma(Person person, ZEL segment)
			throws Exception {
		String mstStatus = null;
		String mstStatusChangeDate = null;
		String mstStatusSite = null;

		// Get MilitarySexualTrauma object
		MilitarySexualTrauma mst = (MilitarySexualTrauma) getHelperService()
				.getClinicalDetermination(MilitarySexualTrauma.class, person);

		if (mst != null) {
			mstStatus = (mst.getStatus() != null) ? mst.getStatus().getCode() : null;
            //ESR_CodeCR5659:MST and NTR did not upload on Vista due to not sending the time (just the date)
			mstStatusChangeDate = DateFormatter.formatDate(mst.getStatusChangeDate(),DateFormatter.DATETIME_ZONE_FORMAT);
			mstStatusSite = (mst.getDeterminationFacility() != null) ? mst
					.getDeterminationFacility().getStationNumber() : null;
		}

		//Set empty string if null
		segment.setMstStatus(mstStatus == null ? StringUtils.EMPTY : mstStatus);
		segment.setMstStatusChangeDate(mstStatusChangeDate == null ? StringUtils.EMPTY : mstStatusChangeDate);
		segment.setMstStatusDeterminingSite(mstStatusSite == null ? StringUtils.EMPTY : mstStatusSite);
	}

	/**
	 * Method that builds eligibility code.
	 *
	 * @param type
	 *            The EligibilityType Object.
	 * @return eligibility code.
	 */
	private String buildEligibilityCode(EligibilityType type)
			throws BuilderException {
		return super.build(type);
	}
}