/********************************************************************
 * Copyright � 2005 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.esr.ui.common.action;

// Java Classes
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Libraries Classes
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

// Framework Classes
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.ServiceOptimisticLockException;
import gov.va.med.fw.service.TimeoutServiceException;
import gov.va.med.fw.service.EntityNotChangedException;
import gov.va.med.fw.util.StringUtils;

// ESR Classes
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.PersonMergeInfo;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.PersonIdEntityKey;
import gov.va.med.esr.ui.ApplicationConstants;
import gov.va.med.esr.ui.common.beans.VeteranHeaderBean;
import gov.va.med.esr.ui.ee.action.EligibilityEnrollmentConversionService;
import gov.va.med.esr.ui.util.SessionManager;
import gov.va.med.esr.ui.util.NameHelper;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.PersonLockedException;

/**
 * Common abstract base class for all UI actions that work with a Person object or typically require one to have been
 * selected.
 *
 * @author Andrew Pach
 * @version 3.0
 */
public abstract class PersonAbstractAction extends AbstractAction
{
    // Struts forwards
    public static final String FORWARD_PERSON_SEARCH = "person.search.display";

    // Message keys
    public static final String PERSON_LOCKED_ERROR_KEY = "error.personLocked";
    public static final String TIMEOUT_ERROR_KEY = "error.timeout";
    public static final String INVALID_ARGUMENT_KEY = "error.invalid.parameter.combination";

    /**
     * A default constructor
     */
    protected PersonAbstractAction()
    {
        super();
    }

    /**
     * Handles ensuring a Person has been selected and is in the session.
     * If an action wishes to extend this action, but doesn't require a person having been selected,
     * they should overwrite the requiresSelectedPerson method to return false.
     *
     *
@param mapping The ActionMapping used to select this instance
     * @param form The optional ActionForm bean for this request (if any)
     * @param request The HTTP request we are processing
     * @param response The HTTP response we are creating
     *
     * @throws Exception if an exception occurs
     */
    public ActionForward execute(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        // Check that a person has been selected if required
        if (requiresSelectedPerson())
        {
            // Get the Person object from the cache and if it isn't set, forward to the person searcbh screen.
            Person person = getSelectedPerson(request);
            boolean isAddAPerson = SessionManager.isAddAPerson(request);
            if (person == null)
            {
                return mapping.findForward(FORWARD_PERSON_SEARCH);
            }
        }

        // Call the base classes processing and return to where it says
        return super.execute(mapping, form, request, response);
    }

    /**
     * Default implementation always returns true.  If a screen doesn't require a person to have
     * been selected, overwrite this method to return false.
     * @return whether the selected person is required.
     */
    protected boolean requiresSelectedPerson()
    {
        return true;
    }

    protected String getSelectedPersonId(HttpServletRequest request)
    {
        boolean isAddAPerson = false;
        isAddAPerson = SessionManager.isAddAPerson(request);
        if (isAddAPerson) {
        	return null;
        }
        Person selectedPerson = getSelectedPerson(request);
        String personId = null;
        if (selectedPerson != null)
        {
            personId = selectedPerson.getEntityKey().getKeyValueAsString();
        }
        return personId;
    }

    /**
     * Re-retrieves and stores in session the latest Person based on the entity key of
     * the passed in person.
     * @param request The HttpServletRequest
     * @param person The person
     * @throws ServiceException if any problems were encountered.
     */
    protected void updateSelectedPerson(HttpServletRequest request, Person person)
        throws ServiceException
    {
    	 boolean isAddAPerson = false;
         isAddAPerson = SessionManager.isAddAPerson(request);
         if (isAddAPerson)
        	 setSelectedPerson(request, person);
         else
         {
        PersonEntityKey personEntityKey =
            CommonEntityKeyFactory.createPersonIdEntityKey(person.getEntityKey().getKeyValueAsString());
        Person updatedPerson = this.getPersonService().getPerson(personEntityKey);
        updatedPerson.setOpenCasesCount(getWorkflowService().getOpenCasesCount(
                updatedPerson.getPersonEntityKey()));
        setSelectedPerson(request, updatedPerson);
         }
    }

    protected void updateSelectedPerson(HttpServletRequest request, Person person, boolean fromUI)
            throws ServiceException
        {
        	 boolean isAddAPerson = false;
             isAddAPerson = SessionManager.isAddAPerson(request);
             if (isAddAPerson)
            	 setSelectedPerson(request, person);
             else
             {
            	 Person updatedPerson = null;
            PersonEntityKey personEntityKey =
                CommonEntityKeyFactory.createPersonIdEntityKey(person.getEntityKey().getKeyValueAsString());
            if (fromUI) {
            	updatedPerson = this.getPersonService().getPerson(personEntityKey, person.getIdentityTraits());
            } else {
            	 updatedPerson = this.getPersonService().getPerson(personEntityKey);
            }
            updatedPerson.setOpenCasesCount(getWorkflowService().getOpenCasesCount(
                    updatedPerson.getPersonEntityKey()));
            setSelectedPerson(request, updatedPerson);
             }
        }

    protected void setSelectedPerson(HttpServletRequest request, Person person)
    {
    	putSandboxEntry(request, ApplicationConstants.SessionData.SELECTED_PERSON, person);
        // CCR11593 -- once the retrieved person is already cached into the session, the traits is no longer needed
        // (to avoid any insonsistency between the person and the traits)
        removeSelectedTraits(request);

        //boolean isAddAPerson = false;
        //isAddAPerson = SessionManager.isAddAPerson(request);
        //if (!isAddAPerson)
        	updateHeader(request);

    }

    protected Person getSelectedPerson(HttpServletRequest request)
    {
        return (Person)getSandboxEntry(request,
            ApplicationConstants.SessionData.SELECTED_PERSON);
    }

     //10901

    protected void removeSelectedPerson(HttpServletRequest request)
    {
        removeSandboxEntry(request, ApplicationConstants.SessionData.SELECTED_PERSON);
        removeSelectedTraits(request);
    }

    // CCR11593 added traits returned from initial search screen, currently this is only leveraged when
    // a single person is returned from search screen, and pulled up right away.
    protected PersonIdentityTraits getSelectedTraits(HttpServletRequest request)
    {
        return (PersonIdentityTraits)getSandboxEntry(request,
            ApplicationConstants.SessionData.SELECTED_TRAITS);
    }

    protected void removeSelectedTraits(HttpServletRequest request)
    {
        removeSandboxEntry(request, ApplicationConstants.SessionData.SELECTED_TRAITS);
    }

    protected void setUpdatedPerson(HttpServletRequest request, Person person)
    {
        putSandboxEntry(request, ApplicationConstants.SessionData.UPDATED_PERSON,
            person);
    }

    protected void removeUpdatedPerson(HttpServletRequest request)
    {
        removeSandboxEntry(request, ApplicationConstants.SessionData.UPDATED_PERSON);
    }

    protected Person getUpdatedPerson(HttpServletRequest request)
    {
        return (Person)getSandboxEntry(request,
            ApplicationConstants.SessionData.UPDATED_PERSON);
    }

    /**
     * Updates the veteran header with the latest details from the Person object.
     * @param request The request
     */
    protected void updateHeader(HttpServletRequest request)
    {
        Person person = getSelectedPerson(request);
        if (person == null)
        {
            return;
        }

        // Put core information in the header bean
        VeteranHeaderBean vetHeader = new VeteranHeaderBean();
        String formattedName = NameHelper.formatNameLastNameFirst(NameHelper.getLegalName(person));
        vetHeader.setName(StringUtils.isBlank(formattedName) ? null : formattedName.toUpperCase());
        vetHeader.setDob((person.getBirthRecord() == null) ? null : person.getBirthRecord().getBirthDate());
        vetHeader.setDod((person.getDeathRecord() == null) ? null : person.getDeathRecord().getDeathDate());
        vetHeader.setSensitiveRecord(person.getSensitiveRecord());
        vetHeader.setOpenCases(person.getOpenCasesCount() > 0 ? Boolean.TRUE: Boolean.FALSE);
        vetHeader.setPersonLockReason(person.isPersonLocked() ? person.getPersonLockedReason().toString().toUpperCase() : null);

        // SSN
        SSN ssn = null;
        if (!SessionManager.isAddAPerson(request))
        	ssn = person.getOfficialSsn();
        else {
    	   for (Iterator iter = person.getSsns().iterator(); iter.hasNext();) {
    			   ssn = (SSN) iter.next();
    			   if (ssn != null || ssn.equals(""))
    				   break; // For Add A Person, it only have one SSN.
           }
        }

        //CCR 11984: check null for ssn.getSsnText() since for pseudo ssn, the ssn text is null
        vetHeader.setSsn( (ssn == null || ssn.getSsnText() ==null) ? null : ssn.getSsnText().toUpperCase());

        // Enrollment Determination Status
        EnrollmentDetermination enrollDeter = person.getEnrollmentDetermination();
        EnrollmentStatus enrollStatus = null;
        String status = "";
        if (enrollDeter != null)
        {
            enrollStatus = person.getEnrollmentDetermination().getEnrollmentStatus();
        }
        if ((enrollStatus != null) && (StringUtils.isNotBlank(enrollStatus.getDescription())))
        {
            status = enrollStatus.getDescription();
            int shortIndex = status.indexOf(";");
            if (shortIndex > 0)
            {
                status = status.substring(0, shortIndex);
            }
        }
        String priority = EligibilityEnrollmentConversionService.getEnrollmentPriority(person);
        if (priority != null && priority.length() > 0)
        {
            status += " (" + priority + ")";
        }
        vetHeader.setStatus(StringUtils.isBlank(status) ? null : status.toUpperCase());

        // Veteran Merge Pending
        vetHeader.setDataMergePending(false);
        if (!SessionManager.isAddAPerson(request)){
        	try
	        {
	        	/***CodeCR9079
	            PersonMergeInfo info = getPersonMergeService().getPersonMergeInfo((PersonIdEntityKey)person.getPersonEntityKey());
	            if ((info != null) && (info.getMergeEndDate() == null))
	            {
	                vetHeader.setDataMergePending(true);
	            }
	            *****/
	        	boolean hasPendingMerge = getPersonMergeService().hasActiveMerge((PersonIdEntityKey)person.getPersonEntityKey());
	        	vetHeader.setDataMergePending(hasPendingMerge);

	            // CCR#9586
	            PersonMergeInfo info =
	                getPersonMergeService().getPersonMergeDataInfo((PersonIdEntityKey)person.getPersonEntityKey());
	            if ((info != null) && (info.getMergeEndDate() == null))
	            {
	                vetHeader.setDataMergePending(true);
	            }

	        	// CCR#9586
	        	PersonMergeInfo mergInfoID = getPersonMergeService().getPersonMergeInfo((PersonIdEntityKey)person.getPersonEntityKey());
	        	String mergInfoIDStr = null;
		        	if(mergInfoID!= null && !mergInfoID.equals("")){
		        		mergInfoIDStr = mergInfoID.getEntityKey().getKeyValueAsString();
			        	if(mergInfoIDStr!= null && !mergInfoIDStr.equals(""))
			        		vetHeader.setMergInfoID(mergInfoIDStr);
			        	else{
			        		mergInfoIDStr = new String("");
			        		vetHeader.setMergInfoID(mergInfoIDStr);
			        	}
		        	}
		        	else
		        	{
		        		mergInfoIDStr = new String("");
		        		vetHeader.setMergInfoID(mergInfoIDStr);
		        	}

	        }
	        catch (ServiceException e)
	        {
	            throw new RuntimeException("Problem getting PersonMergeInfo.", e);
	        }
        }
        // Update the header bean in the session
        SessionManager.setVeteranHeaderBean(request, vetHeader);
    }

    /**
     * Re-retrieves the Person and stores it in session.
     *
     * @param request The request
     * @param ex a service optimistic lock exception
     * @throws Exception If any problems were encountered re-retrieving the person
     */
    protected void processOptimisticLockException(HttpServletRequest request,
        ServiceOptimisticLockException ex) throws Exception
    {
        // Get the updated Person since the original one is out-dated.
        addActionMessage(request, OPTIMISTC_LOCK_ERROR_KEY);
        Person person = null;
        if (ex.getEntity() != null)
        {
            // The exception already populated the updated one so get it from there
            person = (Person)ex.getEntity();
        }
        else
        {
            // Re-retrive he Person
            person = getSelectedPerson(request);
        }

        // Update the newly retrieved person in session
        if (person != null)
        {
            updateSelectedPerson(request, person);
            addActionMessage(request, OPTIMISTC_LOCK_ERROR_KEY);
        }
        else
        {
            // Shouldn't really get here since the person service should throw an exception if
            // the Person couldn't be retrieved.
            throw new Exception("Person could not be re-retrived from the database.");
        }
    }

    /**
     * Adds an error message with the locked reason.
     *
     * @param request The request
     * @param ex The person locked exception
     *
     * @throws Exception If any problems are encountered.
     */
    protected void processPersonLockedException(HttpServletRequest request,
        PersonLockedException ex) throws Exception
    {
        // Add an error message
        addActionMessage(request, PERSON_LOCKED_ERROR_KEY, (ex.getReason() != null ? ex.getReason().getReason() : "Unknown"));
    }

    /**
     * Adds an informational message that the data hasn't changed.
     *
     * @param request The request
     * @param ex The data not changed exception
     *
     * @throws Exception If any problems are encountered.
     */
    protected void processEntityNotChangedException(HttpServletRequest request, EntityNotChangedException ex) throws Exception
    {
        // Add an error message
        addInformationMessage(request, DATA_NOT_CHANGED_MESSAGE_KEY);
    }

    /**
     * Adds an error message for timeouts.
     *
     * @param request The request
     * @param ex The person locked exception
     *
     * @throws Exception If any problems are encountered.
     */
    protected void processTimeoutException(HttpServletRequest request, TimeoutServiceException ex) throws Exception
    {
        // Add an error message
        addActionMessage(request, TIMEOUT_ERROR_KEY);
    }
}