/*******************************************************************************
 * 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.text.DecimalFormat;
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.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.httpclient.methods.GetMethod;

import lu.tudor.santec.gecamed.billing.utils.StatementWorkflow;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Physician;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Insurance;
import lu.tudor.santec.gecamed.usermanagement.ejb.entity.beans.GecamedUser;

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

/**
 * The Statement class encapulates data about third party paying statements
 * as defined by the UCM (Union des Caisses de Maladie). All invoices being
 * taken in charge by a third party paying intitution, such as the UCM or
 * the AAI (Assurance Accident), have to be accumulated over a specific period,
 * usually a month. At the end of this period, a listing containg all the
 * elligible invoices has to be printed as well as a copy of every invoice on
 * the listing. The Statement class contains all the data necessary to fullfil
 * this requirement.
 * @author nico.mack@tudor.lu
 * @since 06/12/15
 */

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

@NamedQueries 
	({
	@NamedQuery(name = Statement.c_AllStatements, query = "SELECT OBJECT(o) FROM Statement o ORDER BY o.reference ASC, o.id DESC")
	})

public class Statement extends GECAMedEntityBean implements Serializable 
	{
	private static final long serialVersionUID = 1L;

	private Account					m_Account;
	private String					m_Reference;
	private Integer					m_State;
	private Date					m_Start;
	private Date					m_End;
	private Boolean					m_AllPhysicians;
	private GecamedUser				m_Closer;
	private Date 					m_ClosureDate;
	private Date					m_SettlementDate;
	private Set <Insurance>			m_ThirdPartyPayers;
	private Set <Physician>			m_Physicians;
	
	private static final 	DecimalFormat m_StatementIDFormat 	= new DecimalFormat ("0000000");

//***************************************************************************
//* Constants			                                                    *
//***************************************************************************	

	public static final transient String c_AllStatements = "getAllStatements";

	public static final Statement DEFAULT_STATEMENT = new Statement();
	static {
		DEFAULT_STATEMENT.setId(-1);
	}
	
//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************	

public Statement ()
	{
	m_State = Integer.valueOf (0);
	}	

//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives                                       						*
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * Formats this statement's ID as a printable statement number.
 * @return a printable representation of this statement's number.
 */
//---------------------------------------------------------------------------

@Transient
public String formatStatementNumber ()
	{
	String	l_StatementNumber;
	long	l_ID;
	
	l_ID = (this.getId() != null)?this.getId().longValue():0L;
	
	l_StatementNumber = "T" + m_StatementIDFormat.format(l_ID);

	return l_StatementNumber;
	}

//---------------------------------------------------------------------------
/**
 * Formats the acronyms of the third party paying institutions this
 * statement is issued for.
 * @return a printable representation of the third party paying institutions
 * specified for this statement.
 */
//---------------------------------------------------------------------------

public String formatThirdPartyPayers ()
	{
	Iterator <Insurance> l_ThirdPartyIterator;
	Insurance			 l_ThirdPartyPayer;				
	String				 l_Label;
	String				 l_Separator;
	
	l_Label 	= new String ();
	l_Separator = new String (); 
	
	if (m_ThirdPartyPayers == null || m_ThirdPartyPayers.size() == 0) return "ALL";
	
	l_ThirdPartyIterator = m_ThirdPartyPayers.iterator();
	while (l_ThirdPartyIterator.hasNext())
		{
		l_ThirdPartyPayer = l_ThirdPartyIterator.next();
		l_Label += l_Separator + l_ThirdPartyPayer.getAcronym();
		l_Separator = "/";
		}
	return l_Label;
	}



public String formatPhysicians ()
{
	Iterator <Physician> l_PhysicianIterator;
	Physician			 l_Physician;				
	String				 l_Label;
	String				 l_Separator;
	
	l_Label 	= new String ();
	l_Separator = new String (); 
	
	if (m_AllPhysicians != null && m_AllPhysicians) return "ALL";
	
	if (m_Physicians == null) return "ALL";
	
	l_PhysicianIterator = m_Physicians.iterator();
	while (l_PhysicianIterator.hasNext())
		{
		l_Physician = l_PhysicianIterator.next();
		l_Label += l_Separator + l_Physician.getMnemonic();
		l_Separator = "/";
		}
	return l_Label;
}
	
//---------------------------------------------------------------------------
/**
 * The method checks whether the invoice whose state has been specified can still 
 * be modified or not. 
 * @param p_State specifies the state of the invoice whose lock state we'd like to have.
 * @return <code>true</code> if invoice is still open an can be modified,
 * <code>false</code> otherwise.
 */
//---------------------------------------------------------------------------

@Transient
public static boolean isSettled (Integer p_State)
	{
	return ((p_State != null) && (p_State.intValue() == StatementWorkflow.c_SettledState));
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Getter and Setter Methods	                                            *
//***************************************************************************	
//---------------------------------------------------------------------------
/**
 * Returns the bank account to be used to settle invoices on this statement.
 * @return The bank account to be used to settle invoices if already set,
 * <code>null</code> otherwise.
 */
//---------------------------------------------------------------------------

@ManyToOne (fetch = FetchType.EAGER)
@JoinColumn (name = "account_id")

public Account getAccount() 
	{
	return m_Account;
	}

//---------------------------------------------------------------------------
/**
 * Sets the account to be used to settled invoices on this statement.
 * @param p_Account specifies the bank account to be used.
 */
//---------------------------------------------------------------------------

public void setAccount (Account p_Account)
	{
	m_Account = p_Account;
	}

//---------------------------------------------------------------------------
/**
 * Returns the reference number of this statement
 * @return statement reference number
 */
//---------------------------------------------------------------------------

@Column (name ="reference")

public String getReference() 
	{
	return m_Reference;
	}

//---------------------------------------------------------------------------
/**
 * Sets the reference number for this statement
 * @param p_Reference specifies the new reference number
 */
//---------------------------------------------------------------------------

public void setReference (String p_Reference) 
	{
	m_Reference = p_Reference;
	}

//---------------------------------------------------------------------------
/**
 * Returns the state this statement is currently in
 * @return The current state of this statement
 */
//---------------------------------------------------------------------------

@Column(name = "state")

public Integer getState() 
	{
	return m_State;
	}

//---------------------------------------------------------------------------
/**
 * Sets the state of this statement
 * @param p_State specifies the new statement state
 */
//---------------------------------------------------------------------------

public void setState (Integer p_State) 
	{
	m_State = p_State;
	}

//---------------------------------------------------------------------------
/**
 * Returns the start date for this statement. The start date defines the
 * day as of which invoices will be considered
 * @return This statements start date
 */
//---------------------------------------------------------------------------

@Column (name ="start_date")

public Date getStartDate() 
	{
	return m_Start;
	}

//---------------------------------------------------------------------------
/**
 * Sets the start date for this statement.
 * @param p_Start specifies the new start date
 */
//---------------------------------------------------------------------------

public void setStartDate (Date p_Start) 
	{
	m_Start = (p_Start != null) ? new Date (p_Start.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
* Returns the end date for this statement. The start date defines the
 * day up to which invoices will be considered
 * @return This statements end date
 */
//---------------------------------------------------------------------------

@Column (name ="end_date")

public Date getEndDate() 
	{
	return m_End;
	}

//---------------------------------------------------------------------------
/**
 * Sets the end date for this statement.
 * @param p_End specifies the new end date
 */
//---------------------------------------------------------------------------

public void setEndDate (Date p_End) 
	{
	m_End = (p_End != null) ? new Date (p_End.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
 * Returns the state of the all physicians flag. When this flag is set,
 * this statement is for all physicians in the office.
 * @return <b>true</b> if statement is for all physicians, <b>false</b>
 * otherwise
 */
//---------------------------------------------------------------------------

@Column (name ="all_physicians")

public Boolean getAllPhysicians() 
	{
	return m_AllPhysicians;
	}

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

public void setAllPhysicians (Boolean p_AllPhysicians)
	{
	m_AllPhysicians = p_AllPhysicians;
	}

//---------------------------------------------------------------------------
/**
 * Returns the user who closed this statement
 * @return The closing user
 */
//---------------------------------------------------------------------------

@ManyToOne (fetch = javax.persistence.FetchType.LAZY)
@JoinColumn(name = "closer_id")

public GecamedUser getCloser() 
	{
	return m_Closer;
	}

//---------------------------------------------------------------------------
/**
 * Sets the user who closed this statement
  * @param p_Closer specifies the closing user
 */
//---------------------------------------------------------------------------

public void setCloser (GecamedUser p_Closer) 
	{
	m_Closer = p_Closer;
	}

//---------------------------------------------------------------------------
/**
 * Returns this statements' closure date
 * @return Statement closure date
 */
//---------------------------------------------------------------------------

@Column(name = "closure_date")

public Date getClosureDate() 
	{
	return m_ClosureDate;
	}

//---------------------------------------------------------------------------
/**
 * Sets the specified date as this statements' closure date
 * @param p_ClosureDate specifies the new statement closure date
 */
//---------------------------------------------------------------------------

public void setClosureDate (Date p_ClosureDate) 
	{
	m_ClosureDate = (p_ClosureDate != null) ? new Date (p_ClosureDate.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
 * Returns this statements' settlement date. This date will be used in
 * conjunction with the account property.
 * @return Statement settlement date
 * @see #getAccount()
 */
//---------------------------------------------------------------------------

@Column(name = "settlement_date")

public Date getSettlementDate() 
	{
	return m_SettlementDate;
	}

//---------------------------------------------------------------------------
/**
 * Sets the specified date as this statements' settlement number
 * @param p_SettlementDate specifies the new statement settlement date
 */
//---------------------------------------------------------------------------

public void setSettlementDate (Date p_SettlementDate) 
	{
	m_SettlementDate = (p_SettlementDate != null) ? new Date (p_SettlementDate.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
 * Returns the third party paying insurances this statement is issued for
 * @return Third party paying insurances this statement is issued for
 */
//---------------------------------------------------------------------------

@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
@JoinTable(name = "rel_statement_insurance", schema = "billing",
		   joinColumns 			= {@JoinColumn(name = "statement_id")},
		   inverseJoinColumns 	= {@JoinColumn(name = "insurance_id")})

public Set<Insurance> getThirdPartyPayers() 
	{
	return m_ThirdPartyPayers;
	}

//---------------------------------------------------------------------------
/**
 * Assigns the specified collection of third party paying insurances to this
 * statement.
 * @param p_ThirdPartyPayers specifies the new third party paying insurances
 */
//---------------------------------------------------------------------------

public void setThirdPartyPayers (Set<Insurance> p_ThirdPartyPayers) 
	{
	m_ThirdPartyPayers = p_ThirdPartyPayers;
	}

//---------------------------------------------------------------------------
/**
 * Returns the physicians whose invoices are covered by this statement 
 * @return A collection of physicians 
 */
//---------------------------------------------------------------------------

@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
		   @JoinTable(name = "rel_statement_physician", schema = "billing",
				   	 joinColumns 			= {@JoinColumn(name = "statement_id")},
				   	 inverseJoinColumns 	= {@JoinColumn(name = "physician_id")})

public Set<Physician> getPhysicians() 
	{
	return m_Physicians;
	}

//---------------------------------------------------------------------------
/**
 * Assigns the specified collection of physicians to this statement
  * @param p_Physicians specifies the new collection of physicians
 */
//---------------------------------------------------------------------------

public void setPhysicians (Set<Physician> p_Physicians) 
	{
	m_Physicians = p_Physicians;
	}

public String toString() {
	return ""+getId();
}

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