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

//Java Classes
import gov.va.med.esr.common.model.ee.CDCondition;
import gov.va.med.esr.common.model.ee.CDDiagnosis;
import gov.va.med.esr.common.model.ee.CDProcedure;
import gov.va.med.esr.common.model.lookup.AffectedExtremity;
import gov.va.med.esr.common.model.lookup.PermanentIndicator;
import gov.va.med.fw.util.builder.Builder;

import gov.va.med.esr.common.model.ee.CDDescriptor;
import gov.va.med.esr.common.model.ee.CatastrophicDisability;
import gov.va.med.esr.common.model.person.Person;
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.ZCD;
import gov.va.med.fw.model.lookup.AbstractLookup;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.fw.util.builder.BuilderException;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
 
/**
 * Class to build the ZCD segment.
 * 
 * @author Alex Yoon
 * @version 1.0
 */
public class ZCDBuilder extends AbstractSegmentBuilder
{
    /**
    * An instance of serialVersionUID 
    */
   private static final long serialVersionUID = -8940210828115771834L;

    private static final String DETERMINATION_METHOD_CODING_SYSTEM = "VA0041";
    
    private Builder affectedExtremityDescriptionBuilder;
    
    private static final String DIAGNOSIS_CODING_SYSTEM = "ICD DIAGNOSIS";

    private static final String PROCEDURE_CODING_SYSTEM = "ICD OPERATION/PROCEDURE";

    private static final String AFFECTED_EXTREMITY_CODING_SYSTEM = "VA0042";

    private static final String CONDITION_CODING_SYSTEM = "VA0043";

    private static final String PERMANENT_INDICATOR_CODING_SYSTEM = "VA0045";

    private static final String PERMANENT_INDICATOR = "PERMANENT"; 
    private static final String PERMANENT_INDICATOR_CODE = "1";    

    private static final String NOT_PERMANENT_INDICATOR = "NOT PERMANENT";
    private static final String NOT_PERMANENT_INDICATOR_CODE = "2";    

    private static final String UNKNOWN_PERMANENT_INDICATOR = "UNKNOWN";
    private static final String UNKNOWN_PERMANENT_INDICATOR_CODE = "3";    

  

    
    /**
     * Default contructor.
     */
    public ZCDBuilder()
    {
        super();
    }
    /**
     * @return Returns the affectedExtremityDescriptionBuilder.
     */
    public Builder getAffectedExtremityDescriptionBuilder()
    {
        return affectedExtremityDescriptionBuilder;
    }
    /**
     * @param affectedExtremityDescriptionBuilder The affectedExtremityDescriptionBuilder to set.
     */
    public void setAffectedExtremityDescriptionBuilder(
            Builder affectedExtremityDescriptionBuilder)
    {
        this.affectedExtremityDescriptionBuilder = affectedExtremityDescriptionBuilder;
    }
    /**

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

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

        try
        {
            //Get CatastrophicDisability object
            CatastrophicDisability cd = (CatastrophicDisability) getHelperService()
                    .getClinicalDetermination(CatastrophicDisability.class,
                            person);

            //If cd is null, nothing to do.
            if (cd == null)
            {
                addEmptySegment(segments);
            } else
            {
                //Set common variables used for all repeating ZCD segments.
                String reviewDate = DateFormatter
                        .formatDate(cd.getReviewDate());
                String decidedBy = cd.getDeterminedBy();
                String determinationFacility = (cd.getDeterminationFacility() != null) ? cd
                        .getDeterminationFacility().getStationNumber()
                        : null;
                String dateOfDecision = DateFormatter.formatDate(cd.getDecisionDate());
                String determinationMethod = buildCodedElement(cd
                        .getDeterminationMethod(),
                        DETERMINATION_METHOD_CODING_SYSTEM, true);
                String veteranRequestedCDReviewDate = DateFormatter
                        .formatDate(cd.getVeteranRequestedCDReviewDate());
                String facilityInitiatedCDReviewDate = DateFormatter
                        .formatDate(cd.getFacilityInitiatedCDReviewDate());
                String veteranWasNotifiedCDDate = DateFormatter.formatDate(cd
                        .getVeteranWasNotifiedCDDate());
                String disabledInd = super.buildBooleanFor0136(cd.getCatastrophicallyDisabled());

                int setID = 1;
                
                boolean buildCPDFlag = true; // Depends on requirment to decide how to set this flag
                boolean hasCPD = false; // Need to be clean up later.  Temprary code to mitigate a deployment risk 
                
                if(buildCPDFlag) {
                 
                   //  Build ZCD for Diagnosis.
                        Set diagnoses = cd.getDiagnoses();
                        if (diagnoses != null)
                        {
                            Iterator iterDiagnoses = diagnoses.iterator();
                            while (iterDiagnoses.hasNext())
                            {
                                CDDiagnosis diagnosis = (CDDiagnosis) iterDiagnoses
                                        .next();
                                if (diagnosis != null)
                                {
                                    ZCD segment = new ZCD();
                                    
                                    setDefaultValues(segment);
                                    
                                    setCommonAttributes(setID, reviewDate, decidedBy,
                                            determinationFacility,dateOfDecision,
                                            determinationMethod,
                                            disabledInd,veteranRequestedCDReviewDate, 
                                            facilityInitiatedCDReviewDate, 
                                            veteranWasNotifiedCDDate, segment);
                                    segment.setDiagnosis(buildCodedElement(diagnosis
                                            .getDiagnosis(), DIAGNOSIS_CODING_SYSTEM,
                                            false));
        
                                    
                                    if(setID == 1) {
                                       segment.setDescriptor( buildDescriptorsStr(cd));
                                       hasCPD = true;
                                    }
                                    segments.add(segment);
                                    setID += 1;
                                    
                                }
                            }
                        }
        
                        //Build ZCD for Procedure.
                        Set procedures = cd.getProcedures();
                        if (procedures != null)
                        {
                            Iterator iterProcedures = procedures.iterator();
                            while (iterProcedures.hasNext())
                            {
                                CDProcedure procedure = (CDProcedure) iterProcedures
                                        .next();
                                if (procedure != null)
                                {
                                    ZCD segment = new ZCD();
                                    
                                    setDefaultValues(segment);
                                    
                                    setCommonAttributes(setID, reviewDate, decidedBy,
                                            determinationFacility, dateOfDecision, 
                                            determinationMethod,
                                            disabledInd,veteranRequestedCDReviewDate, 
                                            facilityInitiatedCDReviewDate, 
                                            veteranWasNotifiedCDDate, segment);
                                    segment.setProcedure(buildCodedElement(procedure
                                            .getProcedure(), PROCEDURE_CODING_SYSTEM,
                                            false));
                                    segment
                                            .setAffectedExtremity(buildAffectedExtremity(procedure
                                                    .getAffectedExtremity()));
                                    if(setID == 1) {
                                        segment.setDescriptor( buildDescriptorsStr(cd));
                                        hasCPD = true;
                                     }
                                    segments.add(segment);
                                    setID += 1;
                                }
                            }
                        }
        
                        //Build ZCD for Condition.
                        Set conditions = cd.getConditions();
                        if (conditions != null)
                        {
                            Iterator iterConditions = conditions.iterator();
                            while (iterConditions.hasNext())
                            {
                                CDCondition condition = (CDCondition) iterConditions
                                        .next();
                                if (condition != null)
                                {
                                    ZCD segment = new ZCD();
        
                                    setDefaultValues(segment);
                                    
                                    setCommonAttributes(setID, reviewDate, decidedBy,
                                            determinationFacility, dateOfDecision, 
                                            determinationMethod,
                                            disabledInd,veteranRequestedCDReviewDate, 
                                            facilityInitiatedCDReviewDate, 
                                            veteranWasNotifiedCDDate, segment);
                                    segment.setCondition(buildCodedElement(condition
                                            .getCondition(), CONDITION_CODING_SYSTEM,
                                            true));
                                    segment.setScore(String.valueOf(condition
                                            .getScore()));
                                    segment.setPermanentIndicator(buildPermanentIndicator(
                                                    condition
                                                    .getPermanentIndicator()));
                                    
                                    if(setID == 1) {
                                        segment.setDescriptor( buildDescriptorsStr(cd));
                                        hasCPD = true;
                                     }
                                    segments.add(segment);
                                    setID += 1;
                                }
                            }
                        }
                }

                //Build ZCD for Descriptor.
                if(!hasCPD) {
                    Set descriptors = cd.getCDDescriptors();
                    if (descriptors != null && descriptors.size() >0 )
                    {
                        Iterator iterDescriptors = descriptors.iterator();
                        StringBuffer formattedDescriptor = new StringBuffer();
                        int i = 0;
                        while (iterDescriptors.hasNext())
                        {
                            CDDescriptor descriptor = (CDDescriptor) iterDescriptors
                                    .next();
                            if (descriptor != null)
                            {
                                formattedDescriptor.append((i == 0) ? "" : "|");
                                formattedDescriptor.append(descriptor.getDescriptorType().getCode());
                                i++;
                            }
                        }
                        ZCD segment = new ZCD();
                        
                        setDefaultValues(segment);
                        
                        setCommonAttributes(setID, reviewDate, decidedBy,
                                determinationFacility,dateOfDecision,
                                determinationMethod,
                                disabledInd,veteranRequestedCDReviewDate, 
                                facilityInitiatedCDReviewDate, 
                                veteranWasNotifiedCDDate, segment);
                        segment.setDescriptor(formattedDescriptor.toString());
                        segments.add(segment);
                        setID += 1;
                    }

                }
               

                // This means that we do not have any CD data.
                if (setID == 1)
                {
                    addEmptySegment(segments);
                }
            }
        } catch (Exception e)
        {
            throw new BuilderException(
                    "Failed to build ZCD Segment due to an exception ", e);
        }

        return segments;
    }

    /**
     * Method that creats empty ZCD segment and add it to the segment list.
     * 
     * @param segments
     *            The List.
     */
    private void addEmptySegment(List<ZCD> segments)
    {
        ZCD segment = new ZCD();
        segment.setSetID("1");
        setDefaultValues(segment);
        segments.add(segment);
    }

    /**
     * Method that sets common attributes for ZCD segment.
     * 
     * @param setId
     *            The set ID.
     * @param reviewDate
     *            The Catastrophic Disability review date.
     * @param decidedBy
     *            The Catastrophic Disability decided by.
     * @param facility
     *            The Catastrophic Disability decision making facility.
     * @param decisionDate
     *            The Catastrophic Disability decision date.
     * @param method
     *            The Catastrophic Disability determination method.
     * @param disabledInd
     * @param veteranRequestedCDReviewDate
     * @param facilityInitiatedCDReviewDate
     * @param segment
     *            The ZCD segment.
     */
    private void setCommonAttributes(int setId, 
            String reviewDate,
            String decidedBy, 
            String facility, 
            String decisionDate, 
            String method,
            String disabledInd,
            String veteranRequestedCDReviewDate,
            String facilityInitiatedCDReviewDate,
            String veteranWasNotifiedCDDate, ZCD segment)
    {
        segment.setSetID(String.valueOf(setId));
        segment.setReviewDate(buildEmptyIfNull(reviewDate));
        segment.setDecidedBy(buildEmptyIfNull(decidedBy));
        segment.setDeterminationFacility(buildEmptyIfNull(facility));
        segment.setDecisionDate(buildEmptyIfNull(decisionDate));
        segment.setDeterminationMethod(buildEmptyIfNull(method));
        segment.setCatastrophicallyDisabled(buildEmptyIfNull(disabledInd));
        segment.setVeteranRequestedCDReviewDate(buildEmptyIfNull(veteranRequestedCDReviewDate));
        segment.setFacilityInitiatedCDReviewDate(buildEmptyIfNull(facilityInitiatedCDReviewDate));
        segment.setVeteranWasNotifiedCDDate(buildEmptyIfNull(veteranWasNotifiedCDDate));
    }

    /**
     * Method that builds coded element in HL7.
     * 
     * @param lookup
     *            The CodedDescriptionLookup Object.
     * @param codingSystem
     *            The coding system.
     * @param mapCode
     *            Whether to map lookup code or not. True means that need to map
     *            code to trim leading zero.
     * @return coded element.
     */
    private String buildCodedElement(AbstractLookup lookup,
            String codingSystem, boolean mapCode) throws BuilderException
    {
        String codedElement = null;

        if (lookup != null)
        {
            if (mapCode == true)
            {
                codedElement = ElementFormatter.formatCodedElement(buildEmptyIfNull(super
                        .build(lookup)), buildEmptyIfNull(lookup.getDescription()), buildEmptyIfNull(codingSystem));
            } else
            {
                codedElement = ElementFormatter.formatCodedElement(lookup
                        .getCode(), lookup.getDescription(), codingSystem);
            }
        }

        return codedElement;
    }
    
    /**
     * Method that builds CD permanent indicator in Coded Element format.
     * 
     * @param indicator
     *            The Boolean Object.
     * @return CD permanent indicator.
     */
    private String buildPermanentIndicator(PermanentIndicator indicator)
    {
        String permanentIndicator = null;
        
        if (indicator != null)
        {
            if (StringUtils.equals(PermanentIndicator.UNKNOWN.getCode(), indicator.getCode()))
            {
                permanentIndicator = ElementFormatter.formatCodedElement(
                        UNKNOWN_PERMANENT_INDICATOR_CODE,
                        UNKNOWN_PERMANENT_INDICATOR,
                        PERMANENT_INDICATOR_CODING_SYSTEM);
            } else if (StringUtils.equals(PermanentIndicator.PERMANENT.getCode(), indicator.getCode()))
            {
                permanentIndicator = ElementFormatter.formatCodedElement(
                        PERMANENT_INDICATOR_CODE, PERMANENT_INDICATOR,
                        PERMANENT_INDICATOR_CODING_SYSTEM);
            } else
            {
                permanentIndicator = ElementFormatter.formatCodedElement(
                        NOT_PERMANENT_INDICATOR_CODE, NOT_PERMANENT_INDICATOR,
                        PERMANENT_INDICATOR_CODING_SYSTEM);
            }
        }

        return permanentIndicator;
    }

    /**
     * Method that builds Affected Extremity coded element.
     * 
     * @param extremity
     *            The AffectedExtremity Object.
     * @return Affected Extremity codeded element.
     * @throws BuilderException
     */
    private String buildAffectedExtremity(AffectedExtremity extremity) throws BuilderException
    {
        String affectedExtremity = null;

        if (extremity != null)
        {
            affectedExtremity = ElementFormatter.formatCodedElement(
                    extremity == null ? StringUtils.EMPTY : extremity.getCode(), 
                    buildEmptyIfNull((String)affectedExtremityDescriptionBuilder.build(extremity.getCode())), 
                    AFFECTED_EXTREMITY_CODING_SYSTEM);
        }

        return affectedExtremity;
    }

    private String buildDescriptorsStr(CatastrophicDisability cd) 
    {
        Set descriptors = cd.getCDDescriptors();
        StringBuffer formattedDescriptor = new StringBuffer();
        if (descriptors != null && descriptors.size() >0 )
        {
            Iterator iterDescriptors = descriptors.iterator();
            int i = 0;
            while (iterDescriptors.hasNext())
            {
                CDDescriptor descriptor = (CDDescriptor) iterDescriptors
                        .next();
                if (descriptor != null)
                {
                    formattedDescriptor.append((i == 0) ? "" : "|");
                    formattedDescriptor.append(descriptor.getDescriptorType().getCode());
                    i++;
                }
            }
            return formattedDescriptor.toString();
        }
        return StringUtils.EMPTY;
        
    }

      
    protected void setDefaultValues(Segment segment)
    {
        ZCD zcd = (ZCD)segment;
        zcd.setReviewDate(StringUtils.EMPTY);
        zcd.setDecidedBy(StringUtils.EMPTY);
        zcd.setDeterminationFacility(StringUtils.EMPTY);
        zcd.setDecisionDate(StringUtils.EMPTY);
        zcd.setDeterminationMethod(StringUtils.EMPTY);
        zcd.setDiagnosis(StringUtils.EMPTY);
        zcd.setProcedure(StringUtils.EMPTY);
        zcd.setAffectedExtremity(StringUtils.EMPTY);
        zcd.setCondition(StringUtils.EMPTY);
        zcd.setScore(StringUtils.EMPTY);
        zcd.setCatastrophicallyDisabled(StringUtils.EMPTY);
        //Fix for CR_8305 send "" for permanentIndicator when it is null
        //zcd.setPermanentIndicator(StringUtils.EMPTY);
        zcd.setPermanentIndicator(SegmentConstants.DEFAULT_VALUE);
        zcd.setVeteranRequestedCDReviewDate(StringUtils.EMPTY);
        zcd.setFacilityInitiatedCDReviewDate(StringUtils.EMPTY);
        zcd.setVeteranWasNotifiedCDDate(StringUtils.EMPTY);
        zcd.setDescriptor(StringUtils.EMPTY);
    }

}