/*******************************************************************************
 * 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.core.utils.printing.ireport;

import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Locale;
import java.util.Set;

import lu.tudor.santec.gecamed.address.ejb.entity.beans.GECAMedAddressBean;
import lu.tudor.santec.gecamed.address.ejb.session.beans.AddressManagerBean;
import lu.tudor.santec.gecamed.address.ejb.session.interfaces.AddressManagerInterface;
import lu.tudor.santec.gecamed.address.utils.AddressFormatter;
import lu.tudor.santec.gecamed.addressbook.ejb.entity.beans.Contact;
import lu.tudor.santec.gecamed.addressbook.ejb.entity.beans.ContactAddress;
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.session.beans.BillingPrinterBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.BillingPrinterInterface;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.PhoneType;
import lu.tudor.santec.gecamed.core.utils.AccidentNumberFormatter;
import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.core.utils.SSNChecker;
import lu.tudor.santec.gecamed.core.utils.Translator;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Office;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.OfficeAddress;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.OfficePhone;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Physician;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.PhysicianAddress;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientAddress;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientContact;
import lu.tudor.santec.gecamed.patient.utils.PatientNameFormatter;
import lu.tudor.santec.gecamed.usermanagement.ejb.entity.beans.GecamedUser;
import lu.tudor.santec.i18n.Translatrix;
import lu.tudor.santec.numberwriter.NumberWriter;

import org.apache.log4j.Level;

/**
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 * @author jens.ferring(at)tudor.lu
 * 
 * Helper class with static methods to format names/addresses/phones for printing.
 */

public class UtilFormatter implements Serializable, Translator
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	private static final long serialVersionUID = 1L;
	
	private static final String TAB 			= "\t";
	private static final String NEWLINE 		= System.getProperty("line.separator");	

	
	// replaced variables
	private static final String ROW_BREAK		= "%BR";
	private static final String TITLE			= "%TITLE";
	private static final String TITLE_FULL		= "%FULLTITLE";
	private static final String FIRST_NAME 		= "%FNAME";
	private static final String SUR_NAME 		= "%SNAME";
	private static final String MAIDEN_NAME 	= "%MNAME";
	private static final String FIRST_NAME_CAP 	= "%FNAMECAP";
	private static final String SUR_NAME_CAP 	= "%SNAMECAP";
	private static final String MAIDEN_NAME_CAP = "%MNAMECAP";
	private static final String STREET 			= "%STREET";
	private static final String NUMBER 			= "%STREETNO";
	private static final String ZIP 			= "%ZIP";
	private static final String ZIP_PREFIX	 	= "%ZIPPREFIX";
	private static final String LOCATION 		= "%LOCATION";
	private static final String COUNTRY 		= "%COUNTRY";
	private static final String SSN				= "%SSN";
	private static final String POLICY_NO		= "%POLICYNO";
	private static final String MARITAL_STATUS	= "%MARITAL";
	private static final String NAME 			= "%NAME";
	private static final String INFO			= "%INFO";
	private static final String WEBSITE			= "%WEB";
	private static final String UCM				= "%UCM";
	private static final String SPECIALITY		= "%SPEC";
	private static final String EMAIL 			= "%EMAIL";
	private static final String PHONE			= "%PHONE";
	private static final String MOBILE			= "%MOBILE";
	private static final String FAX				= "%FAX";
	private static final String NATIONALITY		= "%NATIONALITY";
	
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	/** the logger Object for this class */
//	private static org.apache.log4j.Logger apLogger = org.apache.log4j.Logger.getLogger(UtilFormatter.class.getName());
	private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(UtilFormatter.class.getName());
	
	private static SimpleDateFormat dateFormatter = new SimpleDateFormat();
	
	private Hashtable<String, String> translations = new Hashtable<String, String>();
	
	private AddressFormatter 		addressFormatter;
	private PatientNameFormatter 	patientFormatter;
	private UtilSettings 			utilSettings;
	
	private BillingPrinterInterface billingPrinter;
	
	private GECAMedAddressBean 		referenceAddress;
	
	private boolean forceOriginal = false;
	
	
	// DEFAULT PARAMETER:
	private Patient		defaultPatient;
	private Physician	defaultPhysician;
	private GecamedUser	defaultUser;
	private Office 		defaultOffice;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTOR
	/* ======================================== */
	
	public UtilFormatter (AddressManagerInterface p_AddressManager)
	{
		try {
			int printMode;
			
			utilSettings 	= new UtilSettings();
			printMode 		= utilSettings.getAddressStyle().intValue();
	
			addressFormatter 	= new AddressFormatter(printMode, p_AddressManager);
			patientFormatter 	= new PatientNameFormatter(printMode);
//			ssnFormatter 		= new SSNFormatter();
			
//			initTranslations();
			addressFormatter.setTranslator(this);
			patientFormatter.setTranslator(this);
	//		setSettings(new UtilSettings());
		}
		catch (Throwable t)
		{
			logger.log(Level.ERROR, t.getMessage(), t);
		}
	}
	
	
//	public UtilFormatter (int printMode)
//	{
//		init(new UtilSettings(), printMode, printMode);
//	}
//	
//	
//	public UtilFormatter (int addressPrintMode, int patientPrintMode)
//	{
//		init(new UtilSettings(), addressPrintMode, patientPrintMode);
//	}
	
	
	public void initTranslations ()
	{
		if (translations == null
				|| translations.isEmpty())
			translations = Translatrix.getTranslationsCopy();
	}
	
	
	
	/* ======================================== */
	// 		GETTER AND SETTER
	/* ======================================== */
	
	
	public void setSettings (UtilSettings settings)
	{
		int printMode;
		
		utilSettings 	= settings;
		printMode 		= utilSettings.getAddressStyle().intValue();
		setAddressPrintMode(printMode);
		setPatientNamePrintMode(printMode);
	}
	
	
	public UtilSettings getSettings ()
	{
		return this.utilSettings;
	}
	
	
	public Patient getDefaultPatient()
	{
		return defaultPatient;
	}
	
	
	public void setDefaultPatient(Patient patient)
	{
		this.defaultPatient = patient;
	}
	
	
	public Physician getDefaultPhysician()
	{
		return defaultPhysician;
	}
	
	
	public void setDefaultPhysician(Physician physician)
	{
		this.defaultPhysician = physician;
	}
	
	
	public GecamedUser getDefaultUser()
	{
		return defaultUser;
	}
	
	
	public void setDefaultUser(GecamedUser user)
	{
		this.defaultUser = user;
	}
	
	
	public Office getDefaultOffice()
	{
		return defaultOffice;
	}
	
	
	public void setDefaultOffice(Office office)
	{
		this.defaultOffice = office;
	}
	
	
	public void setAddressPrintMode (int printMode)
	{
		addressFormatter.setPrintMode (printMode);
	}
	
	
	public void setPatientNamePrintMode (int printMode)
	{
		patientFormatter.setPrintMode (printMode);
	}
	
	
	public void setReferenceAddress (GECAMedAddressBean referenceAddress)
	{
		this.referenceAddress = referenceAddress;
	}
	
	
	public void setReferenceOffice (Office office)
	{
		this.defaultOffice = office;
	}
	
	
	public String translate (String key)
	{
		initTranslations();
		return translations.get(key);
	}
	
	
	public void addTranslation (String key, String value)
	{
		initTranslations();
		if (key != null && value != null)
			translations.put(key, value);
		else if (key == null)
			logger.warn("Couldn't add translation, because the key was null");
		else // if (value == null)
			logger.warn("Couldn't add translation for key \""+key+"\", because the value was null");
	}
	
	
	public BillingPrinterInterface getBillingPrinter()
	{
		if (billingPrinter == null)
			billingPrinter = (BillingPrinterInterface) ManagerFactory.getRemote(BillingPrinterBean.class);
		return billingPrinter;
	}
	
	
	public void setBillingPrinter(BillingPrinterInterface billingPrinter)
	{
		this.billingPrinter = billingPrinter;
	}
	
	
	
	/* ======================================== */
	// 		PRIMITIVES
	/* ======================================== */

	public String formatAddressBlock (GECAMedAddressBean p_Address)	
	{
		if (p_Address != null) 
			 return addressFormatter.formatAddressBlock (p_Address, p_Address.isAbroadRelativeTo(referenceAddress));
		else return translate("AddressFormatter.Unavailable");
	}
	
	
	public String formatAddressLine (GECAMedAddressBean p_Address)	
	{
		if (p_Address != null) 
			 return addressFormatter.formatAddressLine (p_Address, p_Address.isAbroadRelativeTo(referenceAddress));
		else return translate("AddressFormatter.Unavailable");
	}
	
	
	public String formatPhoneLine (String label, String number, boolean newline)
	{
		StringAppender phoneLine = new StringAppender ();
		
		if (number != null && number.length() > 0)
		{
			label = label != null ? label : "";
			phoneLine.append(label).append(": ").append(TAB);
			phoneLine.append(number);
			if (newline) 
				phoneLine.append(NEWLINE);
		}
		
		return phoneLine.toString();
	}
	
	
	
	/* ======================================== */
	// 		OFFICE RELATED FORMATTERS
	/* ======================================== */
	
	public String getTitle (Patient patient)
	{
		String title = patient.getTitle();
		if ("madam".equals(title))
			title = translate("Title.madam");
		else if ("miss".equals(title))
			title = translate("Title.miss");
		else if ("mister".equals(title))
			title = translate("Title.mister");
		else 
			title = null;
		
		return title;
	}
	
	
	public String formatOfficeLine ()
	{
		return formatOfficeLine(defaultOffice);
	}
	
	
	/**
	 * formats address of specified office as a single printable line.
	 * @param office specified the medical office to format address of.
	 * @return the address of the specified office as a single printable line
	 * @see #buildSingleLineAddress()
	 */
	public String formatOfficeLine (Office office)
	{
		OfficeAddress address;
		
		if (office == null) return translate("TemplateFormatter.NoOffice");
		address = office.getLatestOfficeAddress();
		return formatAddressLine (address);
	}
	
	
	public String formatOfficeAddress ()
	{
		return formatOfficeAddress(defaultOffice);
	}
	
	
	/**
	 * formats address of specified office as printable address block.
	 * @param office specified the medical office to format address of.
	 * @return the address of the specified office as a printable address block
	 * @see #buildAddressBlock()
	 */
	public String formatOfficeAddress (Office office)
	{
		if (office == null) 
			 return translate("TemplateFormatter.NoOffice");
		else return formatAddressBlock (office.getLatestOfficeAddress());
	}
	
	
	public String formatOfficePhones ()
	{
		return formatOfficePhones(defaultOffice, defaultPhysician);
	}
	
	
	/**
	  * creates a multi-line description of the office phone numbers
	  * @param office the office
	  * @return the office phones in several lines
	  */
	public String formatOfficePhones (Office office, Physician physician)
	{
		StringAppender 	phones = new StringAppender ();
		String			number = null;
		OfficePhone 	officeNumber;
		
		if (physician != null) 
		{
		    // add private tel of physician if set, else tel of office
			number = physician.getPhoneExtension();
			if (number != null && number.length() > 0) 
			{
				phones.append(formatPhoneLine("Tel.", number,	true));
			} 
			else if (office != null) 
			{
				officeNumber = office.getOfficePhoneByType(PhoneType.HOME);
				if (officeNumber != null) 
				{
					number = officeNumber.getNumber();
					phones.append(formatPhoneLine("Tel.", number, true));
				}
			}

			// add private tel of physician if set
			number = physician.getGsm();
			phones.append(formatPhoneLine("GSM.",number,true));
		
			// add fax of physician if set, else fax of office
        		number = physician.getFax();
        		if (number != null && number.length() > 0)
        		{
        			phones.append(formatPhoneLine("Fax.",number,true));
        		}
        		else if (office != null)
        		{
        			officeNumber = office.getOfficePhoneByType (PhoneType.FAX);
        			if (officeNumber != null)
        			{
        				number = officeNumber.getNumber();
        				phones.append(formatPhoneLine("Fax.",number,true));
        			}
        		}
        		
        	// no physician, use office values
        } 
		else 
		{
			officeNumber = office.getOfficePhoneByType(PhoneType.HOME);
			if (officeNumber != null) 
			{
				number = officeNumber.getNumber();
				phones.append(formatPhoneLine("Tel.", number, true));
			}
			
			officeNumber = office.getOfficePhoneByType (PhoneType.FAX);
			if (officeNumber != null)
			{
			    number = officeNumber.getNumber();
			    phones.append(formatPhoneLine("Fax.",number,true));
			}
        }
		
		return phones.toString();
	}
	
	
	public String formatLocalityAndDate (Date date, String datePattern)
	{
		return formatLocalityAndDate(defaultOffice, date, datePattern);
	}
	
	
	public String formatLocalityAndDate (Office office, Date date, String datePattern)
	{
		dateFormatter.applyPattern(datePattern);
		return formatLocalityAndDate(office, date, dateFormatter);
	}
	
	
	public String formatLocalityAndDate (Date date, String style, String locale)
	{
		return formatLocalityAndDate(defaultOffice, date, style, locale);
	}
	
	
	public String formatLocalityAndDate (Office office, Date date, String style, String locale)
	{
		DateFormat dateFormat = DateFormat.getDateInstance(getStyle(style), getLocaleOfString(locale));
		return formatLocalityAndDate(office, date, dateFormat);
	}
	
	
	/**
	 * Returns a printable representation of office locality and current date in
	 * the form ANYTOWN, le DD.MM.YYYY.
	 * 
	 * @param office specifies the office to use locality from. If p_Office is
	 *                 <code>null</code> only current date will be returned.
	 * @return a printable representation of office locality and current date.
	 */
	private String formatLocalityAndDate (Office office, Date date, DateFormat dateFormat) 
	{
		OfficeAddress 	address;
		String[] 		filler;
		String 			formattedDate;

		formattedDate = dateFormat.format(date);
		
		if (office == null) return formattedDate;

		address = office.getLatestOfficeAddress();

		if (address == null) return formattedDate;

		filler = new String[2];
		filler[0] = address.getLocality().toUpperCase();
		filler[1] = formattedDate;

		return Translatrix.getTranslationString("Address.AddressAndDate", filler);
	}
	
	
	/**
	  * creates a Address-Header for a letter to a Patient
	  *
	  * @param p the Patient
	  * @return  the Patients Name and Address formated for a letter
	  */
	public String formatPatientBillingAddress (Patient patient)
	{
		PatientAddress address = null;
		
		if (patient == null) 
			return translate("TemplateFormatter.NoPatient");
		
		address = patient.getBillingAddress();
		if (address == null) 
		{
			for (PatientAddress pa : patient.getAddress())
			{
				address = pa;
				break;
			}
			if (address == null)
				return "";
		}
		
		return formatAddressBlock(address);
	}
	
	
	public String formatPatientHomeAddress ()
	{
		return formatPatientHomeAddress(defaultPatient);
	}
	
	
	/**
	  * creates a Address-Header for a letter to a Patient
	  *
	  * @param p the Patient
	  * @return  the Patients Name and Address formated for a letter
	  */
	public String formatPatientHomeAddress (Patient patient)
	{
		PatientAddress address = null;
		
		if (patient == null) 
			return translate("TemplateFormatter.NoPatient");
	
		address = patient.getHomeAddress();
		if (address == null) 
		{
//			return translate("AddressFormatter.Unavailable");
			for (PatientAddress pa : patient.getAddress())
			{
				address = pa;
				break;
			}
			if (address == null)
				return "";
		}
		
		return formatAddressBlock(address);
	}
	
	
	/**
	 * Returns a printable representation of specified patient's address.
	 * Depending on whether patient is backed by a guarantor or not, address of
	 * guarantor will be used instead of patient's address. An additional flag
	 * allows to decide whether patient name should be included or not.
	 * 
	 * @param patient specifies the patient to get printable representation of
	 *            		address for.
	 * @param p_WithName specifies whether or not to include patient name.
	 * @return a printable representation of specified patient's address
	 */
	public String formatGuarantorAddress (Patient patient) 
	{
		PatientContact guarantor = null;
		
		if (patient == null) 
			return translate("TemplateFormatter.NoPatient");	
		
		guarantor = patient.getPatientContact();
		if (guarantor != null)
			 return formatAddressBlock (guarantor);
		else return formatPatientBillingAddress (patient);
	}
	

	public String formatPhysicianAddress ()
	{
		return formatPhysicianAddress (defaultPhysician);
	}
	
	
	public String formatPhysicianAddress (Physician physician)
	{
		PhysicianAddress address;
	
		if (physician == null) 
			return translate("TemplateFormatter.NoPhysician");
		
		address = physician.getPhysicianAddress();
		if (address != null) 
			 return formatAddressBlock (address);
		else return this.formatOfficeAddress (defaultOffice);
	}
	
	
	public String formatPhysicianLine ()
	{
		return formatPhysicianLine(defaultPhysician);
	}
	
	
	public String formatPhysicianLine (Physician physician)
	{
		PhysicianAddress address;
	
		if (physician == null) 
			return translate("TemplateFormatter.NoPhysician");
		
		address = physician.getPhysicianAddress();
		if (address != null) 
			 return formatAddressLine (address);
		else return this.formatAddressLine(defaultOffice.getLatestOfficeAddress());
	}
	
	
	public String formatPhysicianName ()
	{
		return formatPhysicianName(defaultPhysician, 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_WITH_TITLE)).booleanValue(), 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_SWITCH_NAMES)).booleanValue(), 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_WITH_COMMA)).booleanValue());
	}
	
	
	public String formatPhysicianName (boolean withTitle)
	{
		return formatPhysicianName(defaultPhysician, withTitle, 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_SWITCH_NAMES)).booleanValue(), 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_WITH_COMMA)).booleanValue());
	}
	
	
	public String formatPhysicianName (Physician physician)
	{
		return formatPhysicianName(physician, 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_WITH_TITLE)).booleanValue(), 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_SWITCH_NAMES)).booleanValue(), 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_WITH_COMMA)).booleanValue());
	}
	
	
	public String formatPhysicianName (Physician physician, boolean withTitle)
	{
		return formatPhysicianName(physician, withTitle, 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_SWITCH_NAMES)).booleanValue(), 
				((Boolean)utilSettings.getSetting(UtilSettings.SETTING_PHYSICIAN_WITH_COMMA)).booleanValue());
	}
	
	
	public String formatPhysicianName (Physician physician, boolean withTitle, boolean firstNameLast, boolean withComma)
	{
		if (physician == null) 
			return translate("TemplateFormatter.NoPhysician");
		
		String	firstName	= firstNameLast ? physician.getName() : physician.getFirstName();
		String	lastName	= firstNameLast ? physician.getFirstName() : physician.getName();
		String	title		= physician.getTitle();
		
		
		StringAppender rendered = new StringAppender();
		if (withTitle && !GECAMedUtils.isEmpty(title))
		    rendered.append(title + " ");
		
		if (!GECAMedUtils.isEmpty(firstName))		
		    rendered.append(firstName);
		
		if (!GECAMedUtils.isEmpty(lastName))
		{
			if (!GECAMedUtils.isEmpty(firstName))
				rendered.append(withComma ? ", " : " ");
			rendered.append(lastName);
		}
		
		return rendered.toString().trim();
	}
	
	
	public String formatPhysicianCode (Act act)
	{
		Physician p;
		String code;
		
		if (utilSettings.printPhysicanCNSCode())
		{
			p = act.fetchPhysician();
//			code = p.getUcmFacturation();
//			if (code == null)
				code = p.getUcmCode();
			if (code == null)
				code = "";
		}
		else 
		{
		 	// option is to print no code
			code = "";
		}
		
		return code;
	}

	
	public String formatContactLine (Contact contact) 
		{
		ContactAddress address;
			
		if (contact == null) 
			return translate("TemplateFormatter.NoContact");
		address = contact.getContactAddress();
		return formatAddressLine (address);
		}
	
	
	public String formatContactAddress (Contact contact)
	{
		ContactAddress address;
	
		if (contact == null) 
			return translate("TemplateFormatter.NoContact");
		address = contact.getContactAddress();
		return formatAddressBlock (address);
	}
	
	
	public String formatPatientName (boolean withTitle, boolean firstNameLast)
	{
		return formatPatientName(defaultPatient, withTitle, firstNameLast);
	}
	
	
	public String formatPatientName (Patient patient, boolean withTitle, boolean firstNameLast)
	{
		return formatPatientName(patient, withTitle, firstNameLast, firstNameLast);
	}
	
	
	public String formatPatientName (Patient patient, boolean withTitle, boolean firstNameLast, boolean withComma)
	{
		return patientFormatter.formatPatientName (patient, withTitle, firstNameLast, withComma);
	}
	
	
	public String formatGuarantorName (Patient patient, boolean withTitle, boolean firstNameLast)
	{
		return patientFormatter.formatGuarantorName (patient, withTitle, firstNameLast);
	}
	
	
	public String formatAccidentNumber (Date date)
	{
		return AccidentNumberFormatter.getAccidentNumber(date);
	}
	
	
	public String formatAccountList (Physician physician)
	{
		return getBillingPrinter().formatAccountList(physician);
	}
	
	
	public double getBailiffRate(Invoice invoice) throws Exception
	{
		return getBillingPrinter().getBailiffRateForInvoice(invoice);
	}

	
	
	/* ======================================== */
	// 		FORMAT DATE & TIME
	/* ======================================== */
	
	/**
	 * Formats the date to a string, using the given pattern
	 * 
	 * @param date
	 * @param pattern
	 * @return the formated date
	 */
	public String formatDate (Date date, String pattern)
	{
		dateFormatter.applyPattern(pattern);
		return dateFormatter.format(date);
	}
	
	/**
	 * Formats the date to a string, using the short French pattern
	 * 
	 * @param date
	 * @return the formated date
	 */
	public String formatDateFR (Date date)
	{
		return DateFormat.getDateInstance(DateFormat.SHORT, Locale.FRENCH).format(date);
	}
	
	/**
	 * Formats the date to a string, using the short US pattern
	 * 
	 * @param date
	 * @return the formated date
	 */
	public String formatDateUS (Date date)
	{
		return DateFormat.getDateInstance(DateFormat.SHORT, Locale.US).format(date);
	}
	
	/**
	 * Formats the date to a string, using the short German pattern
	 * 
	 * @param date
	 * @param unaccentPattern
	 * @return the formated date
	 */
	public String formatDateDE (Date date)
	{
		return DateFormat.getDateInstance(DateFormat.SHORT, Locale.GERMAN).format(date);
	}
	
	
	public String formatDate (Date date)
	{
		return DateFormat.getDateInstance(DateFormat.SHORT).format(date);
	}
	
	
	public String formatDate (Date date, int style, String locale)
	{
		Locale l = getLocaleOfString(locale);
		return DateFormat.getDateInstance(style, l).format(date);
	}
	
	
	public String formatDate (Date date, String style, String locale)
	{
		return formatDate (date, getStyle(style), locale);
	}
	
	/**
	 * Returns the time of a given date and format it with the pattern 'hh:mm a' eg: 06:30 AM or 06:30 PM 
	 * 
	 * @param date
	 * @return the format time as a string
	 */
	public String formatTime12 (Date date)
	{
		dateFormatter.applyPattern("hh:mm a");
		return dateFormatter.format(date);
	}
	
	/**
	 * Returns the time of a given date and format it with the pattern 'HH:mm' eg: 06:30 or 18:30
	 * 
	 * @param date
	 * @return the format time as a string
	 */
	public String formatTime24 (Date date)
	{
		dateFormatter.applyPattern("HH:mm");
		return dateFormatter.format(date);
	}
	
	
	public String getAge (Date date)
	{
		Calendar	today	= new GregorianCalendar();
		Calendar	birthday= new GregorianCalendar();
		int			year;
		
		birthday.setTime(date);
		year	= today.get(Calendar.YEAR) - birthday.get(Calendar.YEAR);
		
		if (today.get(Calendar.MONTH) <= birthday.get(Calendar.MONTH)
				&& today.get(Calendar.DAY_OF_MONTH) <= birthday.get(Calendar.DAY_OF_MONTH))
			year++;
		
		return String.valueOf(year);
	}
	
	
	
	/* ======================================== */
	// 		FORMAT NUMBER
	/* ======================================== */
	
	/**
	 * Creates a String out of the number and replaces the '.' by ','
	 * 
	 * @param number the number to format
	 * @return
	 */
	public String formatNumber (Number number)
	{
		return formatNumber(number.doubleValue());
	}

	/**
	 * Creates a String out of the number and replaces the '.' by ','
	 * 
	 * @param number the number to format
	 * @return
	 */
	public String formatNumber (double number)
	{
		if (number % 1 > 0)
			return String.valueOf(number).replace('.', ',');
		else
			return String.valueOf((long)number);
	}

	/**
	 * Writes the number
	 * 
	 * @param number the number to format
	 * @return
	 */
	public String formatNumber (long number)
	{
		return String.valueOf(number);
	}
	
	/**
	 * Formats the number by replacing the decimal dot and 
	 * rounding on decimalPlaces decimal places
	 * 
	 * @param number
	 * @param decimalPlaces
	 * @return
	 */
	public String formatNumber (Number number, int decimalPlaces)
	{
		return formatNumber(number.doubleValue(), decimalPlaces);
	}

	/**
	 * Formats the number by replacing the decimal dot and 
	 * rounding on decimalPlaces decimal places
	 * 
	 * @param number
	 * @param decimalPlaces
	 * @return
	 */
	public String formatNumber (double number, int decimalPlaces)
	{
		if (decimalPlaces == 0)
			return String.valueOf(Math.round(number));
		
		if (decimalPlaces < 0)
			return formatNumber(number);
		
		String numberString = String.valueOf(Math.round(number * (Math.pow(10, decimalPlaces))));
		int size = numberString.length();
		return numberString.substring(0, size - decimalPlaces)
				.concat(",")
				.concat(numberString.substring(size - decimalPlaces));
	}
	
	
	/**
	 * Writes the given number in words in the language of the given locale.<br>
	 * E.g. writeNumber(2.489, "en", " euro ",  2) will return the <br>
	 * String "two euro fortyeight"
	 * 
	 * @param number The number to write
	 * @param speech A String representing the language, respectively a locale which language is taken to write the number
	 * @param delimiter The delimiter set at the position of the comma
	 * @param decimalPlaces The number of decimal places the number is rounded to
	 * @return A String representing the number in words.
	 */
	public String writeNumber (Number number, String speech, String delimiter, int decimalPlaces)
	{
		return NumberWriter.writeNumber(number.doubleValue(), getLocaleOfString(speech), delimiter, decimalPlaces);
	}
	
	
	/**
	 * Takes the social security number in any format and formats it
	 * to the form "#### ## ## ###"
	 * 
	 * @param ssn the social security number to format
	 * @return
	 */
	public String formatSSN (Object ssn)
	{
		if (ssn == null)
			return "";
		
		return SSNChecker.getFormattedSSN(String.valueOf(ssn), false);
	}
	
	
	
	/* ======================================== */
	// 		PATTERN REPLACEMENT
	/* ======================================== */
	
	public String formatPatientData (String pattern)
	{
		return formatPatientData(pattern, defaultPatient, null, true);
	}
	
	
	public String formatPatientData (String pattern, Patient patient)
	{
		return formatPatientData(pattern, patient, null, true);
	}
	
	
	public String formatPatientData (String pattern, String addressType)
	{
		return formatPatientData (pattern, defaultPatient, addressType, true);
	}
	

	public String formatPatientData (String pattern, Patient patient, String addressType)
	{
		return formatPatientData(pattern, patient, addressType, true);
	}
	
	
	public String formatPatientData (String pattern, String addressTye, boolean cutNameIfTooLong)
	{
		return formatPatientData(pattern, defaultPatient, addressTye, cutNameIfTooLong);
	}
	
	
	public String formatPatientData (String pattern, Patient patient, String addressType, boolean cutNameIfTooLong)
	{
		GECAMedAddressBean 			address = null;
		Iterator<PatientAddress> 	iterator;
		
		String firstName 	= patient.getFirstName() .trim().replace("  ", " ");
		String surName 		= patient.getSurName()	 .trim().replace("  ", " ");
		String maidenName 	= patient.getMaidenName().trim().replace("  ", " ");
		
		if (cutNameIfTooLong)
		{
			String[] name;
			if (firstName != null && firstName.length() > 15)
			{
				name = firstName.split(" ");
				if (name.length > 2)
					firstName = name[0] + " " + name[1];
			}
			
			if (surName != null && surName.length() > 15)
			{
				name = surName.split(" ");
				if (name.length > 2)
					surName = name[0] + " " + name[1];
			}
			
			if (maidenName != null && maidenName.length() > 15)
			{
				name = maidenName.split(" ");
				if (name.length > 2)
					maidenName = name[0] + " " + name[1];
			}
		}
		
//		String ssn = Patient.getPrettyFormattedSSN(patient.getSocialSecurityNumber());
		String ssn = SSNChecker.getFormattedSSN(patient.getSocialSecurityNumber(), false);
		if (SSNChecker.PATTERN_EMPTY_SSN.matcher(ssn).matches())
			ssn = null;
		pattern = replaceSpacer(pattern, ROW_BREAK, 		NEWLINE);
		pattern = replaceSpacer(pattern, TITLE, 			translate("Title."+patient.getTitle()));
		pattern = replaceSpacer(pattern, TITLE_FULL,		translate("Title.Long."+patient.getTitle()));
		pattern = replaceSpacer(pattern, FIRST_NAME, 		firstName);
		pattern = replaceSpacer(pattern, SUR_NAME, 			surName);
		pattern = replaceSpacer(pattern, MAIDEN_NAME, 		maidenName);
		pattern = replaceSpacer(pattern, FIRST_NAME_CAP, 	firstName	== null ? null : firstName.toUpperCase());
		pattern = replaceSpacer(pattern, SUR_NAME_CAP, 		surName		== null ? null : surName.toUpperCase());
		pattern = replaceSpacer(pattern, MAIDEN_NAME_CAP, 	maidenName	== null ? null : maidenName.toUpperCase());
		pattern = replaceSpacer(pattern, SSN, 				ssn);
		pattern = replaceSpacer(pattern, POLICY_NO, 		patient.getPolicyNumber());
		pattern = replaceSpacer(pattern, MARITAL_STATUS, 	translate("MaritalStatus."+patient.getMaritalStatus()));
		pattern = replaceSpacer(pattern, NATIONALITY, 		translate("Nationality."+patient.getNationality()));
		
		// get the address bean
		if (addressType != null)
		{
			if (addressType.toLowerCase().equals("home"))
				address = patient.getHomeAddress();
			else if (addressType.toLowerCase().equals("billing"))
				address = patient.getBillingAddress();
			
			if (address == null)
			{
				Set<PatientAddress> addresses = patient.getAddress();
				if (addresses != null
						&& !addresses.isEmpty()
						&& (iterator = addresses.iterator()).hasNext())
					address = iterator.next();
			}
		}
		pattern = replaceAddress(pattern, address);
		
		return pattern;
	}
	
	
	private String replaceSpacer (String wholeText, String textToReplace, String textToInsert)
	{
		if (textToInsert == null)
			return wholeText.replaceAll("(?<!\\w)"+textToReplace+"(?!\\w) ?", "");
		else
			return wholeText.replaceAll("(?<!\\w)"+textToReplace+"(?!\\w)", textToInsert);
	}
	
	
	public String formatPhysicianData (String pattern)
	{
		return formatPhysicianData(pattern, defaultPhysician);
	}
	
	
	public String formatPhysicianData (String pattern, Physician physician)
	{
		pattern = replaceSpacer(pattern, ROW_BREAK, 	NEWLINE);
		pattern = replaceSpacer(pattern, TITLE, 		translate(physician.getTitle()));
		pattern = replaceSpacer(pattern, FIRST_NAME, 	physician.getFirstName());
		pattern = replaceSpacer(pattern, SUR_NAME, 		physician.getName());
		pattern = replaceSpacer(pattern, FIRST_NAME_CAP,physician.getFirstName() == null ? null :physician.getFirstName().toUpperCase());
		pattern = replaceSpacer(pattern, SUR_NAME_CAP, 	physician.getName()		 == null ? null :physician.getName().toUpperCase());
		pattern = replaceSpacer(pattern, UCM,			physician.getUcmCode());
		pattern = replaceSpacer(pattern, SPECIALITY,	physician.getSpeciality());
		pattern = replaceSpacer(pattern, EMAIL,			physician.getEmail());
		pattern = replaceSpacer(pattern, MOBILE,		physician.getGsm());
		pattern = replaceSpacer(pattern, PHONE,			physician.getPhoneExtension());
		pattern = replaceSpacer(pattern, FAX,			physician.getFax());
		
		pattern = replaceAddress(pattern, physician.getPhysicianAddress());
		
		return pattern;
	}
	
	
	public String formatOfficeData (String pattern)
	{
		return formatOfficeData(pattern, defaultOffice);
	}
	
	
	public String formatOfficeData (String pattern, Office office)
	{
		String 		type;
		String 		phoneNo;
		OfficePhone phone;
		int 		index;
		int 		endIndex;

		pattern = replaceSpacer(pattern, ROW_BREAK, 	NEWLINE);
		pattern = replaceSpacer(pattern, NAME, 			office.getName());
		pattern = replaceSpacer(pattern, UCM,			office.getUcmCode());
		pattern = replaceSpacer(pattern, EMAIL,			office.getEmail());
		pattern = replaceSpacer(pattern, WEBSITE,		office.getWebsite());
		pattern = replaceSpacer(pattern, INFO,			office.getInformation());
		
		// find the phone numbers
		while ((index = pattern.indexOf(PHONE)) > 0)
		{
			index += PHONE.length();
			endIndex = index;
			while (pattern.charAt(endIndex) != '%')
				endIndex++;
			type = pattern.substring(index, endIndex);
			
			phone = office.getOfficePhoneByType(type);
			if (phone != null
					&& phone.getNumber() != null)
				phoneNo = phone.getNumber();
			else
				phoneNo = "";
			
			pattern = pattern.replace(PHONE+type+"%", phoneNo);
		}
		
		pattern = replaceAddress(pattern, office.getLatestOfficeAddress());
		
		return pattern;
	}
	
	
	
	/* ======================================== */
	// 		HELP METHODS
	/* ======================================== */
	
	private String replaceAddress (String pattern, GECAMedAddressBean address)
	{
		String					zipPrefix;
		AddressManagerInterface	manager;
		
		if (address != null)
		{
			if (address.isAddressInLuxembourg()) 
				zipPrefix	= "L-";
			else
			{
				manager		= (AddressManagerInterface) ManagerFactory.getRemote(AddressManagerBean.class);
				zipPrefix	= manager.getPostalCodeFromCountry(address.getCountry());
				if (zipPrefix != null && zipPrefix.length() != 0)
					 zipPrefix += "-";
				else zipPrefix = "";
			}
			
			pattern = replaceSpacer(pattern, NUMBER, 	address.getStreetNumber());
			pattern = replaceSpacer(pattern, STREET, 	address.getStreetName());
			pattern = replaceSpacer(pattern, ZIP, 		address.getZip());
			pattern = replaceSpacer(pattern, ZIP_PREFIX,zipPrefix);
			pattern = replaceSpacer(pattern, LOCATION, 	address.getLocality());
			pattern = replaceSpacer(pattern, COUNTRY, 	translate("Country."+address.getCountry()));
			return pattern;
		}
		else
		{
			pattern = replaceSpacer(pattern, NUMBER, 	"");
			pattern = replaceSpacer(pattern, STREET, 	"");
			pattern = replaceSpacer(pattern, ZIP, 		"");
			pattern = replaceSpacer(pattern, LOCATION, 	"");
			pattern = replaceSpacer(pattern, COUNTRY, 	"");
			return pattern;
		}
	}
	
	
	private Locale getLocaleOfString (String locale)
	{
		String language;
		String country;
		String[] array;
		
		if (locale == null
				|| locale.toLowerCase().equals("default")
				// the locale must have the form "xx_xx" or "xx"
				|| !locale.matches("[A-Za-z]{2}(_[A-Za-z]{2})?"))
		{
			if (!locale.toLowerCase().equals("default"))
				logger.log(Level.WARN, "Couldn't create a proper Locale out of \"" +
						locale + "\". Returning the default locale instead.");
			return Locale.getDefault();
		}
		else if (locale.contains("_"))
		{
			try {
				array 		= locale.split("_");
				language 	= array[0];
				country 	= array[1];
				
				return new Locale(language, country);
			}
			catch (Exception e)
			{
				logger.log(Level.WARN, "Couldn't create a proper Locale out of \"" +
						locale + "\". Returning the default locale instead.");
				return Locale.getDefault();
			}
		}
		else
		{
			return new Locale(locale);
		}
	}
	
	
	private int getStyle (String style)
	{

		int i;
		
		if (style == null)
			i = DateFormat.DEFAULT;
		else
		{
			style = style.toUpperCase();
			if (style.equals("SHORT"))
				i = DateFormat.SHORT;
			else if (style.equals("MEDIUM"))
				i = DateFormat.MEDIUM;
			else if (style.equals("LONG"))
				i = DateFormat.LONG;
			else if (style.equals("FULL"))
				i = DateFormat.FULL;
			else // if (style.equals("DEFAULT"))
				i = DateFormat.DEFAULT;
		}
		
		return i;
	}
	
	
	public void setForceOriginal (boolean value)
	{
		this.forceOriginal = value;
	}
	
	
	public boolean forceOriginal ()
	{
		return this.forceOriginal;
	}
	
	public String translateCountry (String p_Country) {
		return addressFormatter.translateCountry(p_Country);
	}
	
	
//	public String formatSSN (String ssn)
//	{
//		Pattern SSN_PATTERN = Pattern.compile("(\\d{4})\\s?(\\d{2})\\s?(\\d{2})\\s?(\\d{0,5})\\s*");
//		Matcher matcher = SSN_PATTERN.matcher(ssn);
//		
//		
//		if (matcher.matches())
//		{
//			ssn.replace(" ", "");
//			StringBuilder ssnBuilder = new StringBuilder()
//					.append(matcher.group(1))
//					.append(" ")
//					.append(matcher.group(2))
//					.append(" ")
//					.append(matcher.group(3))
//					.append(" ");
//			if (matcher.groupCount() >= 4)
//					ssnBuilder.append(matcher.group(4));
//			
//			return ssnBuilder.toString();
//		}
//		else
//		{
//			return ssn;
//		}
//	}
}