package lu.tudor.santec.gecamed.formeditor.gui.controller.converter;

import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Locale;

import lu.tudor.santec.gecamed.address.ejb.entity.beans.GECAMedAddressBean;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.XSLTemplate;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.controller.ControllerConfig;
import lu.tudor.santec.gecamed.core.gui.utils.xslfo.TransFOmer;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.Form;
import lu.tudor.santec.gecamed.formeditor.gui.component.EditableComponent;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormModel;
import lu.tudor.santec.gecamed.letter.gui.settings.LetterTemplateSettings;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Office;
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.patient.ejb.entity.beans.Incident;
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.entity.beans.PatientPhone;
import lu.tudor.santec.gecamed.patient.ejb.session.beans.IncidentManagerBean;
import lu.tudor.santec.gecamed.patient.ejb.session.beans.PatientAdminBean;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.IncidentManager;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public abstract class FormDataConverter
{
	/* ======================================== */
	// 		FINAL MEMBERS
	/* ======================================== */
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(FormDataConverter.class.getName());
	
	protected static final String XML_DEFINITION		= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	protected static final String XML_DEFINITION_REGEX 	= "^\\<\\?xml version=\"1\\.0\" encoding=\"UTF-8\"\\?\\>";
	protected static final String EMPTY_STRING			= "";

//	protected static final String FILE_EXTENTION_HTML 	= ".html";
	
//	protected static final XMLOutputter xmlFormater 	= new XMLOutputter(Format.getRawFormat());
	protected static final XMLOutputter xmlFormater 	= new XMLOutputter(Format.getPrettyFormat());
	
	public static final int NO_HANDLER_OPTION 			= -1;
	public static final int DESKTOP_HANDLER_OPTION 		= 0;
	public static final int MSWORD_HANDLER_OPTION 		= 1;
	public static final int OOWRITER_HANDLER_OPTION 	= 2;
	
	private static final String TYPE_ATTRIBUTE_NAME 	= "type";
	public static final String 	TYPE_HISTORY 			= "history";
	public static final String 	TYPE_PRINT 				= "print";
	public static final String 	TYPE_EDIT 				= "edit";
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	private static int formHandlerOption = -1;
	
	
	protected FormModel 	model;
	
	protected Element 		root;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTOR
	/* ======================================== */
	
	public static FormDataConverter createHandler (FormModel model)
	{
		return createHandler (model, formHandlerOption);
	}
	
	public static FormDataConverter createHandler (FormModel model, int handlerOption)
	{
		switch (handlerOption)
		{
		case MSWORD_HANDLER_OPTION:
			return new DocumentFormDataConverter(model, ControllerConfig.getController(LetterTemplateSettings.WORD, false));
			
		case OOWRITER_HANDLER_OPTION:
			return new DocumentFormDataConverter(model, ControllerConfig.getController(LetterTemplateSettings.WRITER, false));
			
		case DESKTOP_HANDLER_OPTION:
			return new DesktopFormDataConverter(model);
			
		default:
			throw new WrongOptionException(formHandlerOption);
		}
	}
	
	protected FormDataConverter (FormModel model)
	{
		this.model = model;
	}
	
	
	
	/* ======================================== */
	// 		GETTER & SETTER
	/* ======================================== */
	
	public FormModel getModel ()
	{
		return model;
	}
	
	public void setModel (FormModel model)
	{
		this.model = model;
	}
	
	public static int getHandlerOption ()
	{
		return formHandlerOption;
	}
	
	public static void setHandlerOption (int option)
	{
		formHandlerOption = option;
	}
	
	
	
	/* ======================================== */
	// 		TRANSFORMATION METHODS
	/* ======================================== */
	
	public abstract void open (XSLTemplate xslTemplate);
	
	public abstract void edit (XSLTemplate xslTemplate);
	
	public abstract void print (XSLTemplate xslTemplate);
	
	
	
	/* ======================================== */
	// 		HELPER METHODS
	/* ======================================== */
	
//	protected byte[] createFileData (String type) throws JDOMException
//	{
//		XSLTemplate xslTemplate = getXslTemplate(type);
//		
//		if (xslTemplate == null)
//			 return null;
//		else return createFileData(type, xslTemplate);
//	}
	
	
	protected byte[] createFileData (String type, XSLTemplate xslTemplate) throws JDOMException, UnsupportedEncodingException
	{
//		FormTemplate 		template 	= model.getTemplate();
//		List<XSLTemplate> 	templates;
//		XSLTemplate 		xslTemplate = getXslTemplate(type);
		XMLOutputter 		outputter;
//		String 				xsl;
		String 				dataXml;
		TransFOmer			transFOmer;
//		String				docString;
		String				extension;
		String				xsl;
		byte[]				data;
		
		createXmlOfModel(type);
		outputter 	= new XMLOutputter(Format.getPrettyFormat());
		dataXml 	= outputter.outputString(this.root);
		extension	= xslTemplate.getFileNameExtension();
		xsl			= xslTemplate.getData();
		
		transFOmer	= TransFOmer.getInstance();
//		docString	= transFOmer.transFO(dataXml, xsl, extension);
//		data		= docString.getBytes(TransFOmer.UTF8);
		data		= transFOmer.transFO(dataXml, xsl, extension);
		
		return data;
//		return createHtml(dataXml, type);
	}
	
	
	protected void createXmlOfModel (String type)
	{
		this.root = createXmlOfModel(model, type);
	}
	
	
	public static Element createXmlOfModel (FormModel model, String type)
	{
		Element root = new Element("form");
		Element rootElement;
		Element element;
		
		PatientAdminInterface 	patientManager;
		IncidentManager 		incidentManager;
		Incident 				incident;
		Form 					form;
		Physician 				physician 	= null;
		Patient 				patient 	= null;
		
		
		root.setAttribute(TYPE_ATTRIBUTE_NAME, type);
		Collection<EditableComponent> components = model.getEditableComponents().values();
		
		// add the components to the root
		rootElement = new Element("components");
		for (EditableComponent component : components)
		{
			rootElement.addContent(component.getDataAsXML());
		}
		root.addContent(rootElement);
		
		// add the form context to the root
		rootElement = new Element("formContext");
		HashMap<Object, Object> context = model.getFormContext();
		Object value;
		for (Object key : context.keySet())
		{
			value = context.get(key);
			if (value != null)
			{
				element = new Element(key.toString());
				element.addContent(value.toString());
				rootElement.addContent(element);
			}
		}
		root.addContent(rootElement);
		
		// add the global context to the root
		rootElement = new Element("globalContext");
		context = model.getGlobalContext();
		for (Object key : context.keySet())
		{
			value = context.get(key);
			if (value != null)
			{
				element = new Element(key.toString());
				element.addContent(value.toString());
				rootElement.addContent(element);
			}
		}
		root.addContent(rootElement);
		
		// add the other useful data to the root
		try
		{
			patientManager 	= (PatientAdminInterface) ManagerFactory.getRemote(PatientAdminBean.class);
			incidentManager = (IncidentManager) 	  ManagerFactory.getRemote(IncidentManagerBean.class);
			form 			= model.getForm();
			
			if (form != null)
			{
				patient 	= patientManager.getPatient(form.getPatientId());
				incident 	= incidentManager.getIncident(form.getIncidentEntry().getIncidentId());
				if (incident != null)
					physician 	= MainFrame.getPhysicianById(incident.getPhysicianId());
			}
			
			if (physician == null)
				physician 	= MainFrame.getCurrentPhysician();
			if (patient == null)
				patient 	= GECAMedModule.getCurrentPatient();
			
			root.addContent(getDateXMLData		("today", 				new Date()));
			root.addContent(getPatientXMLData	("patient", 			patient));
			root.addContent(getPhysicianXMLData	("treatingPhysician", 	physician));
			root.addContent(getPhysicianXMLData	("currentPhysician", 	MainFrame.getCurrentPhysician()));
			root.addContent(getOfficeXmlData 	("office", 				MainFrame.getCurrentOffice()));
		} 
		catch (Exception e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		return root;
	}
	

//	protected String createHtml (String xsl, String type) throws JDOMException
//	{
//		StringReader 	reader;
//		SAXBuilder 		builder;
//		Document 		stylesheet;
//		XSLTransformer 	transformer;
//		Document		inputXml;
//		Document 		outputXml;
//		String 			output;
//		
//		try
//		{
//			createXmlOfModel(type);
//			reader 		= new StringReader(xsl);
//			builder 	= new SAXBuilder();
//			stylesheet 	= builder.build(reader);
//			transformer = new XSLTransformer(stylesheet);
//			inputXml	= new Document(root);
//			outputXml	= transformer.transform(inputXml);
//			output 		= writeDocument(outputXml);
//			
//			if (output.startsWith(XML_DEFINITION))
//				output = output.replaceFirst(XML_DEFINITION_REGEX, EMPTY_STRING);
//			
//			return output;
//		} 
//		catch (JDOMException e)
//		{
//			logger.log(Level.WARN, e.getMessage());
//			GECAMedBaseDialogImpl.showMessageDialog(MainFrame.getInstance(), 
//					Translatrix.getTranslationString("formeditor.errorWhileCreatingXSL_title"), 
//					Translatrix.getTranslationString("formeditor.errorWhileCreatingXSL_msg") + "<br><br>" + 
//							e.getMessage(), 
//					GECAMedBaseDialog.OK_BUTTON_MODE, 
//					GECAMedModule.getIcon(GECAMedIconNames.ERROR));
////			throw e;
//		}
//		catch (Exception e)
//		{
//			logger.log(Level.ERROR, e.getMessage(), (Throwable)e);
//		}
//		return null;
//	}
	
	
	public static synchronized String writeDocument (Document xmlDoc)
	{
		StringWriter writer = new StringWriter();
		try
		{
			xmlFormater.output(xmlDoc, writer);
		} 
		catch (IOException e)
		{
			logger.log(Level.ERROR, e.getMessage(), (Throwable)e);
		}
		
		return writer.toString();
	}
	
	
	
	/* ======================================== */
	// 		POJO TO XML-DATA METHODS
	/* ======================================== */
	
	
	/**
	 * Creates a new Element with the given name containing 
	 * information of the given date.
	 * 
	 * @param name The name of the element to create
	 * @param date The date
	 */
	public static Element getDateXMLData (String name, Date date)
	{
		Calendar 	cal 		= new GregorianCalendar();
		Element 	dateElement = new Element(name);
		Element 	predefinedDate;
		DateFormat 	formatter;
		
		if (date == null)
			return dateElement;
		
		cal.setTime(date);
		int year 	= cal.get(Calendar.YEAR);
		int month 	= cal.get(Calendar.MONTH);
		int day 	= cal.get(Calendar.DAY_OF_MONTH);
		int hour24 	= cal.get(Calendar.HOUR_OF_DAY);
		int hour12 	= cal.get(Calendar.HOUR);
		int ampm 	= cal.get(Calendar.AM_PM);
		int minute 	= cal.get(Calendar.MINUTE);
		int second 	= cal.get(Calendar.SECOND);
		
		dateElement.setAttribute("year", 		String.valueOf(year));
		dateElement.setAttribute("month", 		(month < 10 ? "0" : "") + month);
		dateElement.setAttribute("monthShort", 	String.valueOf(month));
		dateElement.setAttribute("day", 		(day < 10 ? "0" : "") + day);
		dateElement.setAttribute("dayShort", 	String.valueOf(day));
		dateElement.setAttribute("hour12", 		(hour12 < 10 ? "0" : "") + hour12 
										  + (ampm == Calendar.AM ? " AM" : " PM"));
		dateElement.setAttribute("hour24", 		(hour24 < 10 ? "0" : "") + hour12);
		dateElement.setAttribute("minute", 		(minute < 10 ? "0" : "") + minute);
		dateElement.setAttribute("second", 		(second < 10 ? "0" : "") + second);
		
		// predefine date and time formats (DE, US, FR)
		formatter 		= DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.GERMANY);
		predefinedDate 	= new Element("de");
		predefinedDate.setText(formatter.format(date));
		dateElement.addContent(predefinedDate);
		
		formatter 		= DateFormat.getDateInstance(DateFormat.LONG, Locale.US);
		predefinedDate 	= new Element("us");
		predefinedDate.setText(formatter.format(date));
		dateElement.addContent(predefinedDate);
		
		formatter 		= DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
		predefinedDate 	= new Element("fr");
		predefinedDate.setText(formatter.format(date));
		dateElement.addContent(predefinedDate);
		
		formatter 		= DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.US);
		predefinedDate 	= new Element("time12");
		predefinedDate.setText(formatter.format(date));
		dateElement.addContent(predefinedDate);
		
		formatter 		= DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.GERMANY);
		predefinedDate 	= new Element("time24");
		predefinedDate.setText(formatter.format(date));
		dateElement.addContent(predefinedDate);
		
		return dateElement;
	}
	
	
	public static Element getPhysicianXMLData (String name, Physician physician)
	{
		Element physicianElement 	= new Element(name);
		Element element;
		
		if (physician == null)
			return physicianElement;
		
		element = new Element("title");
		element.setText(physician.getTitle());
		physicianElement.addContent(element);
		
		element = new Element("firstName");
		element.setText(physician.getFirstName());
		physicianElement.addContent(element);
		
		element = new Element("lastName");
		element.setText(physician.getName());
		physicianElement.addContent(element);
		
		element = new Element("initials");
		element.setText(physician.getMnemonic());
		physicianElement.addContent(element);
		
		element = new Element("speciality");
		element.setText(physician.getSpeciality());
		physicianElement.addContent(element);
		
		element = new Element("ucm");
		element.setText(physician.getUcmCode());
		physicianElement.addContent(element);
		
		element = new Element("ucmBilling");
		element.setText(physician.getUcmFacturation());
		physicianElement.addContent(element);
		
		element = new Element("gsm");
		element.setText(physician.getGsm());
		physicianElement.addContent(element);
		
		element = new Element("fax");
		element.setText(physician.getFax());
		physicianElement.addContent(element);
		
		element = new Element("phoneExtension");
		element.setText(physician.getPhoneExtension());
		physicianElement.addContent(element);
		
		element = new Element("email");
		element.setText(physician.getEmail());
		physicianElement.addContent(element);
		
		element = new Element("color");
		element.setText("#" + Integer.toHexString(physician.getColor().intValue()));
		physicianElement.addContent(element);
		
		GECAMedAddressBean address = physician.getPhysicianAddress();
		physicianElement.addContent(getAddressXmlData("address", address));
		
		return physicianElement;
	}
	
	
	public static Element getPatientXMLData (String name, Patient patient)
	{
		Element 		patientElement = new Element(name);
		Element 		element;
		Collection<?> 	elements;
		
		if (patient == null)
			return patientElement;
		
		element = new Element("ssn");
		element.setText(patient.getSocialSecurityNumber());
		patientElement.addContent(element);
		
		element = new Element("title");
		element.setText(patient.getTitle());
		patientElement.addContent(element);
		
		element = new Element("firstName");
		element.setText(patient.getFirstName());
		patientElement.addContent(element);
		
		element = new Element("surName");
		element.setText(patient.getSurName());
		patientElement.addContent(element);
		
		element = new Element("maidenName");
		element.setText(patient.getMaidenName());
		patientElement.addContent(element);
		
		element = new Element("gender");
		element.setText(patient.getGender());
		patientElement.addContent(element);
		
		element = new Element("nationality");
		element.setText(patient.getNationality());
		patientElement.addContent(element);
		
		element = new Element("language");
		element.setText(patient.getLanguage());
		patientElement.addContent(element);
		
		element = new Element("birthLocality");
		element.setText(patient.getBirthLocality());
		patientElement.addContent(element);
		
		element = new Element("email");
		element.setText(patient.getEmail());
		patientElement.addContent(element);
		
		element = new Element("parentName");
		element.setText(patient.getParentName());
		patientElement.addContent(element);
		
		element = new Element("maritalStatus");
		element.setText(patient.getMaritalStatus());
		patientElement.addContent(element);
		
		element = new Element("policyNumber");
		element.setText(patient.getPolicyNumber());
		patientElement.addContent(element);
		
		element = new Element("status");
		element.setText(patient.getStatusName());
		patientElement.addContent(element);
		
		element = new Element("job");
		element.setText(patient.getJob());
		patientElement.addContent(element);
		
		element = new Element("insuranceId");
		element.setText(String.valueOf(patient.getInsuranceID()));
		patientElement.addContent(element);
		
		elements = patient.getAddress();
		if (elements != null)
			for (Object address : elements)
			{
				element = getAddressXmlData("address", (GECAMedAddressBean) address);
				patientElement.addContent(element);
			}
		
		element = getAddressXmlData("homeAddress", patient.getHomeAddress());
		patientElement.addContent(element);
		
		element = getAddressXmlData("billingAddress", patient.getBillingAddress());
		patientElement.addContent(element);
		
		elements = patient.getPhones();
		if (elements != null)
			for (Object phone : elements)
			{
				element = getPhoneXmlData("phoneno", (PatientPhone)phone);
				patientElement.addContent(element);
			}
		
		element = getDateXMLData("birthday", 		patient.getBirthDate());
		patientElement.addContent(element);
		
		element = getInsuranceXmlData("insurance", 	patient.getInsurance());
		patientElement.addContent(element);
		
		return patientElement;
	}
	
	
	public static Element getOfficeXmlData (String name, Office office)
	{
		Element 		officeElement = new Element(name);
		Element 		element;
		Collection<?> 	elements;
		
		if (office == null)
			return officeElement;

		element = new Element("email");
		element.setText(office.getEmail());
		officeElement.addContent(element);
		
		element = new Element("info");
		element.setText(office.getInformation());
		officeElement.addContent(element);
		
		element = new Element("name");
		element.setText(office.getName());
		officeElement.addContent(element);
		
		element = new Element("ucmCode");
		element.setText(office.getUcmCode());
		officeElement.addContent(element);
		
		element = new Element("website");
		element.setText(office.getWebsite());
		officeElement.addContent(element);
		
		elements = office.getOfficeAddress();
		if (elements != null)
			for (Object address : elements)
			{
				element = getAddressXmlData("address", (GECAMedAddressBean) address);
				officeElement.addContent(element);
			}
		
		element = getAddressXmlData("latestAddress", office.getLatestOfficeAddress());
		officeElement.addContent(element);


		elements = office.getOfficePhones();
		if (elements != null)
			for (Object phone : elements)
			{
				element = getPhoneXmlData("phoneno", (OfficePhone) phone);
				officeElement.addContent(element);
			}
		
		return officeElement;
	}
	
	
	private static Element getAddressXmlData (String name, GECAMedAddressBean address)
	{
		Element addressElement = new Element(name);
		Element element;
		
		if (address == null)
			return addressElement;
		
		addressElement.setAttribute ("type", 	address.getType());
		
		element = new Element("streetName");
		element.setText(address.getStreetName());
		addressElement.addContent(element);
		
		element = new Element("streetNumber");
		element.setText(address.getStreetNumber());
		addressElement.addContent(element);
		
		element = new Element("zip");
		element.setText(address.getZip());
		addressElement.addContent(element);
		
		element = new Element("city");
		element.setText(address.getLocality());
		addressElement.addContent(element);
		
		element = new Element("country");
		element.setText(address.getCountry());
		addressElement.addContent(element);
		
		return addressElement;
	}
	
	
	private static Element getPhoneXmlData (String name, OfficePhone phone)
	{
		Element phoneElement = new Element(name);
		
		if (phone == null)
			return phoneElement;
		
		phoneElement.setText(phone.getNumber());
		phoneElement.setAttribute("type", phone.getType());
		
		return phoneElement;
	}
	
	
	private static Element getPhoneXmlData (String name, PatientPhone phone)
	{
		Element phoneElement = new Element(name);
		
		if (phone == null)
			return phoneElement;
		
		phoneElement.setText(phone.getNumber());
		phoneElement.setAttribute("type", phone.getType());
		
		return phoneElement;
	}
	
	
	private static Element getInsuranceXmlData (String name, Insurance insurance)
	{
		Element insuranceElement = new Element(name);
		
		if (insurance == null)
			return insuranceElement;
		
		insuranceElement.setText(insurance.getName());
		insuranceElement.setAttribute("acronym", 		insurance.getAcronym());
		insuranceElement.setAttribute("primary", 		String.valueOf(insurance.getPrimary()));
		insuranceElement.setAttribute("private", 		String.valueOf(insurance.getPrivate()));
		insuranceElement.setAttribute("thirdParty", 	String.valueOf(insurance.getThirdParty()));
		insuranceElement.setAttribute("ucmAffiliated", 	String.valueOf(insurance.getUcmAffiliated()));
		
		return insuranceElement;
	}
}


class WrongOptionException extends RuntimeException
{
	private static final long serialVersionUID = 1L;
	
	public WrongOptionException(int choosenOption)
	{
		super("Option not supported! Must be one of " +
				FormDataConverter.DESKTOP_HANDLER_OPTION + " (to use the desktop API), " +
				FormDataConverter.MSWORD_HANDLER_OPTION + " (to use MSWord) or " +
				FormDataConverter.OOWRITER_HANDLER_OPTION +
				" (to use OO), but is " + choosenOption + ".");
	}
}
