/*******************************************************************************
 * 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.entity.beans;

import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;

import lu.tudor.santec.gecamed.billing.ejb.session.beans.NomenclatureBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.NomenclatureInterface;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Hospitalisation;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

//***************************************************************************
//* Class Definition and Members                                            *
//***************************************************************************

/**
 * Rates in UCM nomenclature are grouped in chapters and sections. The RateIndex
 * class encapsulates data about these chapters and sections. It can be compared
 * to some kind of table of content of the UCM nomenclature. 
 * @author nico.mack@tudor.lu
 * @since 06/04/19
 */

@Entity
@Table(name = "rate_index", schema = "billing")

@NamedQueries 
	({
	@NamedQuery(name = RateIndex.c_RateIndexByLabel,  		query = "SELECT OBJECT(o) FROM RateIndex o WHERE o.label = :label"),
	@NamedQuery(name = RateIndex.c_RateIndexRoots,    		query = "SELECT OBJECT(o) FROM RateIndex o WHERE o.enclosingChapter IS NULL ORDER BY o.title"),
	@NamedQuery(name = RateIndex.c_AllPrivateRateIndexes,	query = "SELECT OBJECT(i) FROM RateIndex i " +
																	"WHERE i.enclosingChapter IS NULL " +
																	"AND i.label NOT IN ('G', 'T', 'L', 'DG', 'DT')"),
	@NamedQuery(name = RateIndex.c_MapCodeToRateLabel,		query = "SELECT r.code, i.label " + 
																	"FROM Rate r, RateIndex i " + 
																	"WHERE r.indexId = i.id " + 
																	"AND r.code IN (:codes)"),
	@NamedQuery(name = RateIndex.c_getRateLabelOfCode,		query = "SELECT i.label " + 
																	"FROM Rate r, RateIndex i " + 
																	"WHERE r.indexId = i.id " + 
																	"AND r.code = :code")
	})

public class RateIndex extends GECAMedEntityBean implements Serializable 
	{
	private static final long serialVersionUID = 1L;
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(Hospitalisation.class.getName());
	
	private String			m_Label;	
	private String			m_Title;	
	private Set <Rate>		m_Rates;
	private Set <RateIndex> m_SubChapters;
	private RateIndex		m_EnclosingChapter;
	private Date			m_Applicability;
	
	private static NomenclatureInterface m_NomenclatureInterface;
	
//***************************************************************************
//* Constants	                                                            *
//***************************************************************************	

	public static final transient String c_RateIndexByLabel 		= "getRateIndexByLabel";
	public static final transient String c_RateIndexRoots   		= "getRateIndexRoots";
	public static final transient String c_AllPrivateRateIndexes	= "getAllPrivateRateIndexes";
	public static final transient String c_MapCodeToRateLabel		= "mapCodeToRateLabel";
	public static final transient String c_getRateLabelOfCode		= "getRatelabelOfCode";
	
	
//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************	
	
//***************************************************************************
//* Getter and Setter Methods	                                            *
//***************************************************************************	
//---------------------------------------------------------------------------
/**
 * Returns the label defined for this index entry.
 * @return Label defined for this index entry
 */
//---------------------------------------------------------------------------

@Column (name ="label")

public String getLabel() 
	{
	return m_Label;
	}

//---------------------------------------------------------------------------
/**
 * Sets this label for this index entry
 * @param p_Label specifies the new label for this index entry
 */
//---------------------------------------------------------------------------

public void setLabel (String p_Label) 
	{
	m_Label = p_Label;
	}

//---------------------------------------------------------------------------
/**
 * Returns the title defined for this index entry.
 * @return Title defined for this index entry
 */
//---------------------------------------------------------------------------

@Column (name ="title")

public String getTitle() 
	{
	return m_Title;
	}

//---------------------------------------------------------------------------
/**
 * Sets the title for this index entry
 * @param p_Title specifies the new title for this index entry
 */
//---------------------------------------------------------------------------

public void setTitle (String p_Title) 
	{
	m_Title = p_Title;
	}
//---------------------------------------------------------------------------
/**
 * Returns the rates which are grouped under this index entry
 * @return All the rates refered to by this index entry
 */
//---------------------------------------------------------------------------

@OneToMany (cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn (name = "index_id")
@OrderBy("code ASC, applicability DESC")
public Set<Rate> getRates ()
	{
	return m_Rates;
	}

//---------------------------------------------------------------------------
/**
 * Sets the rates to be grouped by this index entry.
 * @param p_Rates specifies the rates that should be refered to by this index entry
 */
//---------------------------------------------------------------------------

public void setRates (Set<Rate> p_Rates)
	{
	m_Rates = p_Rates;
	}


//---------------------------------------------------------------------------
/**
* @return Returns the date as of which this rate_index is 
* applicable
*/
//---------------------------------------------------------------------------	
	
@Column (name ="applicability")

public Date getApplicability() 
	{
	return m_Applicability;
	}
	
//---------------------------------------------------------------------------
/**
* Sets this rate_index application date 
* @param p_Applicability specifies the application date for rate_index
*/
//---------------------------------------------------------------------------

public void setApplicability (Date p_Applicability) 
	{
	m_Applicability = (p_Applicability != null) ? new Date (p_Applicability.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
 * Returns the rate index entries directly underneath this one in the
 * table of content. To use the table of content metaphor again, if this
 * index entry is the chapter, then the entries directly underneath are the
 * sub chapters.
 * @return All the index entries directly underneath this one.
 */
//---------------------------------------------------------------------------

@OneToMany (cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn (name = "parent_id")
@OrderBy("label ASC")
public Set<RateIndex> getSubChapters ()
	{
	return m_SubChapters;
	}

//---------------------------------------------------------------------------
/**
 * Sets the specified index entries as sub chapters of this index entry.
 * @param p_SubChapters specifies the new sub chapter index entries of this
 * index entry.
 */
//---------------------------------------------------------------------------

public void setSubChapters (Set<RateIndex> p_SubChapters)
	{
	m_SubChapters = p_SubChapters;
	}

//---------------------------------------------------------------------------

@ManyToOne (cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn (name = "parent_id")
public RateIndex getEnclosingChapter ()
	{
	return m_EnclosingChapter;
	}

//---------------------------------------------------------------------------

public void setEnclosingChapter (RateIndex p_EnclosingChapter)
	{
	m_EnclosingChapter = p_EnclosingChapter;	
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Rule Engine Helpers                                       				*
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * the includesCode method checks whether this index entry, or any of its
 * sub chapters includes a rate with the specified code.
 * @param p_Code specifies the code of the rate to look for.
 * @return <code>true</code> if a matching rate was found in the rates refered
 * to by this index entry or one of its sub chapters, <code>false</code>
 * otherwise.
 */
//---------------------------------------------------------------------------

@Transient
public Boolean includesCode (String p_Code)
	{
	Iterator <RateIndex>	l_SubChapterIterator;
	RateIndex				l_SubChapter;

	Iterator <Rate> 		l_RateIterator;
	Rate					l_Rate;
	boolean					l_isIncluded = false;
	
	if (m_Rates != null)
		{
		l_RateIterator = m_Rates.iterator();
		while ((l_RateIterator.hasNext()) && !l_isIncluded)
			{
			l_Rate = l_RateIterator.next();
			l_isIncluded = l_Rate.getCode().equals(p_Code);
			}
		}

	if ((l_isIncluded == false) && (this.getSubChapters() != null))
		{
		l_SubChapterIterator = this.getSubChapters().iterator();
		while (l_SubChapterIterator.hasNext() && !l_isIncluded)
			{
			l_SubChapter = l_SubChapterIterator.next();
			l_isIncluded = l_SubChapter.includesCode(p_Code);
			}
		}
		
	return l_isIncluded;
	}

//---------------------------------------------------------------------------
/**
 * the includesAct method checks whether this index entry, or any of its
 * sub chapters includes a rate matching the specified act. In this case,
 * matching boils down to codes of both act and rate being equal.
 * @param p_Act specifies the act to look for a matching rate.
 * @return <code>true</code> if a matching rate was found in the rates refered
 * to by this index entry or one of its sub chapters, <code>false</code>
 * otherwise.
 * @see #includesCode (String)
 */
//---------------------------------------------------------------------------

@Transient
public Boolean includesAct (Act p_Act)
	{
	String			l_Code;
	
	l_Code = p_Act.getCode();
	return this.includesCode(l_Code);
	}

//---------------------------------------------------------------------------

public static RateIndex loadRateIndexByLabel (String p_Label)
	{
	RateIndex	l_RateIndex = null;
	
	if (m_NomenclatureInterface == null)
		{
		m_NomenclatureInterface = (NomenclatureInterface) ManagerFactory.getRemote(NomenclatureBean.class);
		}
	
	try {
		l_RateIndex = m_NomenclatureInterface.getRateIndexByLabel(p_Label);
		if (l_RateIndex != null) l_RateIndex = m_NomenclatureInterface.fetchLazyDependencies(l_RateIndex);
		}
	catch (Exception p_Exception) 
		{
		logger.log(Level.FATAL, "Failed to load Rate Index with label " + p_Label,p_Exception);
		}
	return l_RateIndex;
	}

	public String toString() {
		return getLabel() + " " + getTitle();
	}

//***************************************************************************
//* End of Class															*
//***************************************************************************
}
