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

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

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version
 * <br>$Log: RuleInvoker.java,v $
 */

public class RuleInvoker
{
	/* ======================================== */
	// MEMBERS
	/* ======================================== */
	
	private RulesObjectsHolder	roh;
	
	
	
	/* ======================================== */
	// CONSTRUCTORS
	/* ======================================== */
	
	public RuleInvoker (RulesObjectsHolder roh)
	{
		this.roh	= roh;
	}
	
	/* ======================================== */
	// CLASS BODY
	/* ======================================== */
	
	public void applyRules ()
	{
//		RulesObjectsHolder.enableDebugMode(true);
//		RulesObjectsHolder.toggleDebugMode();
		
		roh.appendLog("==== RULEENGINE DETAIL LOG ============================================");
		roh.printActList("Acts before RuleEngine started", "RuleInvoker.applyRules All Acts as shown on Invoice"); 
		
		// Filter
		ActSorter.filterActs(roh, RulesObjectsHolder.getPattern(RulesObjectsHolder.PATTERN_MED_AND_DENT_ACT));
		roh.printActList("After ActSorter.filterActs", "RuleInvoker.applyRules Filter only Medical and Dental Acts"); 
		
		// Reset Suffixes
		SuffixHandler.resetSuffixes(roh.getInvoice());
		roh.printActList("After SuffixHandler.resetSuffixes", "Remove all Rule-Dependant Suffixes N D F E R V");
		
		// Change Acts
		ActChanger.checkAndChange(roh);
		roh.printActList("After ActChanger.checkAndChange"," Changing Act Codes for Night/Sunday/Holiday");
		
		// Suffixes
		SuffixHandler.setSuffixes(roh);
		roh.printActList("After SuffixHandler.setSuffixes", "Reapply Suffixes for Date/Time/Age N D F E R V");
		
		// Reset Reductions
		resetReductions();
		roh.printActList("After RulesInvoker.resetReductions", "Reset Reductions R-Suffix and set Quantity to 1 if it was 0");
		
		// Eliminate Illegal Combinations
		eliminateIllegalCombinations();
		roh.printActList("After RulesInvoker.eliminateIllegalCombinations", "Eleminating illegal Act Combinations [removeIllegalActCombinationsOfInvoice, removeIncompatibleActCombinationsOfInvoice, removeActsWithoutDependencies,removeActsWithExclusivelyValidCodes]");
		
		// Cumulation
		/* Each type of nomenclature has its own cumulation rules and therefore
		 * The cumulation of each type is done separately.
		 * Therefore all acts, that doesn't belong to the according nomenlcature
		 * are filtered first.
		 */
		Cumulations.executePreCumulativeOperations(roh);
		
		// execute the medical cumulations only on the medical acts
		ActSorter.filterActs(roh, RulesObjectsHolder.getPattern(RulesObjectsHolder.PATTERN_MED_ACT));
		roh.printActList("After ActSorter.filterActs", "execute the medical cumulations only on the medical acts");
		
		Cumulations.executeMedicalCumulations(roh);
		
		// execute the dental cumulations only on the medical acts
		ActSorter.filterActs(roh, RulesObjectsHolder.getPattern(RulesObjectsHolder.PATTERN_DENT_ACT));
		Cumulations.executeDentalCumulations(roh);
		ActSorter.filterActs(roh, RulesObjectsHolder.getPattern(RulesObjectsHolder.PATTERN_MED_AND_DENT_ACT));
		Cumulations.executePostCumulativeOperations(roh);
		
		// Eliminate Illegal Combinations
		eliminateIllegalCombinations();
		roh.printActList("After RulesInvoker.eliminateIllegalCombinations", "Eleminating illegal Act Combinations [removeIllegalActCombinationsOfInvoice, removeIncompatibleActCombinationsOfInvoice, removeActsWithoutDependencies,removeActsWithExclusivelyValidCodes]");
		
		Cumulations.checkRentalActs(roh);
		roh.printActList("After Cumulations.checkRentalActs", "Remove all rental acts, that haven't their base act set");
		
		Cumulations.checkMinima(roh);
		roh.printActList("After Cumulations.checkMinima", "Check the minima for anesthetic and assistance acts and apply it if necessary.");
		
		// Unfilter
		ActSorter.unfilterActs(roh);
		roh.printActList("After ActSorter.unfilterActs", "Re-add filtered Acts to Invoice");
		
		// set thrid party payer
		setThirdPartyPayer();
		roh.printActList("After RulesInvoker.setThirdPartyPayer", "Special Billing Rules or Codes lead to special third party payer, Set if Needed");
		
		roh.appendLog("==== RULEENGINE END =====================================================");
	}
	
	
	
	/* ======================================== */
	// HELP METHODS
	/* ======================================== */
	
	private void resetReductions ()
	{
		Invoice invoice	= roh.getInvoice();
		
		
		for (Act act : invoice.getActs())
		{
			act.clearSuffix('R');
			act.resetOrgCoefficient();
			
			if (act.getQuantity().intValue() == 0)
			{
				act.setQuantity(Integer.valueOf(1));
			}
		}
		
		invoice.monetize();
	}
	
	
	private void eliminateIllegalCombinations ()
	{
		ActRemover.removeIllegalActCombinationsOfInvoice(roh);
		ActRemover.removeIncompatibleActCombinationsOfInvoice(roh);
		ActRemover.removeActsWithoutDependencies(roh);
		ActRemover.removeActsWithExclusivelyValidCodes(roh);
		roh.getInvoice().monetize();
		roh.discardSortedActs();
	}


	/**
	 * check and set the thirdparty payer on the invoice.
	 * 
	 * Special Billing Rules or Codes lead to special third party payer.
	 * if none of this rules fits, the ttp stays untouched.
	 * 
	 */
	private void setThirdPartyPayer () {
		Invoice		invoice	= roh.getInvoice();
		
//		// save original ttp
//		Insurance tpp = invoice.getThirdPartyPayer();
//		// reset ttp
//		invoice.setThirdPartyPayer("");
		
		if (!invoice.isAmbulatory().booleanValue() 																// not ambulant
			&& !invoice.isAccident()																			// not accident
			&& (!invoice.isFirstClass().booleanValue() || invoice.getFirstClassRequired().booleanValue())		// not first class or firstclassrequired
			&& (invoice.isInsuranceUCMAffiliated().booleanValue() || invoice.isInsuranceE111().booleanValue())	// UCM aff. or E11
			&& (invoice.getAmount().doubleValue() >= roh.getTiersPayantValue()|| roh.getLengthOfStay() >= 4))	// more than ttp min ammount
																				
		{
			invoice.setThirdPartyPayer(Insurance.INSURANCE_CNS);												// -> set CNS
		}
		else if (roh.includesAct("8V53"))																		// 8C53 Mammo Screening
		{
			invoice.setThirdPartyPayer(Insurance.INSURANCE_MAMMO);												// set MAMMO
			invoice.setAllMajorations(Invoice.c_DefaultMajoration);
		}
		else if (invoice.isInsuranceUCMAffiliated().booleanValue())												// is CNS affiliated
		{
			if (invoice.isAccident())																			// is Accident
			{
				// set Insurance to AAI
				invoice.setThirdPartyPayer(Insurance.INSURANCE_AAA);											// set AAA
			}
			else if (roh.includesAct("E30")														// E30 Form
					|| invoice.getPatient().getStatus().intValue() == Patient.STATUS_DEPARTED)  // or departed patient
			{
				invoice.setThirdPartyPayer(Insurance.INSURANCE_CNS);  							// -> set CNS
			}
		}
		
//		if (invoice.getThirdPartyPayer() == null 																	// TTP is null
//				&& tpp != null																						// TTP was NOT null
//				&& tpp.getId() != null																				// TTP was NOT dummy caisse
//				&& invoice.getPatient().getComplementary() != null													// patient has complementary
//				&& invoice.getPatient().getComplementary().getCompany() != null										// with company
//				&& tpp.getId().intValue() == invoice.getPatient().getComplementary().getCompany().getId().intValue())// TTP is same as patients complementary
//		{
//			invoice.setThirdPartyPayer(tpp);																		// set the old TTP
//		}
		
		// if we have a TTP here, set TTP as primary insurance as well.
		if (invoice.getThirdPartyPayer() != null && invoice.getThirdPartyPayer().getId() != 0) {
			invoice.setHealthInsurance(invoice.getThirdPartyPayer());
		}
		
	}
}
