/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.billing.ejb.session.beans;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateless;

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.billing.ejb.entity.beans.Suffix;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.InvoiceInterface;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.NomenclatureInterface;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.RuleInterface;
import lu.tudor.santec.gecamed.billing.utils.BillingAdminSettings;
import lu.tudor.santec.gecamed.billing.utils.rules.RuleInvoker;
import lu.tudor.santec.gecamed.billing.utils.rules.RuleOptions;
import lu.tudor.santec.gecamed.billing.utils.rules.RulesObjectsHolder;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.NationalHoliday;
import lu.tudor.santec.gecamed.core.ejb.session.beans.GECAMedSessionBean;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.NationalHolidayInterface;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Hospitalisation;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Insurance;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.InsuranceInterface;
import lu.tudor.santec.gecamed.usermanagement.ejb.session.interfaces.LoginInterface;

import org.apache.log4j.Level;

//***************************************************************************
//* Interface Definition and Members                                        *
//***************************************************************************

/**
 * The RuleBean centralizes access to the rule engine.
 * @author nico.mack@tudor.lu
 */

@Stateless
@Remote (RuleInterface.class)
public class RuleBean extends GECAMedSessionBean implements RuleInterface
	{
//	private static final long serialVersionUID = 1L;
	
	/** the logger Object for this class */
	private static org.apache.log4j.Logger m_Logger = org.apache.log4j.Logger.getLogger(RuleBean.class.getName());
	
	@EJB
	private LoginInterface				m_LoginInterface;
	
	@EJB
	private InvoiceInterface			m_InvoiceInterface;
	
	@EJB
	private InsuranceInterface			m_InsuranceInterface;
	
	@EJB
	private NomenclatureInterface		m_NomenclatureInterface;
	
	@EJB
	private NationalHolidayInterface	m_HolidayInterface;

	private static boolean					m_Initialized = false;
//	private static RuleBase					m_BillingRules = null;
//	private static Collection <RateIndex>  	m_IndexGlobals; 
//	private static Collection <Rate> 		m_RateGlobals;
	
//	private static Vector <String>			m_ClientAgendaGroups;
//	private static Vector <String>			m_ServerAgendaGroups;
	
//	private static RateIndex[]				m_IgnoredIndexes	= { RateIndex.loadRateIndexByLabel("G_3") };
//	private static Rate[]					m_IgnoredActs		= {  };

//	private static Pattern					c_GroupPattern = Pattern.compile ("^AgendaGroup_(\\d*)$",Pattern.CASE_INSENSITIVE);
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constants	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
		
//	private static final Collection <Integer> m_Dependencies = new ArrayList <Integer> ();
//  
//	static 	{
//    		m_Dependencies.add(InvoiceInterface.c_PatientDependency);
//    		m_Dependencies.add(InvoiceInterface.c_HospitalisationDependency);
//	    	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * The postConstruct method is part of the bean lifecycle interface and its
 * being called just after the bean has been created. The RuleBean uses this
 * moment to compile the packaged rules and to initialize the Rule Engine
 * itself.
 */
//---------------------------------------------------------------------------
	    
@PostConstruct
public void postConstruct ()
	{
	Collection <Suffix>			l_Suffixes;
//	PackageBuilderConfiguration l_Configuration;
//	JavaDialectConfiguration 	l_Dialect;
//	PackageBuilder				l_Builder;
	
//	this.setLoggerForClass (RuleBean.class);
//	super.postConstruct();
	
	if (m_Initialized == false)
		{
		l_Suffixes = this.loadSuffixes();
		Act.setAllSuffixes	(l_Suffixes);
		Act.setHolidays	(this.loadHolidays());
		Invoice.setInsurances(this.loadInsurances());
	
//		m_Logger.info( "Loading Working Memory Presets...");
//
//		this.loadWorkingMemoryGlobals ("/lu/tudor/santec/gecamed/billing/rules/WorkingMemoryGlobals");
//	
//		m_Logger.info( "Loading Agenda Groups...");
//
//		m_ClientAgendaGroups = this.loadAgendaGroups ("/lu/tudor/santec/gecamed/billing/rules/ClientAgendaGroups");
//		m_ServerAgendaGroups = this.loadAgendaGroups ("/lu/tudor/santec/gecamed/billing/rules/ServerAgendaGroups");
//
//		m_Logger.info( "Compiling Rulebase for Billing Module");
//	
//		if (m_BillingRules == null) m_BillingRules = RuleBaseFactory.newRuleBase();
//
//		try	{
//			l_Configuration = new PackageBuilderConfiguration();
//			l_Dialect = (JavaDialectConfiguration) l_Configuration.getDialectConfiguration( "java" );
//			l_Dialect.setCompiler (JavaDialectConfiguration.JANINO);		
//			
//			l_Builder = new PackageBuilder (l_Configuration);
//			l_Builder.addPackageFromDrl(new InputStreamReader (this.getClass().getResourceAsStream("/lu/tudor/santec/gecamed/billing/rules/NightAndHolidays.drl")));
//			l_Builder.addPackageFromDrl(new InputStreamReader (this.getClass().getResourceAsStream("/lu/tudor/santec/gecamed/billing/rules/ThirdPartyPayment.drl")));
//			l_Builder.addPackageFromDrl(new InputStreamReader (this.getClass().getResourceAsStream("/lu/tudor/santec/gecamed/billing/rules/Majoration.drl")));
//			l_Builder.addPackageFromDrl(new InputStreamReader (this.getClass().getResourceAsStream("/lu/tudor/santec/gecamed/billing/rules/Cumulation.drl")));
//	
//			m_BillingRules.addPackage(l_Builder.getPackage());
//			}
//		catch (DroolsParserException p_Exception)
//			{
//			m_Logger.error("Failed to parse Rules",p_Exception);
//			}
//		catch (IOException p_Exception)
//			{
//			m_Logger.error("Failed to read Rule Files",p_Exception);	
//			}
//		catch (Exception p_Exception)
//			{
//			m_Logger.error("Failed to add Rule Packages to Rulebase",p_Exception);	
//			}	
		m_Initialized = true;
		}
	}

//***************************************************************************
//* Class Primitives                                                        *
//***************************************************************************
//---------------------------------------------------------------------------
//===========================================================================
//= Required Objects Prefetching
//===========================================================================
//---------------------------------------------------------------------------
/**
 * Fetches all Suffixes defined in the database.
 * @return A collection holding all Suffixes defined in the database.
 */
//---------------------------------------------------------------------------

private Collection <Suffix> loadSuffixes ()
	{
	Collection <Suffix>	l_Suffixes = null;

	if (m_NomenclatureInterface != null)
		{
		try {
			l_Suffixes = m_NomenclatureInterface.getAllSuffixes ();
			}
		catch (Exception p_Exception) 
			{
			m_Logger.error("Failed to prefetch suffixes",p_Exception);
			}
		}	
	return l_Suffixes;
	}

//---------------------------------------------------------------------------
/**
 * Fetches all national hoildays defined in the database.
 * @return A collection holding all national hoildays defined in the database.
 */
//---------------------------------------------------------------------------

private Collection <NationalHoliday> loadHolidays ()
	{
	Collection <NationalHoliday> l_Holidays = null;
	
	if (m_HolidayInterface != null)
		{
		try {
			l_Holidays = m_HolidayInterface.getAllNationalHolidays ();
			}
		catch (Exception p_Exception) 
			{
			log(Level.FATAL, "Failed to prefetch national holidays!",p_Exception);
			}
		}
	
	return l_Holidays;
	}

//---------------------------------------------------------------------------
/**
 * Fetches all third party paying insurances defined in the database.
 * @return A collection holding all third party paying insurances  defined in 
 * the database.
 */
//---------------------------------------------------------------------------

private Collection <Insurance> loadInsurances ()
	{
	Collection <Insurance> l_Insurances = null;
	
	if (m_InvoiceInterface != null)
		{
		try {
			l_Insurances = m_InsuranceInterface.getAllInsurances();
			}
		catch (Exception p_Exception) 
			{
			log(Level.FATAL, "Failed to prefetch insurances!",p_Exception);
			}
		}
	
	return l_Insurances;
	}

//---------------------------------------------------------------------------
/**
 * Fetches lazy dependencies for specified invoice.
 * @param p_Invoice specifies the invoice to fetch lazy dependencies for.
 * @return the specified invoice having its lazy dependencies properly set.
 */
//---------------------------------------------------------------------------

private Invoice fetchDependenciesForInvoice (Invoice p_Invoice)
	{
	Patient			l_Patient;
	Hospitalisation l_Hospitalisation;
	
	if (p_Invoice != null)
		{
		try {
			if ((p_Invoice.getPatient() != null) && (p_Invoice.getPatient().getId() != null))
				{
				l_Patient = m_EntityManager.find (Patient.class, p_Invoice.getPatient().getId());
				p_Invoice.setPatient(l_Patient);
				}

			if ((p_Invoice.getHospitalisation() != null) && (p_Invoice.getHospitalisation().getId() != null))
				{
				l_Hospitalisation = m_EntityManager.find(Hospitalisation.class, p_Invoice.getHospitalisation().getId());
				p_Invoice.setHospitalisation(l_Hospitalisation);
				}
			}
		catch (Exception p_Exception) 
			{
			log(Level.WARN, "Failed to fetch lazy dependencies for Invoice", p_Exception);
			}
		}
	return p_Invoice;
	}

////---------------------------------------------------------------------------
///**
// * UCM (Union des Caisses de Maladie) Rates are uniquely identified by a
// * short alphanumerical code. The getRateByCode method looks up a rate for
// * the specified code.
// * @param p_Code specifies the code of the rate to look for.
// * @return A matching rate for the specified code.
// */
////---------------------------------------------------------------------------
//
//private Rate loadRateByCode (String p_Code)
//	{
//	Rate	l_Rate = null;
//
//	if (m_NomenclatureInterface != null)
//		{
//		try {
//			l_Rate = m_NomenclatureInterface.getRateByCode(p_Code);
//			}
//		catch (Exception p_Exception) 
//			{
//			m_Logger.error("Failed to load Rate with code " + p_Code,p_Exception);
//			}
//		}	
//	return l_Rate;
//	}
//
////---------------------------------------------------------------------------
///**
// * loads the rate index entry specified by the given label. Please note that
// * method not only loads the entry itself, but also all its sub entries as
// * well as lazy dependencies, i.e. Rates.
// * @param p_Label specifies the label of the rate index entry to look for.
// * @return the fully initialized rate index entry if available.
// */
////---------------------------------------------------------------------------
//
//private RateIndex loadRateIndexByLabel (String p_Label)
//	{
//	RateIndex	l_RateIndex = null;
//	
//	if (m_NomenclatureInterface != null)
//		{
//		try {
//			l_RateIndex = m_NomenclatureInterface.getRateIndexByLabel(p_Label);
//			if (l_RateIndex != null) l_RateIndex = m_NomenclatureInterface.fetchLazyDependencies(l_RateIndex);
//			}
//		catch (Exception p_Exception) 
//			{
//			m_Logger.error("Failed to load Rate Index with label " + p_Label,p_Exception);
//			}
//		}	
//	return l_RateIndex;
//	}

////---------------------------------------------------------------------------
///**
// * the method loads the working memory globals from the specified file. Working
// * Memory Globals are Rates and Rate Index entries that the rules require
// * to work properly.
// * @param p_GlobalsFile specifies the file path of the globals file to load.
// */
////---------------------------------------------------------------------------
//
//private void loadWorkingMemoryGlobals (String p_GlobalsFile)
//	{
//	Properties				l_Properties;
//	Enumeration<Object>		l_Keys	= null;
//	String         			l_Key;
//	String		   			l_Value;
//	String[]	   			l_Fields;
//	String					l_Label;
//	int						l_Index;
//
//	RateIndex				l_RateIndex;
//	Rate					l_Rate;
//
//	m_RateGlobals	= new HashSet <Rate> ();
//	m_IndexGlobals	= new HashSet <RateIndex> ();
//   	l_Properties	= new Properties ();
//    
//    try {
//    	l_Properties.load (this.getClass().getResourceAsStream(p_GlobalsFile));
//		l_Keys = l_Properties.keys();
//		}
//    catch(FileNotFoundException p_Exception)
//		{
//		m_Logger.error("Working Memory Globals file could not be found", p_Exception);
//		return;
//		}
//	catch(IOException p_Exception)
//		{
//		m_Logger.error("Error while reading Working Memory Globals file", p_Exception);
//		return;
//		}
//	
//	if (l_Keys == null) return;
//	
//	while (l_Keys.hasMoreElements())
//    	{
//		l_Key 	= (String)l_Keys.nextElement();
//		l_Value = l_Properties.getProperty(l_Key);
//		if (l_Value == null) continue;
//		
//		if (l_Key.equals("RateGlobals"))
//        	{
//			l_Fields = l_Value.split (",");
//			for (l_Index = 0; l_Index < l_Fields.length; l_Index++)
//           		{
//				l_Label = l_Fields[l_Index].trim();
//				l_Rate = this.loadRateByCode(l_Label);
//				
//				if (l_Rate == null)
//					 log(Level.WARN, "Rate \"" + l_Label + "\" not found in DB table billing.rate.\n" +
//					 		"The rate is needed for the working memory of the billing rule system. This might cause trouble!");
//				else m_RateGlobals.add(l_Rate);
//           		}
//        	}
//		else if (l_Key.equals("IndexGlobals"))
//       		{
//			l_Fields = l_Value.split (",");
//			for (l_Index = 0; l_Index < l_Fields.length; l_Index++)
//           		{
//				l_Label = l_Fields[l_Index].trim();
//				l_RateIndex = this.loadRateIndexByLabel(l_Label);
//
//				if (l_RateIndex == null)
//					log(Level.WARN, "RateIndex \"" + l_Label + "\" not found in DB table billing.rate_index.\n" +
//					 		"The rate index is needed for the working memory of the billing rule system. This might cause trouble!");
//				else m_IndexGlobals.add(l_RateIndex);
//           		}
//       		}
//        }
// 	}

////---------------------------------------------------------------------------
///**
// * the method loads the agenda groups to be executed from the specified file. 
// * Agenda Groups define groups of rules to fire simultaneously.
// * @param p_GroupsFile specifies the file path of the file to load.
// */
////---------------------------------------------------------------------------
//
//private Vector <String> loadAgendaGroups (String p_GroupsFile)
//	{
//	Vector <String>			l_AgendaGroups;
//	
//	Properties				l_Properties;
//	Enumeration<Object>		l_Keys	= null;
//	String         			l_Key;
//	String		   			l_Value;
//
//	Matcher					l_GroupMatcher;
//	int						l_Position;
//  	l_Properties			= new Properties ();
//    
//    l_AgendaGroups = new Vector <String> ();
//  	
//  	try {
//    	l_Properties.load (this.getClass().getResourceAsStream(p_GroupsFile));
//		l_Keys = l_Properties.keys();
//		}
//    catch(FileNotFoundException p_Exception)
//		{
//		m_Logger.error("Agenda Groups file could not be found", p_Exception);
//		return l_AgendaGroups;
//		}
//	catch(IOException p_Exception)
//		{
//		m_Logger.error("Error while reading Agenda Groups file", p_Exception);
//		return l_AgendaGroups;
//		}
//	
//	if (l_Keys == null) return l_AgendaGroups;
//	
//	l_AgendaGroups.setSize(l_Properties.size());
//	
//	while (l_Keys.hasMoreElements())
//    	{
//		l_Key 	= (String)l_Keys.nextElement();
//		l_Value = l_Properties.getProperty(l_Key);
//		if (l_Value == null) continue;
//		
//		l_GroupMatcher = c_GroupPattern.matcher(l_Key);
//		if (l_GroupMatcher.matches())
//			{
//			try	{
//				l_Position = Integer.parseInt (l_GroupMatcher.group(1));
//				}
//			catch (NumberFormatException p_Exception)
//				{
//				l_Position = 0;
//				}
//		
//			l_AgendaGroups.set(l_Position, (String)l_Value);
//			}
//        }
// 	
//	return l_AgendaGroups;
//	}

////---------------------------------------------------------------------------
///** 
// * Inserts the previously loaded working memory globals into the specified
// * working memory.
// * @param p_Memory specifies the working memory to insert globals into.
// */    
////---------------------------------------------------------------------------
//
//private void setWorkingMemoryGlobals (StatefulSession p_Memory)
//    {
//	Iterator <Rate> 		l_RateIterator;
//	Rate					l_Rate;
//	Iterator <RateIndex>	l_IndexIterator;
//	RateIndex				l_Index;
//	String					l_Code;
//	
//	
//	RulesObjectsHolder.clearAll();
//    if (m_RateGlobals != null)
//    	{
//    	l_RateIterator = m_RateGlobals.iterator();
//    	while (l_RateIterator.hasNext())
//    		{
//    		l_Rate = l_RateIterator.next();
//    		l_Code = l_Rate.getCode();
//    		l_Code = l_Code.replace('.', '_');
//    		p_Memory.setGlobal("Rate_" + l_Code,l_Rate);
////    		RulesObjectsHolder.putRate(l_Code, l_Rate);
//    		}
//    	}
//	
//    if (m_IndexGlobals != null)
//		{
//    	l_IndexIterator = m_IndexGlobals.iterator();
//    	while (l_IndexIterator.hasNext())
//			{
//    		l_Index = l_IndexIterator.next();
//    		p_Memory.setGlobal("Index_" + l_Index.getLabel(),l_Index);
//    		RulesObjectsHolder.putRateIndex(l_Index.getLabel(), l_Index);
//			}
//		}
//     }

//***************************************************************************
//* Class Body                                                              *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * applies rules to the specified invoice.
 * @param p_Invoice specifies the invoice to apply rules to.
 * @param p_AgendaGroups specifies which agenda group set to use. Possible
 * values are:
 * <ul>
 * <li> c_ClientAgendaGroups : Agenda Group file holding agenda groups to be
 * fired when called from client.</li>
 * <li>c_ServeAgendaGroups  : Agenda Group file holding agenda groups to be
 * fired when called from server. (default)</li>
 * </ul>
 * @return specified invoice having the rules applied to it.                       
 */
//---------------------------------------------------------------------------

public Invoice applyRules (Invoice p_Invoice, RuleOptions p_RuleOptions, String logComment) throws Exception
{
	log(Level.INFO, logComment);
	return applyRules(p_Invoice, p_RuleOptions);
}



/**
 * applies rules to the specified invoice.
 * @param p_Invoice specifies the invoice to apply rules to.
 * @param p_AgendaGroups specifies which agenda group set to use. Possible
 * @param p_RuleOptions An Object array which defines the rule settings, like
 * whether or not to use the session mode or 
 * values are:
 * <ul>
 * <li> c_ClientAgendaGroups : Agenda Group file holding agenda groups to be
 * fired when called from client.</li>
 * <li>c_ServeAgendaGroups  : Agenda Group file holding agenda groups to be
 * fired when called from server. (default)</li>
 * </ul>
 * @return specified invoice having the rules applied to it.                       
 */
//---------------------------------------------------------------------------

public Invoice applyRules (Invoice p_Invoice, RuleOptions p_RuleOptions) throws Exception
	{
//		StatefulSession 	l_WorkingMemory;
//		Set <Act>			l_Acts;
//		Iterator <Act>		l_ActIterator;
//		Act					l_Act;
//		Vector <String>		l_AgendaGroups;
//		Iterator <String>	l_GroupIterator;
//		String				l_AgendaGroup;
//		List <Act>			l_IgnoredActs;
		
		RuleInvoker				ruleEngine;
		RulesObjectsHolder		roh					= null;
		HashMap<Integer, Act>	oldActs				= null;
		Set<Integer>			generatedIds		= null;
		StringBuilder			invoiceChangesLog	= null;
		int						lastGeneratedId;
		String rulogDetails = "";
		
		p_Invoice.setRuleLog("RULEENGINE NOT APPLIED");
		
//		System.out.println("RuleBean.applyRules:");
//		for (Act act : p_Invoice.getActs())
//			System.out.println(act.print());
		
		if (p_RuleOptions == null)
		{
			// read rule options from admin settings and get patient's birthday from the patient linked with the invoice
			Date		patientsBirthdate	= p_Invoice.getPatient().getBirthDate();
			
			
			// set defaults
			p_RuleOptions = new RuleOptions(
					(Boolean) m_LoginInterface.getAdminSettingValue(BillingAdminSettings.c_ModuleName, BillingAdminSettings.c_UseSessionMode), 
					(Integer) m_LoginInterface.getAdminSettingValue(BillingAdminSettings.c_ModuleName, BillingAdminSettings.c_HospitalizedCumulationMode), 
					patientsBirthdate, 
					(Integer) m_LoginInterface.getAdminSettingValue(BillingAdminSettings.c_ModuleName, BillingAdminSettings.c_TiersPayantMinValue));
		}
		
		if (p_Invoice == null)
			return null;
		
		if (p_Invoice.isPersistent())
			p_Invoice = this.fetchDependenciesForInvoice(p_Invoice);
		
		try
		{
			// initialize the helper classes
			roh = new RulesObjectsHolder(p_Invoice, p_RuleOptions, m_NomenclatureInterface);
			
			// copy all acts to be able to see any changes while the rules have been applied
			oldActs = new HashMap<Integer, Act>();
			generatedIds = new HashSet<Integer>();
			lastGeneratedId = 0;
			for (Act a : p_Invoice.getActs())
			{
//				System.out.println(
//						a.getCode() + " - " + 
//						GECAMedUtils.getDateFormatter("yyyy-MM-dd HH:mm").format(a.getPerformedDate()) + " - " + 
//						a.getHospitalisationClass());
				
				a.setRulesObjectHolder(roh);
				if (!a.isPersistent())
				{
					// every acts needs an ID, to ensure it can be identified
					a.setId(Integer.valueOf(--lastGeneratedId));
					generatedIds.add(lastGeneratedId);
				}
				oldActs.put(a.getId(), a.updateFields(new Act()));
			}
			
//			l_WorkingMemory = m_BillingRules.newStatefulSession();
//			
//			setWorkingMemoryGlobals(l_WorkingMemory);
//			l_WorkingMemory.setGlobal("ROH", roh);
//			
//			l_WorkingMemory.insert(p_Invoice);
//			l_WorkingMemory.insert(p_Invoice.getPatient());
//			
//			if (p_Invoice.getHospitalisation() == null)
//				Hospitalisation.createHospitalisationForInvoice(p_Invoice);
//			
//			if (p_Invoice.getHospitalisation() != null)
//				l_WorkingMemory.insert(p_Invoice.getHospitalisation());
//			
//			this.log(Level.INFO, "Asserting Acts...");
//			
//			l_IgnoredActs = removeIgnoreActs(p_Invoice);
//			
//			l_Acts = p_Invoice.getActs();
//			l_ActIterator = l_Acts.iterator();
//			
//			while (l_ActIterator.hasNext())
//			{
//				l_Act = l_ActIterator.next();
//				l_WorkingMemory.insert(l_Act);
//			}
			
			this.log(Level.INFO, "Fireing Rules...");
			ruleEngine	= new RuleInvoker(roh);
			ruleEngine.applyRules();
			rulogDetails = roh.getRuleLog();
			
//			switch (p_AgendaGroups)
//			{
//				case RuleInterface.c_ClientAgendaGroups:
//					l_AgendaGroups = m_ClientAgendaGroups;
//					break;
//				
//				case RuleInterface.c_ServerAgendaGroups:
//					l_AgendaGroups = m_ServerAgendaGroups;
//					break;
//				
//				default:
//					l_AgendaGroups = m_ServerAgendaGroups;
//			}
//			
//			
//			if (l_AgendaGroups != null)
//			{
//				l_GroupIterator = l_AgendaGroups.iterator();
//				while (l_GroupIterator.hasNext())
//				{
//					l_AgendaGroup = l_GroupIterator.next();
//					l_WorkingMemory.setFocus(l_AgendaGroup);
//					l_WorkingMemory.fireAllRules();
//				}
//			}
//			
//			l_WorkingMemory.dispose();
//			addIgnoredActs(p_Invoice, l_IgnoredActs);
			
			// compare old and new acts, to check if the rules changed something
			Act oldAct;
			String actChangesLog;
			Integer id;
			
			for (Act newAct : p_Invoice.getActs())
			{
				id = newAct.getId();
				oldAct = oldActs.get(id);
				actChangesLog = Act.compareActs(oldAct, newAct, 3, true);
				if (actChangesLog != null)
				{
					newAct.setChanges(Act.CHANGED_RULEENGINE);
					if (invoiceChangesLog == null)
						invoiceChangesLog = new StringBuilder();
					invoiceChangesLog.append(actChangesLog)
							.append("\n");
				}
			}
		}
		catch (Exception p_Exception)
		{
			this.log(Level.WARN, p_Exception.getLocalizedMessage(), p_Exception);
		}
		finally
		{
			p_Invoice.removeAllActsChangedListener();
			
			if (p_Invoice.getHospitalisation() != null
					&& !p_Invoice.getHospitalisation().isPersistent())
				p_Invoice.setHospitalisation(null);
			
//			if (roh != null)
//				roh.destructor();
			
			for (Act newAct : p_Invoice.getActs())
			{
				if (generatedIds.contains(newAct.getId()))
					// remove the generated IDs
					newAct.setId(null);
			}
		}
		
			
		StringBuffer ruleLog = new StringBuffer("\n==== RULEENGINE CHANGE SUMMARY: =======================================\n");
		if (invoiceChangesLog != null) {
			ruleLog.append(invoiceChangesLog.toString());
			log(Level.DEBUG, invoiceChangesLog.toString());
		} else {
			ruleLog.append("\tNO CHANGES\n");
		}
		ruleLog.append("================================================================\n");
		
		ruleLog.append(rulogDetails);
		
		
		p_Invoice.setRuleLog(ruleLog.toString());
		
		if (RulesObjectsHolder.isDebug()) {
			log(Level.INFO, ruleLog.toString());
		}
		
		return p_Invoice;
	}

//private void addIgnoredActs(Invoice p_Invoice, List<Act> l_IgnoredActs)
//	{
//	Set<Act> l_Acts	= p_Invoice.getActs();
//	
//	l_Acts.addAll(l_IgnoredActs);
//	}
//
//private List<Act> removeIgnoreActs(Invoice p_Invoice)
//	{
//	List<Act> l_IgnoredActs	= new LinkedList<Act>();
//	Set<Act> l_Acts			= p_Invoice.getActs();
//	
//	for (Act l_Act : l_Acts)
//		{
//		for (RateIndex l_Index : m_IgnoredIndexes)
//			if (l_Index.includesAct(l_Act))
//			{
//				l_Act.setSuffixes("");
//				l_IgnoredActs.add(l_Act);
//			}
//		}
//	
//	for (Act l_Act : l_IgnoredActs)
//		l_Acts.remove(l_Act);
//	
////	p_Invoice.setActs(l_Acts);
//	
//	return l_IgnoredActs;
//	}
	
//***************************************************************************
//* End of Class															*
//***************************************************************************
	}
