// Package 
package gov.va.med.esr.ui.demographic.action;

// Java Classes

// Library Classes
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.lookup.AddressChangeSource;
import gov.va.med.esr.common.model.lookup.CollectionMethod;
import gov.va.med.esr.common.model.lookup.EthnicityType;
import gov.va.med.esr.common.model.lookup.Gender;
import gov.va.med.esr.common.model.lookup.NameType;
import gov.va.med.esr.common.model.lookup.PensionReasonCode;
import gov.va.med.esr.common.model.lookup.PseudoSSNReason;
import gov.va.med.esr.common.model.lookup.RaceType;
import gov.va.med.esr.common.model.lookup.SSAMessage;
import gov.va.med.esr.common.model.lookup.SSAVerificationStatus;
import gov.va.med.esr.common.model.lookup.SSNChangeSource;
import gov.va.med.esr.common.model.lookup.SSNType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.party.Email;
import gov.va.med.esr.common.model.person.BirthRecord;
import gov.va.med.esr.common.model.person.Ethnicity;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.Race;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.service.external.person.collections.RaceCollection;
import gov.va.med.esr.ui.common.service.LookupCacheService;
import gov.va.med.esr.ui.common.util.DateUtils;
import gov.va.med.esr.ui.common.util.JspUtils;
import gov.va.med.esr.ui.conversion.UIConversionServiceImpl;
import gov.va.med.esr.ui.util.ConvertUtils;
import gov.va.med.esr.ui.util.SessionManager;
import gov.va.med.fw.conversion.ConversionServiceException;
import gov.va.med.fw.model.AuditInfo;
import gov.va.med.fw.util.StringUtils;
import gov.va.med.esr.ui.common.service.LookupCacheService;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;

/**
 * Converts from Person to DemographicIdentityTraitsForm and vice versa.
 *
 * @author Andrew Pach
 */
public class DemographicIdentityTraitsConversionService extends UIConversionServiceImpl
{
    /**
     * Converts between a Person object and the DemographicIdentityTraitsForm. It only converts Person to Form and not
     * vice versa.
     *
     * @param source the source object
     * @param target the target object
     *
     * @throws gov.va.med.fw.conversion.ConversionServiceException if any errors were encountered during the
     * conversion.
     */
    protected void convertBean(Object source, Object target) throws ConversionServiceException
    {
        Validate.notNull(source, "DemographicIdentityTraitsConversionService source is null");
        Validate.notNull(source, "DemographicIdentityTraitsConversionService target is null");

        if ((source instanceof Person) && (target instanceof DemographicIdentityTraitsForm))
        {
            convertPersontoForm((Person)source, (DemographicIdentityTraitsForm)target);
        }
        else
        {
            if ((target instanceof Person) && (source instanceof DemographicIdentityTraitsForm))
            {
                convertFormtoPerson((DemographicIdentityTraitsForm)source, (Person)target);
            }
            else
            {
                throw new ConversionServiceException(
                    "Type mismatch: Can't convert from " + source.getClass().getName() + " to "
                        + target.getClass().getName());
            }
        }
    }

    private void doConvertPersontoNonEditableForm(Person person, SubmittedIdentityTraitsForm form)
    throws ConversionServiceException {
    	// specialized behavior...
    	
    	// audit data
    	AuditInfo auditInfo = person.getIdentityTraitsAuditInfo();
    	form.setUpdateStatus(auditInfo.getStatus());
    	form.setUpdateUser(auditInfo.getChangeUser());
    	
        SSN officialSSN = person.getOfficialSsn();
        if (officialSSN != null)
        {
        	// text for SSNSourceOfChange        	
            SSNChangeSource changeSource = officialSSN.getSourceOfChange();
            form.setSsnSourceOfChange((changeSource == null) ? null : changeSource.getDescription());
        	// text for SSAVerificationStatus            
            SSAVerificationStatus verfStatus = officialSSN.getSsaVerificationStatus();
            form.setSsaVerificationStatus((verfStatus == null) ? null : verfStatus.getDescription());
        	// text for SSAMessage            
            SSAMessage ssaMsg = officialSSN.getSsaMessage();
            form.setSsaMessage((ssaMsg == null) ? null : ssaMsg.getDescription());
        	// text for PsuedoSSNReason            
            PseudoSSNReason reason = officialSSN.getPseudoSSNReason();
            form.setPseudoSsnReason((reason == null) ? null : reason.getDescription());    	
        }
    }

    private void convertPersontoForm(Person person, DemographicIdentityTraitsForm form)
		throws ConversionServiceException {
    	doConvertPersontoEditableForm(person, form); // do this first....
    	
    	if(form instanceof SubmittedIdentityTraitsForm)
    		doConvertPersontoNonEditableForm(person, (SubmittedIdentityTraitsForm) form);
    }
		
    
    private void doConvertPersontoEditableForm(Person person, DemographicIdentityTraitsForm form)
        throws ConversionServiceException
    {    	
        try
        {
            // Populate data from the Person object
            Name name = person.getLegalName();
            if (name != null)
            {
                form.setPrefix(name.getPrefix());
                form.setFirstName(name.getGivenName());
                form.setMiddleName(name.getMiddleName());
                form.setLastName(name.getFamilyName());
                form.setSuffix(name.getSuffix());
            }

            SSN officialSSN = person.getOfficialSsn();
            if (form.isAddAPerson()) 
            {
				 for (Iterator iter = person.getSsns().iterator(); iter.hasNext();) {        		 
					 officialSSN = (SSN) iter.next();
					 if (officialSSN != null || !officialSSN.equals(""))
					   break;         // For CCR 10471 Add A Person, it only have one SSN after the search   
				 }
            }
            if (officialSSN != null)
            {
                form.setSsn(JspUtils.displaySSN(officialSSN.getSsnText()));
                SSNChangeSource changeSource = officialSSN.getSourceOfChange();
                form.setSsnSourceOfChange((changeSource == null) ? null : changeSource.getCode());
                SSAVerificationStatus verfStatus = officialSSN.getSsaVerificationStatus();
                form.setSsaVerificationStatus((verfStatus == null) ? null : verfStatus.getCode());
                form.setSsaVerificationDate(DateUtils.format(officialSSN.getSsaVerificationDate(), null));
                SSAMessage ssaMsg = officialSSN.getSsaMessage();
                form.setSsaMessage((ssaMsg == null) ? null : ssaMsg.getDescription());
                PseudoSSNReason reason = officialSSN.getPseudoSSNReason();
                form.setPseudoSsnReason((reason == null) ? null : reason.getCode());
            }

            form.setOtherSSN(JspUtils.displayValues(person.getOtherSsns(), "", null));
            form.setGender(getLookupCode(person.getGender()));
            
            BirthRecord birthRecord = person.getBirthRecord();
            if (birthRecord != null) 
            {
                form.setDateOfBirth(DateUtils.format(birthRecord.getBirthDate(), null));
                form.setBirthCity(birthRecord.getCity());
                form.setBirthState(birthRecord.getState());
             }

            form.setMultipleBirth(ConvertUtils.convertYesNoNoData(getMultipleBirth(person)));
            form.setMothersMaidenName(person.getMothersMaidenName());
            
            VPIDEntityKey vpid = person.getVPIDEntityKey();
            if(vpid != null)
            	form.setVpidValue(vpid.getVPID());          

        	form.setRaceSelections(getRaceTypes(person));     
    
        	boolean isAddAPerson = form.isAddAPerson();
            if (isAddAPerson){
            	form.setEthnicity(getEthnicity(person));
            }
            else {
            	form.setEthnicity(getDisplayEthnicity(person));    
            }
        }
        catch (Exception ex)
        {
            throw new ConversionServiceException("Conversion Failed", ex);
        }
    }

    private void convertFormtoPerson(DemographicIdentityTraitsForm form, Person person)
        throws ConversionServiceException
    {
        LookupCacheService lookupCacheService = getLookupCacheService();
        try
        {
            // Name Information
            Name name = person.getLegalName();
            if (name == null)
            {
                name = new Name();
                name.setType(
                    (NameType)lookupCacheService.getByCodeFromCache(NameType.class, NameType.LEGAL_NAME.getName()));
                person.setLegalName(name);
            }

            name.setPrefix(StringUtils.isBlank(form.getPrefix()) ? null : form.getPrefix());
            name.setGivenName(StringUtils.isBlank(form.getFirstName()) ? null : form.getFirstName());
            name.setMiddleName(StringUtils.isBlank(form.getMiddleName()) ? null : form.getMiddleName());
            name.setFamilyName(StringUtils.isBlank(form.getLastName()) ? null : form.getLastName());
            name.setSuffix(StringUtils.isBlank(form.getSuffix()) ? null : form.getSuffix());
            person.setGender((Gender)lookupCacheService.getByCodeFromCache(Gender.class, form.getGender()));

            // SSN Information
            if (isAnySsnInformationOnForm(form))
            {
                SSN officialSSN = person.getOfficialSsn();
                if (officialSSN == null)
                {
                    // Create a new ssn if it doesn't already exists
                    officialSSN = new SSN();
                    officialSSN.setType(
                        (SSNType)lookupCacheService.getByCodeFromCache(SSNType.class, SSNType.CODE_ACTIVE.getName()));
                    person.addSsn(officialSSN);
                }

                // SSN
                officialSSN.setSsnText(StringUtils.isBlank(SSN.formatSSN(form.getSsn())) ? null :
                    SSN.formatSSN(form.getSsn()));

                // Pseudo SSN Reason
                if (StringUtils.isNotEmpty(form.getPseudoSsnReason()))
                {
                    officialSSN.setPseudoSSNReason((PseudoSSNReason)lookupCacheService.getByCodeFromCache(
                        PseudoSSNReason.class, form.getPseudoSsnReason()));
                }
                else
                {
                    officialSSN.setPseudoSSNReason(null);
                }

                // Source Of Change
                if (StringUtils.isNotEmpty(form.getSsnSourceOfChange()))
                {
                    officialSSN.setSourceOfChange((SSNChangeSource)lookupCacheService.getByCodeFromCache(
                        SSNChangeSource.class, form.getSsnSourceOfChange()));
                }
                else
                {
                    officialSSN.setSourceOfChange(null);
                }

                // SSA Verification Status
                if (StringUtils.isNotEmpty(form.getSsaVerificationStatus()))
                {
                    officialSSN.setSsaVerificationStatus((SSAVerificationStatus)lookupCacheService
                        .getByCodeFromCache(SSAVerificationStatus.class, form.getSsaVerificationStatus()));
                }
                else
                {
                    officialSSN.setSsaVerificationStatus(null);
                }
            }
            else
            {
                SSN officialSSN = person.getOfficialSsn();
                if (officialSSN != null)
                {
                    person.removeSsn(officialSSN);
                }
            }
            setBirthRecord(person, form);
            person.setMothersMaidenName(StringUtils.isBlank(form.getMothersMaidenName()) ? null : form.getMothersMaidenName());
           
            boolean isAddAPerson = form.isAddAPerson();
            if (isAddAPerson){
            	if (StringUtils.isNotEmpty(form.getEthnicity()))
            		setEthnicity(person, form);
             	setRaces(person, form);
            }
       }
        catch (Exception ex)
        {
            throw new ConversionServiceException("Conversion Failed", ex);
        }
    }

    private void setEthnicity(Person person, DemographicIdentityTraitsForm form)throws Exception
    {
    	LookupCacheService lookupCacheService = getLookupCacheService();
    	Ethnicity e = person.getEthnicity();
    	if (e==null){
    		e = new Ethnicity();
    		e.setPerson(person);
    		person.setEthnicity(e);
    	}
    	e.setEthnicityType((EthnicityType)lookupCacheService.getByCodeFromCache(EthnicityType.class, form.getEthnicity()));
    }
    
    private void setRaces(Person person, DemographicIdentityTraitsForm form) throws Exception
    {	
        List<Race> remainingRaces = new ArrayList<Race>();
        RaceType type = null;
        Race formRace = null;
       
    	if (form.getRaceSelections()!=null) {
    		for (int i = 0; i < form.getRaceSelections().length; i++) {
    			type = (RaceType)getLookupCacheService().getByCodeFromCache(RaceType.class, form.getRaceSelections()[i]);
    			formRace = person.getRaceByType(type);
                
                if (formRace == null) {
                	formRace = new Race();
                	formRace.setPerson(person);
                	formRace.setRaceType(type);
                	formRace.setCollectionMethod((CollectionMethod)getLookupCacheService().getByCodeFromCache(CollectionMethod.class, CollectionMethod.CODE_SLF.getCode()));
                }
                remainingRaces.add(formRace);                
    		}
    	}
    	
        // Update the races on the Person
        person.removeAllRaces();
        for (Iterator iterator = remainingRaces.iterator(); iterator.hasNext();)
        {
            Race race = (Race)iterator.next();
            person.addRace(race);
        }

    }
    
    /**
     * Returns whether any SSN information is present on the form.
     *
     * @param form The form
     *
     * @return True if any SSN information is present or false if not.
     */
    protected boolean isAnySsnInformationOnForm(DemographicIdentityTraitsForm form)
    {
        return ((StringUtils.isNotEmpty(SSN.formatSSN(form.getSsn()))) ||
            (StringUtils.isNotEmpty(form.getPseudoSsnReason())) ||
            (StringUtils.isNotEmpty(form.getSsnSourceOfChange())) ||
            (StringUtils.isNotEmpty(form.getSsaVerificationStatus())));
    }
   
    // Multiple Birth Indicator

    private Boolean getMultipleBirth(Person person) {
        BirthRecord birthRecord = person.getBirthRecord();

        // ESR 3.1 CCR9930 -- ESR and MPI has conflicing requirements, so make
        // multiple birth (along with POB city/state, and mothers maiden name) readonly       

        /*if (birthRecord == null)
            return ConvertUtils.NULL_VALUE;
        else
            return ConvertUtils.convertYesNoNoDataString(birthRecord.getMultipleBirth());
        */
        
        // CCR10147 - ADD A PERSON 
        if (birthRecord == null || birthRecord.isMultipleBirth() == null)
        	return null;
        else
        	return birthRecord.isMultipleBirth();
        
      // return birthRecord == null ? null : (birthRecord.getMultipleBirth() == null ? null :  ConvertUtils.convertYesNoNoDataString(birthRecord.getMultipleBirth()));
    }
    
    /** 
     * DOB, Birth City, State, Multiple Birth Indicator
     * @param person
     * @param form
     */
    private void setBirthRecord(Person person, DemographicIdentityTraitsForm form)
    {
        BirthRecord birthRecord = person.getBirthRecord();
        
        if (StringUtils.isNotEmpty(form.getDateOfBirth()) ||
                StringUtils.isNotEmpty(form.getBirthCity()) ||
                StringUtils.isNotEmpty(form.getBirthState()))
        {
            if (birthRecord == null)
            {
                // Create a new record
                birthRecord = new BirthRecord();
                person.setBirthRecord(birthRecord);
            }
            birthRecord.setBirthDate(DateUtils.getImpreciseDate(form.getDateOfBirth()));
            birthRecord.setCity(form.getBirthCity());
            birthRecord.setState(form.getBirthState());
            if (! form.isDisabledMultipleBirthIndicator())
            	setMultipleBirth(person, form.getMultipleBirth());
           // setMultipleBirth(person, form.getMultipleBirth());
         }
        else
        {
            if (birthRecord != null)
            {
                // TODO Check whether we need to delete the date of birth if user deletes it ?
                birthRecord.setBirthDate(null);
                birthRecord.setCity(null);
                birthRecord.setState(null);
                birthRecord.setMultipleBirth(null);
                /*
                if (! form.isDisabledMultipleBirthIndicator())
                	setMultipleBirth(person, form.getMultipleBirth());
                */
            }
        }
 
        // ESR 3.1 CCR9930 -- ESR and MPI has conflicing requirements, so make
        // multiple birth (along with POB city/state, and mothers maiden name) readonly       
        // setMultipleBirth(person, form.getMultipleBirth());         
    }

    private void setMultipleBirth(Person person, String setYes) 
    {
        BirthRecord birthRecord = person.getBirthRecord();
        
        if (setYes == null || setYes.equalsIgnoreCase(ConvertUtils.NULL_VALUE))
        {
            if (birthRecord != null) {
                birthRecord.setMultipleBirth(null);
            }
        }
        else
        {
            if (birthRecord == null)
                birthRecord = new BirthRecord();
        
            birthRecord.setMultipleBirth(ConvertUtils.convertBoolean(setYes));
        
            person.setBirthRecord(birthRecord);
        }
    }
    
    // regular display
    private String getDisplayEthnicity(Person person) {
        return person.getEthnicity()==null ? null : person.getEthnicity().getEthnicityType().getName();
    }
    
    // Race for add a person
    private String[] getRaceTypes(Person person) {
        // Create a list for Races
        Set raceSource = person.getRaces();
        String[] raceTarget = new String[(raceSource.size())];

        Race r = null;
        String type = null;
        int i = 0;
        for (Iterator iter=raceSource.iterator(); iter.hasNext(); ) {
        	r = (Race)iter.next();
        	type = r.getRaceType().getCode();
        	raceTarget[i++] = type;
        }
        return raceTarget;
   }
    
    //  Ethnicity for add a person
    private String getEthnicity(Person person) {
        return person.getEthnicity()==null ? null : person.getEthnicity().getEthnicityType().getCode();
    }

   
}
