package lu.tudor.santec.gecamed.billing.utils.rules;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Act;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version
 * <br>$Log: SuffixHandler.java,v $
 * <br>Revision 1.2  2013-04-23 09:00:18  ferring
 * <br>Suffixes only applied to technical acts
 * <br>
 * <br>Revision 1.1  2013-03-22 10:27:03  ferring
 * <br>UnionActs added and billing bugs fixed (since last not released commit)
 * <br>
 */
public class SuffixHandler
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	private static Collection<String>	actsExcludedFromDateAndTimeSuffixes	= new HashSet<String>();
	
//	private static final Pattern		PATTERN_IS_ANY_ACT_GENERAUX			= Pattern.compile("D?G(_[0-9_]+)?");
//	private static final Pattern		PATTERN_DONT_SHOW_TIME_INDEXES		= Pattern.compile("G_4(_[0-9_]+)?");
	
	private static final String[]	codesExcludedFromDateAndTimeSuffixes	= new String[] {
			// add all excludes of technical acts here:
			// private acts are not considered by the rule engine: "3.091", "3.092", "3.093", "3.094"
			
	};
	
	private static final String[]	indexesExcludedFromDateAndTimeSuffixes	= new String[] {
			"T_6_1_1"
	};
	
	
	/* ======================================== */
	// 		INITIALISATION
	/* ======================================== */
	
	static
	{
		for (String index : indexesExcludedFromDateAndTimeSuffixes)
		{
			for (String exclude : RulesObjectsHolder.getAllCodesOfRateIndex(RulesObjectsHolder.getRateIndex(index)))
				actsExcludedFromDateAndTimeSuffixes.add(exclude);
		}
		
		// The ActChanger class adds all codes that can be changed into this exclude Set
		for (String exclude : codesExcludedFromDateAndTimeSuffixes)
			actsExcludedFromDateAndTimeSuffixes.add(exclude);
	}
	
	
	
	/* ======================================== */
	// 		CLASS BODY
	/* ======================================== */
	
	public static void excludeFromDayAndTimeSuffixes (String code)
	{
		actsExcludedFromDateAndTimeSuffixes.add(code);
	}
	
	
	public static void excludeFromDayAndTimeSuffixes (Collection<String> codes)
	{
		actsExcludedFromDateAndTimeSuffixes.addAll(codes);
	}
	
	
	public static boolean isExcludedFromDayAndTimeSuffixes (String code)
	{
		return actsExcludedFromDateAndTimeSuffixes.contains(code);
	}
	
	
	public static void resetSuffixes (Invoice invoice)
	{
//		Invoice	invoice	= RulesObjectsHolder.getInvoiceForRules();
		
		for (Act act : invoice.getActs())
		{
			act.clearSuffix('N');
			act.clearSuffix('D');
			act.clearSuffix('F');
			act.clearSuffix('E');
			act.clearSuffix('R');
			act.clearSuffix('V');
			
			// TODO do not change hastime flag on Act, this should be done only manually....
//			act.setShowTime(RulesObjectsHolder.indexIncludes(RulesObjectsHolder.PATTERN_DONT_SHOW_TIME_INDEXES, act) ? Boolean.FALSE : Boolean.TRUE);
		}
	}
	
	
	public static void setSuffixes (RulesObjectsHolder roh)
	{
		setDayAndTimeSuffixes(roh);
		setAgeSuffixes(roh);
	}
	
	
	private static void setDayAndTimeSuffixes (RulesObjectsHolder roh)
	{
//		Invoice			invoice		= RulesObjectsHolder.getInvoiceForRules();
		HashSet<String>	codesOfDay;
		boolean			isSunday;
		boolean			isHoliday;
		Act				firstAct;
		
		
		for (List<Act> actsOfDay : roh.getSortedActs())
		{
			if (actsOfDay == null || actsOfDay.size() <= 0)
				return;
			
			firstAct	= actsOfDay.get(0);
			isSunday	= firstAct.performedOnSunday();
			isHoliday	= firstAct.performedOnHoliday();
			
			codesOfDay	= new HashSet<String>();
			for (Act act : actsOfDay)
				codesOfDay.add(act.getCode());
			
			if (codesOfDay.contains("V26"))
				continue;
			
			for (Act act : actsOfDay)
			{
				setDayAndTimeSuffixes (act, isSunday, isHoliday);
			}
		}
		
		roh.getInvoice().monetize();
	}
	
	
	private static void setAgeSuffixes (RulesObjectsHolder roh)
	{
		Integer		age;
		String		rateIndexKey;
		
		
		for (List<Act> actsOfDay : roh.getSortedActs())
		{
			if (actsOfDay == null || actsOfDay.isEmpty())
				continue;
			
			
			age	= null;
			for (Act act : actsOfDay)
			{
				if (age == null)
				{
					age	= roh.getPatientAgeOnDayOfAct(act);
					
					if (age < 0)
						break;
				}
				/* Check for suffix 'E': 
				 * Add the suffix 'E' for all acts of T_5_1_2 ("Endoscopie exploratrice et opérationnelle") and 
				 * T_1_4_2 ("Épreuve fonctionnelle respiratoire"), performed on children younger than 14 years
				 * or all acts of T_1_6 ("Gastro-Entérologie"), performed on children younger than 6 years. 
				 * 
				 * See nomenclature art. 4 §3 & art. 8 §2 & T_1_4_2 remark
				 */
				if (age < 14)
				{
					rateIndexKey	= RulesObjectsHolder.getIndexLabelOfCode(act.getCode());
					if (rateIndexKey.equals("T_5_1_2")
							|| rateIndexKey.equals("T_1_4_2")
							||(age < 6
							&& rateIndexKey.startsWith("T_1_6")))
					{
						act.setSuffix('E');
					}
				}
				
				/* Validate suffix 'A':
				 * Suffix 'A' is only allowed on technical acts
				 */
				if (act.suffixAlreadySet('A'))
				{
					if (RulesObjectsHolder.indexIncludes(RulesObjectsHolder.PATTERN_MED_AND_DENT_GEN_ACT, act))
					{
						act.clearSuffix('A');
					}
					else if (age >= 75 || age < 14)
					{
						/* Check for suffix 'V':
						 * All acts with the suffix 'A' performed on a patient older than 65
						 * or younger than 14
						 * 
						 * See nomenclature art. 4 §3 & art. 12 §5
						 */
						act.setSuffix('V');
					}
				}
			}
		}
	}
	
	
	private static void setDayAndTimeSuffixes (Act act, boolean sunday, boolean holiday)
	{
		String	code	= act.getCode();
		int		hour;
		
		
		if (isExcludedFromDayAndTimeSuffixes(code)
				|| !RulesObjectsHolder.getRateIndex("T").includesCode(code)
				|| act.isRental() 
				|| act.isMaterial())
			return;
		
		hour	= act.getPerformedHour();
		if (hour >= 20 || hour < 7)
			act.setSuffix('N');
		else if (sunday)
			act.setSuffix('D');
		else if (holiday)
			act.setSuffix('F');
	}
}
