/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.messaging.service.inbound;

// Java Classes

// Library Classes

// Framework Classes
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import gov.va.med.fw.hl7.InvalidMessageException;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.hl7.constants.SegmentConstants;
import gov.va.med.fw.hl7.segment.PID;
import gov.va.med.fw.hl7.segment.ZEL;
import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.service.InvalidArgumentException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.StringUtils;

import gov.va.med.esr.common.builder.datatype.metadata.ST;
import gov.va.med.esr.common.builder.datatype.metadata.XPN;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonSearchQueryInfo;
import gov.va.med.esr.service.PersonService;

/**
 * @author Martin Francisco
 */
public class PersonFinderFromVBA extends AbstractComponent implements PersonFinder
{
    private PersonService personService;
    private LookupService lookupService;

    public PersonFinderFromVBA()
    {
        super();
    }

    public PersonService getPersonService()
    {
        return this.personService;
    }

    public void setPersonService(PersonService personService)
    {
        this.personService = personService;
    }

    /*
     * (non-Javadoc)
     *
     * @see gov.va.med.esr.messaging.service.inbound.PersonFinder#find(gov.va.med.fw.hl7.Message)
     */
    public Person find(Message message) throws ServiceException
    {
        try
        {
            PID pid = (PID)message.getSegment(SegmentConstants.PID);
            String ssn = pid.getSSN();
            String dateOfBirth = pid.getDOB();
            String gender = pid.getSex();
            String icn = pid.getPrimaryID();

            String unformatedSsn = StringUtils.trimToNull(SSN.formatSSN(ssn));
            icn = StringUtils.trimToNull(icn);

            XPN[] names = XPN.create(pid.getPatientName(), pid.getRepeatDelimiter(), pid.getComponentDelimiter());
            XPN name = ((names == null) || (names.length == 0)) ? null : names[0];
            
            String givenName = (name != null) ? this.getValue(name.getGivenName()) : null;
            String familyName = (name != null) ? this.getValue(name.getFamilyName()) : null;

            String cfn = null;
            ZEL zel = (ZEL)message.getSegment(SegmentConstants.ZEL);
            if(zel != null)
                cfn = zel.getClaimFolderNumber();

            Person person = null;
            List searchQueryInfos = createAllPersonSearchQueryInfos(icn, unformatedSsn,cfn,givenName,familyName,dateOfBirth, gender);
            for(Iterator iter=searchQueryInfos.iterator(); iter.hasNext();) {
                PersonSearchQueryInfo personSearchQueryInfo = (PersonSearchQueryInfo)iter.next();
                try {
                    List list = this.personService.search(personSearchQueryInfo,false,true,false,1);
                    if(list != null && list.size() == 1 && list.get(0) != null) {
                        person = (Person)list.get(0);
                        break;
                    }
                } catch(InvalidArgumentException iaEx) {
                    String logMessage = "Criteria is not supported by PSIM: " + personSearchQueryInfo;
                    logMessage = iter.hasNext() ? logMessage + " trying the next criteria" : logMessage;
                    logger.error(logMessage);
                }
            }
            if(person == null) {
                throw new ServiceException("Person not found");
            }
            return person;
        }
        catch(InvalidMessageException e)
        {
            //ASSERT: Should not happen if the message is already parsed.
            throw new RuntimeException("Failed to get the PID segment", e);
        }
        catch(ServiceException e)
        {
            //Catch and set the message to "Person not found". That will be the
            //message to be sent in the AE
            throw new ServiceException("Person not found", e);
        }
    }

    private List createAllPersonSearchQueryInfos(String icn, String ssn, String cfn, String firstName, String lastName, String dob, String gender)
    throws ServiceException {
        List list = new ArrayList();

        String unformatedSsn = StringUtils.trimToNull(SSN.formatSSN(ssn));

        String givenName = StringUtils.keepAlphaAndHyphen(firstName);

        if(StringUtils.isNotEmpty(icn)) {
            list.add(createPersonSearchQueryInfo(icn, null,null,null,null,null, null));
        }

        List familyNames = getFamilyNames(StringUtils.keepAlphaAndHyphen(lastName));
        for(Iterator iter=familyNames.iterator(); iter.hasNext();) {
            String familyName = (String)iter.next();

            //***************************//
            //CCR 13219 - send all criteria we get for each version of family name
            //previous impl is always sending null first name in first trait search
            list.add(createPersonSearchQueryInfo(null, unformatedSsn,null,givenName,familyName,dob,gender ));

            //*************************************************

            /*if(StringUtils.isNotEmpty(unformatedSsn)) {
                if(StringUtils.isNotEmpty(dob)) {
                    //Family Name, Date of Birth, SSN, and Gender if available
                    if(StringUtils.isNotEmpty(familyName)) {
                        list.add(createPersonSearchQueryInfo(null, unformatedSsn,null,null,familyName,dob,
                        			StringUtils.isNotEmpty(gender)?gender:null ));
                    }
                    //Given Name, Date of Birth, SSN, and Gender if available
                    if(StringUtils.isNotEmpty(givenName)) {
                        list.add(createPersonSearchQueryInfo(null, unformatedSsn,null,givenName,null,dob,
                        		StringUtils.isNotEmpty(gender)?gender:null ));
                    }
                }
                //Given Name, Family Name, SSN, and Gender if available
                if(StringUtils.isNotEmpty(givenName) && StringUtils.isNotEmpty(familyName)) {
                    list.add(createPersonSearchQueryInfo(null, unformatedSsn,null,givenName,familyName,null,
                    		StringUtils.isNotEmpty(gender)?gender:null ));
                }
            }
            if(StringUtils.isNotEmpty(cfn)) {
                 //Given Name, Family Name, CFN, and Gender if available
                if(StringUtils.isNotEmpty(givenName) && StringUtils.isNotEmpty(familyName)) {
                    list.add(createPersonSearchQueryInfo(null,null,cfn,givenName,familyName,null,
                    		StringUtils.isNotEmpty(gender)?gender:null ));
                }
            }*/
        }
        return list;
    }

    private PersonSearchQueryInfo createPersonSearchQueryInfo(String icn, String ssn, String cfn, String givenName, String familyName, String dob, String gender)
    throws ServiceException {
        PersonSearchQueryInfo pSearchQueryInfo = new PersonSearchQueryInfo();
        pSearchQueryInfo.setVpid(icn);
        pSearchQueryInfo.setRemoveEmptyStrings(true);
        pSearchQueryInfo.setUnformattedSsn(ssn);
        pSearchQueryInfo.setClaimFolderNumber(cfn);
        pSearchQueryInfo.setGivenName(givenName);
        pSearchQueryInfo.setFamilyName(familyName);
        pSearchQueryInfo.setDateOfBirth(dob);
        pSearchQueryInfo.setGender(gender);
        return pSearchQueryInfo;
    }

    private List getFamilyNames(String familyName) {
        List lastnames = new ArrayList();
        lastnames.add(familyName);
        if(StringUtils.contains(familyName,'-')) {
            String[] familyNames = StringUtils.split(familyName,'-');
            for(int i=0; i<familyNames.length; i++) {
                lastnames.add(familyNames[i]);
            }
        }
        return lastnames;
    }



    private String getValue(ST data)
    {
        return (data == null) ? null : data.getValue();
    }

	/**
	 * @return Returns the lookupService.
	 */
	public LookupService getLookupService() {
		return lookupService;
	}

	/**
	 * @param lookupService The lookupService to set.
	 */
	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}
}