/*
 * Created on Nov 15, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package gov.va.med.esr.common.util;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.DateUtils;
import gov.va.med.esr.common.infra.ImpreciseDateUtils;

import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.ConfidentialAddressCategoryType;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.party.ConfidentialAddressCategory;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.persistent.demographic.AddressDAO;
import gov.va.med.esr.common.persistent.demographic.ConfidentialAddressCategoryDAO;


/**
 * @author DNS   TSAIG
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Stppyle - Code Templates
 */
public class MailingAddressHelper {

	private static Hashtable addrPriorityTab = new Hashtable();
	
	static
	{
		//		 *** initialize the address priority table ***
	    //UC Manage Bulk Mailing
		//5153[UC15.14] Address Hierarchy for Letters 
	    //The system will send the address for the particular letter request according to the following hierarchy:
	    //    Confidential Address shall be sent as the address if the Confidential Address category is Eligibility/Enrollment.
	    //    If no confidential address exists then send the Temporary Address if the start date is within the time frame of residency and time of mailing.
	    //    If no Confidential Address and no Temporary Address then send the current Permanent Mailing Address. 
		addrPriorityTab.put(AddressType.CODE_CONFIDENTIAL_ADDRESS.getCode(), new Integer(3)); //confidential
		addrPriorityTab.put(AddressType.CODE_TEMPORARY_CORRESPONDENCE_ADDRESS.getCode(), new Integer(2)); //temporary correspondence (mailing)
		addrPriorityTab.put(AddressType.CODE_PERMANENT_ADDRESS.getCode(), new Integer(1)); //permanent
	}
	
	private static int getAddressPriority(String addrTypeCode)
	{
		if (addrTypeCode == null || addrTypeCode.length() == 0)
			return 0;

		Integer priority = (Integer) addrPriorityTab.get(addrTypeCode);
		return ( priority == null) ? 0 : priority.intValue();
	}
	
	// SUC72.10.1[2727]
	private static boolean isAddressValid(Address addr, Date today)
	{
		/* use this implementation per BadAddressReason (ONLY for Permanent) and start/end date (ONLY for Non Permanent) */
		boolean validAddress = false;		
		if (addr != null) {
			if(addr.isPermanent()) {
				if(addr.getBadAddressReason() == null ||
			       addr.getNcoaInvalidUndeliverableDate() == null ||
			       DateUtils.isEqualOrAfterIgnoreTime(addr.getNcoaInvalidUndeliverableDate(), today))
					validAddress = true;
			} else {
				// not permanent, check start and end dates
				boolean validStartDate = false;
				boolean validEndDate = false;
				
				if(addr.getStartDate() == null ||
						  DateUtils.isEqualOrBeforeIgnoreTime(ImpreciseDateUtils.getDateWithDefault(addr.getStartDate()), today))
					validStartDate = true;
				if(addr.getEndDate() == null ||
				   DateUtils.isEqualOrAfterIgnoreTime(ImpreciseDateUtils.getDateWithDefault(addr.getEndDate()), today))
					validEndDate = true;
				validAddress = validStartDate && validEndDate;
			}
		}
		return validAddress;
	}
	
	public static Address selectAddressForMailing(Collection addresses)
	{
		if (addresses == null || addresses.size() ==0)
			return null;
		
		int currPriority = 0; //current address priority
		int priority = 0;
		Address addr = null;
		Address addrToMail = null;

		Iterator iter = addresses.iterator();
		
		Date today = DateUtils.getCurrentDate(); // note this does not contain time

		while (iter.hasNext())
		{
			addr = (Address)iter.next();
			if (!isAddressValid(addr, today))
				continue;
			
			if (addr != null && addr.getType() != null) {
				priority = getAddressPriority(addr.getType().getCode());
			}
			
			// CCR 10302: Confidential Address Category displaying as the active address 
			// 		when category is not eligibilityenrollment
			// Previously only Eligibility/Enrollment was uploaded, hence there was no issue. 
			// After ESR was loaded with 5 categories (Eligibility/Enrollment and other 4 
			// Categories Appointment/Scheduling, Copayments/Veteran Billing, Medical Records, 
			// Others), the algorithm is broken. Need to ensure Confidential Address shall 
			// be sent as the address if the Confidential Address category is in 
			// Eligibility/Enrollment.
			Person person = addr.getPerson();
			
//			System.out.println("Conf Address with Person VPID/Priority/Addr Type/Categories=" 
//					+ person.getVPIDEntityKey().getVPID()
//					+ "/" + priority
//					+ "/" + addr.getType().getDescription()
//					+ "/" + addr.getPerson().getConfidentialAddressCategories());
			if (priority == ((Integer) addrPriorityTab.get(AddressType.CODE_CONFIDENTIAL_ADDRESS.getCode())).intValue() &&  
				!isConfAddrCategoriesContainsCategoryType(person, 
						ConfidentialAddressCategoryType.CODE_ELIGIBILITY_ENROLLMENT.getCode())) {
				priority = 0;

			}
			
			if (priority > currPriority)
			{
				addrToMail = addr;
				currPriority = priority; //reset the priority
			}
		}
		
		return addrToMail;
	}
	
    private static boolean isConfAddrCategoriesContainsCategoryType(Person person, String confAddrType) {
        // Create a list for Confidential Address Categories
        Set categoriesSource = person.getConfidentialAddressCategories();
        
        ConfidentialAddressCategory confAddrCat = null;
        Iterator iter = categoriesSource.iterator();
        while (iter.hasNext()) {
			
        	confAddrCat = (ConfidentialAddressCategory)iter.next();
//        	System.out.println("Conf Address with Categories ="
//					+ person.getVPIDEntityKey().getVPID() 
//					+ "/" + confAddrCat.getType().getCode());
        	if (confAddrCat.getType().getCode().equals(confAddrType)){
        		return true;
        	}
        }
          
        return false;       
    }
    
    public static Address selectAddressForMailing(Collection confidentialAddressCategories, Collection addresses)
	{
		if (addresses == null || addresses.size() ==0)
			return null;
		
		int currPriority = 0; //current address priority
		int priority = 0;
		Address addr = null;
		Address addrToMail = null;

		Iterator iter = addresses.iterator();
		
		Date today = DateUtils.getCurrentDate(); // note this does not contain time

		while (iter.hasNext())
		{
			addr = (Address)iter.next();
			if (!isAddressValid(addr, today))
				continue;
			
			priority = getAddressPriority(addr.getType().getCode());
			
			if (priority == ((Integer) addrPriorityTab.get(AddressType.CODE_CONFIDENTIAL_ADDRESS.getCode())).intValue() &&  
				!isConfAddrCategoriesContainsCategoryType(confidentialAddressCategories, 
						ConfidentialAddressCategoryType.CODE_ELIGIBILITY_ENROLLMENT.getCode())) {
				priority = 0;

			}
			
			if (priority > currPriority)
			{
				addrToMail = addr;
				currPriority = priority; //reset the priority
			}
		}
		
		return addrToMail;
	}
    
    private static boolean isConfAddrCategoriesContainsCategoryType(Collection confidentialAddressCategoryList, String confAddrType) {
    	
    	if (confidentialAddressCategoryList == null || confidentialAddressCategoryList.size() == 0)
			return false;
        // Create a list for Confidential Address Categories       
        ConfidentialAddressCategory confAddrCat = null;
        Iterator iter = confidentialAddressCategoryList.iterator();
        while (iter.hasNext()) {
			
        	confAddrCat = (ConfidentialAddressCategory)iter.next();
        	if (confAddrCat.getType().getCode().equals(confAddrType)){
        		return true;
        	}
        }
          
        return false;       
    }
}
