package lu.tudor.santec.gecamed.esante.gui.utils;

import java.awt.Image;
import java.net.ConnectException;
import java.net.SocketException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.persistence.Transient;
import javax.swing.ImageIcon;
import javax.xml.xpath.XPathExpressionException;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedLocale;
import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedLog;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.IconFetcher;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.plugin.filehandler.FileOpener;
import lu.tudor.santec.gecamed.core.gui.utils.UrlOpener;
import lu.tudor.santec.gecamed.core.gui.widgets.ErrorDialog;
import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.CdaCode;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.CdaCodeCategory;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.CdaCodeDescription;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.CdaDocument;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.Dsp;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.ESanteProperty;
import lu.tudor.santec.gecamed.esante.ejb.session.beans.ESanteConfigManagerBean;
import lu.tudor.santec.gecamed.esante.gui.IconNames;
import lu.tudor.santec.gecamed.esante.gui.InfoString;
import lu.tudor.santec.gecamed.esante.gui.dialogs.EHealthIDRequestDialog;
import lu.tudor.santec.gecamed.esante.gui.dialogs.ESanteDialog;
import lu.tudor.santec.gecamed.esante.gui.dialogs.LoginDialog;
import lu.tudor.santec.gecamed.esante.gui.tab.ESanteTab;
import lu.tudor.santec.gecamed.esante.gui.webservice.Security;
import lu.tudor.santec.gecamed.esante.gui.webservice.SoapSender;
import lu.tudor.santec.gecamed.esante.gui.webservice.WebserviceConstants;
import lu.tudor.santec.gecamed.esante.gui.webservice.WebserviceException;
import lu.tudor.santec.gecamed.esante.utils.ESanteUtils;
import lu.tudor.santec.gecamed.esante.utils.exceptions.SendingStoppedException;
import lu.tudor.santec.gecamed.importexport.utils.XPathAPI;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.IncidentEntry;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.gui.PatientManagerModule;
import lu.tudor.santec.gecamed.usermanagement.ejb.entity.beans.GecamedUser;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version <br>
 *          $Log: ESanteUtils.java,v $ <br>
 *          Revision 1.34 2014-03-05 13:08:11 ferring <br>
 *          eSante bug fixed, where user was asked several times, to link with DSP when compare dialog was shown automatically, because there was only one DSP
 *          for choice <br>
 * <br>
 *          Revision 1.33 2014-02-20 16:38:34 ferring <br>
 *          PatientManagerModule taken directly <br>
 * <br>
 *          Revision 1.32 2014-02-12 12:17:02 ferring <br>
 *          short and long eHealth IDs introduced <br>
 * <br>
 *          Revision 1.31 2014-02-06 14:31:12 ferring <br>
 *          SendingStoppedException handling and logging changed <br>
 * <br>
 *          Revision 1.30 2014-02-04 10:08:42 ferring <br>
 *          eSante ID management completed <br>
 *          Only those documents will be shown, that are retrieved by the RSQ <br>
 * <br>
 *          Revision 1.29 2014-01-29 12:48:22 ferring <br>
 *          made readable (also without Java doc) <br>
 * <br>
 *          Revision 1.28 2014-01-28 17:53:12 donak <br>
 *          Practice eHealth id is now set automatically <br>
 *          Document format is now determined automatically <br>
 *          Text of eHealth id input dialog has been changed DSP id --> eHealth ID <br>
 *          eHealth id support link is now obtained from db <br>
 * <br>
 *          Revision 1.27 2014-01-28 14:06:51 ferring <br>
 *          Exception added <br>
 * <br>
 *          Revision 1.26 2014-01-27 13:13:47 donak <br>
 *          * Properties are now saved in db in respect to the following contexts: <br>
 *          - specific to a GECAMed user <br>
 *          - specific to a GECAMed physician <br>
 *          - specific to an eSanté plattform user <br>
 *          - general (independend of GECAMed user, physician, eSanté platform user) <br>
 *          * Improved authentication handling. A signed authentication request is now only done for user authentication. for dsp authentication requests the
 *          provided saml assertion is used. (authentication speed up). ATTENTION: This fix is currently disabled till a bug in the eSanté platform has been
 *          fixed. <br>
 *          * Naming of message loggings had been adapted to the naming in the connection kit (e.g. DSP-10, DSP-22) <br>
 *          * Changed behavior for handling of dsps for which physician has insufficient access permissions. If physician does not want to provide presence
 *          password, a DSP-11 is sent instead of a DSP-12 to allow the physician to at least access the documents he is author of. <br>
 * <br>
 *          Revision 1.25 2014-01-07 13:19:44 donak <br>
 *          Adding initial version of pdf preview dialog <br>
 *          Several bug fixes <br>
 * <br>
 *          Revision 1.24 2014-01-06 12:34:28 ferring <br>
 *          download site constants moved <br>
 * <br>
 *          Revision 1.23 2013-12-27 18:09:26 donak <br>
 *          Cleanup of imports <br>
 * <br>
 *          Revision 1.22 2013-12-23 18:07:36 donak <br>
 *          Stabilized smartcard login - using now the LuxTrust API and should thus be easily extendible to signing server usage. <br>
 * <br>
 *          Revision 1.21 2013-12-16 12:54:00 ferring <br>
 *          Connection timeout caught and handled <br>
 * <br>
 *          Revision 1.20 2013-12-16 12:05:12 ferring <br>
 *          logging eSanté actions <br>
 * <br>
 *          Revision 1.19 2013-12-13 15:02:24 ferring <br>
 *          Exception handling changed <br>
 * <br>
 *          Revision 1.18 2013-12-13 13:46:26 ferring <br>
 *          check for download site corrected <br>
 * <br>
 *          Revision 1.17 2013-12-13 12:31:32 ferring <br>
 *          Exception handling changed <br>
 * <br>
 *          Revision 1.16 2013-12-10 11:58:18 ferring <br>
 *          webservice error handling improved <br>
 * <br>
 *          Revision 1.15 2013-12-04 13:12:08 ferring <br>
 *          Checking eSante server status <br>
 * <br>
 *          Revision 1.14 2013-11-22 14:49:11 ferring <br>
 *          Help methods enlarged to figure out the state of a CDA document <br>
 * <br>
 *          Revision 1.13 2013-11-14 17:56:33 ferring <br>
 *          check if document needs to be downloaded and better check if documents can be integrated <br>
 * <br>
 *          Revision 1.12 2013-10-23 07:32:32 ferring <br>
 *          icon names changed to constants <br>
 * <br>
 *          Revision 1.11 2013-10-09 13:36:35 ferring <br>
 *          eSanté icon changed and tree view initialised <br>
 * <br>
 *          Revision 1.10 2013-09-30 10:56:40 ferring <br>
 *          eSante integration in GECAMed and synchronise funktion added <br>
 * <br>
 *          Revision 1.9 2013-09-13 14:18:56 ferring <br>
 *          eSante bugs fixed and database implementation started <br>
 * <br>
 *          Revision 1.8 2013-07-15 06:18:37 ferring <br>
 *          logging changed <br>
 * <br>
 *          Revision 1.6 2013-07-02 14:04:46 ferring <br>
 *          *** empty log message *** <br>
 * <br>
 *          Revision 1.5 2013-06-27 14:13:07 ferring <br>
 *          *** empty log message *** <br>
 * <br>
 *          Revision 1.4 2013-06-27 14:12:39 ferring <br>
 *          *** empty log message *** <br>
 * <br>
 *          Revision 1.3 2013-06-26 11:19:37 ferring <br>
 *          migrating eSante view from dialogs to eSante patient tab <br>
 * <br>
 *          Revision 1.2 2013-06-24 12:59:26 ferring <br>
 *          eSante deactivated for release (until its ready) <br>
 * <br>
 *          Revision 1.1 2013-06-10 08:22:14 ferring <br>
 *          eSante POC <br>
 */

public class ESanteGuiUtils implements GECAMedIconNames {
	/* ======================================== */
	// CONSTANTS
	/* ======================================== */

	// private static final String ICON_PATH = "../resources/icons/";

	private static final char SEPARATOR = '#';

	private static final String LUXTRUST_MIDDLEWARE_DOWNLOAD_LINK_EN = "https://www.luxtrust.lu/en/simple/225";
	private static final String LUXTRUST_MIDDLEWARE_DOWNLOAD_LINK_DE = "https://www.luxtrust.lu/de/simple/393";
	private static final String LUXTRUST_MIDDLEWARE_DOWNLOAD_LINK_FR = "https://www.luxtrust.lu/fr/simple/189";

	/* ======================================== */
	// MEMBERS
	/* ======================================== */

	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(ESanteGuiUtils.class.getName());

	private static Map<Integer, ImageIcon> eSanteIcons = new HashMap<Integer, ImageIcon>();

	private static Map<String, ImageIcon> sqaureIcons = new HashMap<String, ImageIcon>();

	private static Boolean eSanteMobuleActive = null;

	private static ImageIcon loadingImage;

	private static ESanteInitializer initializer;

	/* ======================================== */
	// CLASS BODY
	/* ======================================== */

	public static ImageIcon getESanteIcon(int width) {
		if (!isESanteModuleActivated())
			return null;

		ImageIcon icon = eSanteIcons.get(Integer.valueOf(width));

		if (icon != null)
			return icon;

		icon = getImage(IconNames.ESANTE_LOGO);

		if (icon == null)
			return null;

		int orgWidth = icon.getIconWidth();
		int orgHeight = icon.getIconHeight();
		int height = (int) (Double.valueOf(orgHeight).doubleValue() * (Double.valueOf(width).doubleValue() / Double.valueOf(orgWidth).doubleValue()));

		icon = new ImageIcon(icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH));
		eSanteIcons.put(Integer.valueOf(width), icon);

		return icon;
	}

	public static ImageIcon getIcon(String iconName, int scale) {
		if (scale <= 0)
			scale = -1;

		ImageIcon icon;
		String iconIdentifier = new StringBuilder(iconName.length() + 4).append(iconName).append(SEPARATOR).append(scale).toString();

		icon = sqaureIcons.get(iconIdentifier);

		if (icon != null)
			return icon;

		try {
			icon = getImage(iconName);

			if (icon == null)
				return null;

			if (scale < 0) {
				icon = new ImageIcon(icon.getImage());
			} else {
				icon = new ImageIcon(icon.getImage().getScaledInstance(scale, scale, Image.SCALE_SMOOTH));
			}
			sqaureIcons.put(iconIdentifier, icon);
		} catch (Exception e) {
			logger.log(Level.WARN, "Error while trying to load eSanté icon \"" + iconName + "\"", e);
			icon = null;
		}

		return icon;
	}

	/**
	 * Obtain the eHealth Id of the currently logged in eSanté user.<br/>
	 * <br/>
	 * <i>The current eSanté user is identified either by the serial number of the LuxTrust smartcard or the entered id depending of the chosen login method
	 * (via smartcard or username/password)</i><br/>
	 * <br>
	 * <b>Approach:</b><br/>
	 * <i> 1. Try to obtain the required id from the local cache. If none is found:<br/>
	 * 2. Try to obtain the required id from database. If none is found:<br/>
	 * 3. Open a dialog and ask the user to enter his eHealth id. If it is entered, it is also automatically stored in the db</i>
	 * 
	 * @return The eHealth ID of the user or null if none could be provided<br/>
	 *         <i>(e.g. the user pressed cancel in the input dialog)</i>
	 */
	public static String getUserEHealthId() {
		try {
			// obtain the eHealth id support link from database
			return EHealthIDRequestDialog.getInstance().requestESanteID(LoginDialog.getConfiguration().getAuthorId());
			
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * Obtain the eHealth Id of the practice. If no such id is found in the db, the eHealth id of the current physician who is connected to the eHealth platform
	 * will be used as the practice eHealth id and also stored to the database as such.
	 * 
	 * @param userEHealthId
	 *            The eHealth id of the current physician who is connected to the eHealth platform.
	 * @return The eHealth id of the practice
	 */
	public static String getPracticeEHealthId(String userEHealthId) {

		String value = null;
		
		Integer siteID = MainFrame.getCurrentSiteId();
		String site = "";
		if (siteID != null) {
			site = "_SITE-" + siteID;
		}

		// check if the practice eHealth id is available in the DB
		value = ESanteConfigManagerBean.getInstance().getESantePropertyValue(ESanteProperty.PROP_PRACTICE_EHEALTH_ID + site);
		
		// if no Practice ID for the current Site is set, return the Practice ID
		if (value == null && siteID != null) {
			value = ESanteConfigManagerBean.getInstance().getESantePropertyValue(ESanteProperty.PROP_PRACTICE_EHEALTH_ID);
		}
		
		if (value == null) {
			try {
				// No, it isn't. Thus use the eHealth id of the current physician (because there is actually no real eHealth id for specific practices)
				// Plus the GENERAL_PRACTICE_IDENTIFIER as PREFIX
				value = WebserviceConstants.GENERAL_PRACTICE_IDENTIFIER + userEHealthId;
				// and store it in the database for subsequent usage
				ESanteConfigManagerBean.getInstance().setESanteProperty(ESanteProperty.PROP_PRACTICE_EHEALTH_ID, value);
			} catch (Exception e) {
			}
		}
		
		return value;
	}

	public static boolean isESanteModuleActivated() {
		if (eSanteMobuleActive == null) {
			eSanteMobuleActive = PatientManagerModule.getInstance().subModulesPlugin.getEnabledElements().contains(ESanteTab.TAB_NAME) ? Boolean.TRUE
					: Boolean.FALSE;
		}

		return eSanteMobuleActive.booleanValue();
	}

	public static ImageIcon getLoadingImage() {
		if (loadingImage == null) {
			URL imageUrl = InfoString.class.getResource("resources/icons/loading.gif");
			loadingImage = new ImageIcon(imageUrl);
		}

		return loadingImage;
	}

	private static ImageIcon getImage(String iconName) {
		return IconFetcher.getIcon(InfoString.class, iconName);
	}

	public static void showConnectionError(Exception e) {
		if (e instanceof SocketException)
			handleSocketException((SocketException) e);
		else if (e instanceof WebserviceException)
			handleWebserviceException((WebserviceException) e);
		else {
			logger.error(e);
			throw new RuntimeException("NOT YET IMPLEMENTED!");
		}
	}

	private static void handleSocketException(SocketException e) {
		String eSanteServerName = "www-integration.esante.lu";
		boolean iNetPinged = GECAMedUtils.ping("www.google.com");
		boolean esantePinged = iNetPinged && GECAMedUtils.ping(eSanteServerName);
		String message;

		if (!iNetPinged) {
			// couldn't ping www.google.com, no Internet connection
			message = Translatrix.getTranslationString("esante.exceptions.connectionError.noInetConnection");
			logger.error("Not connected to the internet", e);
		} else if (!esantePinged) {
			message = Translatrix.getTranslationString((e instanceof ConnectException ? "esante.exceptions.connectionError.connectionTimeout"
					: "esante.exceptions.connectionError.esanteServerNotReachable"), new String[] { eSanteServerName });
			// couldn't ping the eSanté server
			logger.error("eSanté server \"" + eSanteServerName + "\" is not reachable", e);
		} else {
			// eSanté server was pinged successfully, but the
			message = Translatrix.getTranslationString("Socket Exception although the eSanté server is reachable.", new String[] { e.getMessage() });
			logger.error("Connection problem, although the eSanté server (\"" + eSanteServerName + "\") seems to be reachable.", e);
		}

		ESanteDialog.showMessageDialog(MainFrame.getInstance(), Translatrix.getTranslationString("esante.exceptions.connectionError"), message,
				ESanteDialog.OK_BUTTON_MODE, GECAMedModule.getBigIcon(GECAMedIconNames.WARNING));
	}

	private static void handleWebserviceException(WebserviceException e) {
		showAndLogWebserviceException(e);
	}

	public static void showHtmlErrorCodeException(String htmlMessage) {
		FileOpener.open(htmlMessage.getBytes(), ".html", null);
	}

	public static void showAndLogWebserviceException(WebserviceException e) {
		logger.error(
				"Unspecified WebserviceException: " + e.getCode() + (GECAMedUtils.isEmpty(e.getMessage()) ? "" : " (" + e.getMessage())
						+ (GECAMedUtils.isEmpty(e.getDetails()) ? "" : ")\nDETAIL: " + e.getDetails()), e);
		StringBuilder message = new StringBuilder("<html><font size=5><b>").append(e.getCode());
		if (!GECAMedUtils.isEmpty(e.getMessage()))
			message.append(":</font><font size=3><br> ").append(e.getMessage());
		message.append("</font></b>");
		if (!GECAMedUtils.isEmpty(e.getDetails()))
			message.append("<br><br><i>").append(e.getDetails()).append("</i>");
		message.append("</html>");

		GecamedUser user = GECAMedModule.getCurrentUser();
		SoapSender sender = e.getSender();

		StringBuilder details = new StringBuilder();
		try {
			details.append("GECAMed User: ").append(user.getName()).append("\nGECAMed Patient: ").append(sender.getDsp().getPatientId())
					.append("\nREQUEST SENT:\n").append(sender.getRequest()).append("\n\nRESPONSE RECEIVED:\n").append(sender.getResponseMessage());
		} catch (SendingStoppedException e1) {
			// should be possible to happen ...
			logger.error(e1.getMessage());
		}

		ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(), message.toString(), details.toString(), sender.getDsp().getPatientId() != null ? sender
				.getDsp().getPatientId() : -1, GECAMedUtils.getDateFormatter(GECAMedUtils.DATE_FORMAT_FILE_TIME).format(new Date()) + "_user-" + user.getName()
				+ "_" + sender.getLogSuffix() + ".log");
	}

	/**
	 * Logs this operation in the GECAMed logs.
	 * 
	 * @param operationName
	 *            The name of the executed operation.
	 * @param message
	 *            The message to log
	 */
	public static void trace(String operationName, String message) {
		String userAndCardTrace = "";
		try {
			userAndCardTrace = " [eHealthID: "+ LoginDialog.getConfiguration().getAuthorId() + "/eSanteUserId: " + getUserEHealthId() + "]";
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		GECAMedLog.user(ESanteTab.NAME, operationName, message + userAndCardTrace);
		logger.info(message);
	}

	public static Patient getPatientForDsp(Dsp dsp) {
		if (dsp.getPatient() == null || dsp.getPatient().getId() != dsp.getPatientId()) {
			try {
				dsp.setPatient(ManagerFactory.find(Patient.class, dsp.getPatientId()));
			} catch (Exception e) {
				logger.error("Error while trying to load the patient for dsp " + dsp.getId(), e);
				return null;
			}
		}

		return dsp.getPatient();
	}

	public static Dsp getDspOfDocument(CdaDocument cda) {
		if ((cda.getDsp() == null && cda.getDspId() != null) || (cda.getDsp().getId() != null && !cda.getDsp().getId().equals(cda.getDspId()))) {
			try {
				cda.setDsp(ManagerFactory.find(Dsp.class, cda.getDspId()));
			} catch (Exception e) {
				logger.error("Error while trying to load the patient for dsp " + cda.getDspId(), e);
				return null;
			}
		}

		return cda.getDsp();
	}

	/**
	 * Creates a new CDA document.
	 * 
	 * @param node
	 *            The xml snippet containing the metadata of a cda document (ExtrinsicObject-tag). Relevant information is extracted and added to the new
	 *            cda-document object.
	 * @param dsp
	 *            The health record to which the cda document should belong
	 * @return The CDA document
	 * @throws Exception
	 *             Please check exception message for further details
	 */
	public static CdaDocument createCdaDocument(Node node, Dsp dsp) throws Exception {
		CdaDocument cda = new CdaDocument();
		cda.setDsp(dsp);
		// cda.setDspId(dsp.getId());

		// document unique ID
		cda.setDocumentUniqueId(XPathAPI.selectNodeValue(node, CdaDocument.XPATH_UID, false));

		// document name
		try {
			cda.setTitle(XPathAPI.selectNodeValue(node, "*[local-name()='Name']/*[local-name()='LocalizedString']/@value", false));
		} catch (XPathExpressionException e) {
			logger.warn("Couldn't find document title.", e);
		}

		// set the unique id of this document
		cda.setDocumentUniqueId(XPathAPI.selectNodeValue(node, CdaDocument.XPATH_UID, false));
		
		// set the object id of this document
		cda.setDocumentOid(XPathAPI.selectNodeValue(node, CdaDocument.XPATH_OID, false));

		// creation time
		String creationTimeValue = XPathAPI.selectNodeValue(node,
				"*[local-name()='Slot' and @*[local-name()='name']='creationTime']/*[local-name()='ValueList']/*[local-name()='Value']/text()", false);

		if (creationTimeValue == null)
			throw new Exception("Creation time is not set!");

		// read codes
		cda.setLanguageCode(XPathAPI.selectNodeValue(node, "*[local-name()='Slot' and @*[local-name()='name']='languageCode']/*[local-name()='ValueList']/*[local-name()='Value']/text()", false));
		cda.setClassCode(getCode(cda, node, CodeFetcher.CLASS_CODE));
		cda.setTypeCode(getCode(cda, node, CodeFetcher.TYPE_CODE));
		cda.setFormatCode(getCode(cda, node, CodeFetcher.FORMAT_CODE));
		cda.setPracticeSettingCode(getCode(cda, node, CodeFetcher.PRACTICE_SETTING_CODE));
		cda.setHcfTypeCode(getCode(cda, node, CodeFetcher.HEALTH_CARE_FACILITY_TYPE_CODE));
		cda.setAuthorRole( XPathAPI.selectNodeValue(node, "*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d']/Slot[@name='authorRole']/ValueList/Value/text()", false));
		cda.setAuthorSpecialty(XPathAPI.selectNodeValue(node, "*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d']/Slot[@name='authorSpecialty']/ValueList/Value/text()", false));

		
		// read the confidentiality codes
		String[] codeIds = getCodes(cda, node, CodeFetcher.CONFIDENTIALITY_CODE);;
		boolean blocked = false;
		String confidentiality = CdaDocument.CONFIDENTIALITY_NORMAL;

		for (int index = 0; index < codeIds.length; index++) {
			if (CdaDocument.CONFIDENTIALITY_BLOCKED.equals(codeIds[index]))
				blocked = true;
			else
				confidentiality = codeIds[index];
		}
		cda.setConfidentialityStatus(confidentiality, blocked);

		try {
			// creationTime = timeFormatter.parse(creationTimeValue);
			cda.setCreationTime(ESanteUtils.parseDate(creationTimeValue));
		} catch (Exception e) {
			logger.log(Level.WARN, "Couldn't parse \"" + creationTimeValue + "\" to a date. The format should be \"" + CdaDocument.TIME_FORMAT + "\"");
		}

		// content mime type
		cda.setMimeType(XPathAPI.selectNodeValue(node, "@mimeType", false));

		// repository unique ID
		try {
			cda.setRepositoryUniqueId(XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Slot' and @*[local-name()='name']='repositoryUniqueId']/*[local-name()='ValueList']/*[local-name()='Value']/text()",
							false));
		} catch (XPathExpressionException e) {
			logger.warn("Couldn't find \"repositoryUniqueId\".", e);
		}

		// description
		try {
			cda.setDescription(XPathAPI.selectNodeValue(node, "*[local-name()='Description']/*[local-name()='LocalizedString']/@value", false));
			// TODO get the language
			// String descriptionLang = XPathAPI.selectNodeValue(node,
			// "*[local-name()='Description']/*[local-name()='LocalizedString']/@*[local-name()='lang']",
			// false);
		} catch (XPathExpressionException e) {
			logger.warn("Couldn't find document description.", e);
		}

		// author
		try {
			cda.setAuthor(XPathAPI.selectNodeValue(node,
					"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d']"
							+ "/*[local-name()='Slot' and @*[local-name()='name']='authorPerson']/*[local-name()='ValueList']/*[local-name()='Value']/text()",
					false));
		} catch (Exception e) {
			logger.warn("Couldn't find the author of the document.");
		}

		// content size
		String contentSizeValue;
		try {
			contentSizeValue = XPathAPI.selectNodeValue(node,
					"*[local-name()='Slot' and @*[local-name()='name']='size']/*[local-name()='ValueList']/*[local-name()='Value']/text()", false);

			try {
				cda.setContentSize(Long.parseLong(contentSizeValue));
			} catch (NumberFormatException e) {
				logger.log(Level.WARN, "Wrong content size: \"" + contentSizeValue + "\"");
			}
		} catch (XPathExpressionException e) {
			logger.warn("Couldn't find content size.", e);
		}
		// this.html = null;
		// this.tip = null;

		return cda;
	}

	/**
	 * Searches for the code of the specified category and creates it, if it doesn't exist.
	 * 
	 * @param node
	 * @param codeCategoryId
	 * @return
	 */
	@Transient
	private static String getCode(CdaDocument cda, Node node, String codeCategoryId) {
		String[] codes = getCodes(cda, node, codeCategoryId);

		if (codes != null && codes.length > 0) {
			if (codes.length > 1)
				logger.warn("Found more than one code for code category " + codeCategoryId);

			return codes[0];
		} else {
			return null;
		}
	}

	/**
	 * 
	 * @param cda
	 * @param node
	 * @param codeCategoryId
	 * @return
	 */
	@Transient
	private static String[] getCodes(CdaDocument cda, Node node, String codeCategoryId) {
		try {
			CdaCodeCategory category = CodeFetcher.getCodeCategoryByName(codeCategoryId);
			String uuid = category.getUuid();
			Node codeNode;
			String[] codeIds;
			NodeList nodeList;

			// String codeId = XPathAPI.selectNodeValue(node,
			// "*[local-name()='Classification' and @*[local-name()='classificationScheme']='"+uuid+"']/@nodeRepresentation",
			// false);
			nodeList = XPathAPI.selectNodeIterator(node, "*[local-name()='Classification' and @*[local-name()='classificationScheme']='" + uuid
					+ "']/@nodeRepresentation", false);
			codeIds = new String[nodeList.getLength()];

			for (int index = 0; index < nodeList.getLength(); index++) {
				codeNode = nodeList.item(index);
				codeIds[index] = codeNode.getNodeValue();

				if (codeIds[index] != null && CodeFetcher.getCdaCode(codeCategoryId, codeIds[index]) == null) {
					// TODO read the language and the description
					// the code doesn't exist in this DB, create it
					String displayName = XPathAPI.selectNodeValue(node, "*[local-name()='Classification' and @*[local-name()='classificationScheme']='" + uuid
							+ "' and @*[local-name()='nodeRepresentation']='" + codeIds[index] + "']/Name/LocalizedString/@value", false);
					// String language = XPathAPI.selectNodeValue(node,
					// "*[local-name()='Classification' and @*[local-name()='classificationScheme']='"+uuid+"']/Name/LocalizedString/@value",
					// false);
					String language = CdaCodeDescription.LANGUAGE_FRENCH;
					String description = null;
					String codeOid = null;
					category = CodeFetcher.getCodeCategoryByName(codeCategoryId);
					Integer parentId = null;

					if (CodeFetcher.TYPE_CODE.equals(codeCategoryId) && cda.getClassCode() != null) {
						CdaCode parentCode = CodeFetcher.getCdaCode(CodeFetcher.CLASS_CODE, cda.getClassCode());
						if (parentCode != null)
							parentId = parentCode.getId();
					}

					ESanteConfigManagerBean.getInstance().saveCdaCode(category.getId(), codeIds[index], codeOid, displayName, description, language, parentId);
				}
			}

			return codeIds;
		} catch (XPathExpressionException e) {
			logger.warn("Error while retriving code for type \"" + codeCategoryId + "\".", e);
			return null;
		}
	}

	/**
	 * Extracts all upload-relevant metadata of a specific document description originating from a RSQ request.<br/>
	 * <br/>
	 * the following attributes will be available:
	 * <ul>
	 * <li>title</li>
	 * <li>authorRole</li>
	 * <li>authorSpecialty</li>
	 * <li>classCode</li>
	 * <li>typeCode</li>
	 * <li>confidentialityCode</li>
	 * <li>healthcareFacilityCode</li>
	 * <li>practiceSettingCode</li>
	 * <li>languageCode</li>
	 * </ul>
	 * 
	 * @param node
	 *            The xml snippet containing the document meta data
	 * @return A hashmap containing the document metadata information
	 */
	@Transient
	public static HashMap<String, String> extractDocumentAttributes(Node node) {

		String key;
		String value;
		HashMap<String, String> metadata = new HashMap<String, String>();

		try {
			// add title
			key = "title";
			value = XPathAPI.selectNodeValue(node, "*[local-name()='ExtrinsicObject'/Name/LocalizedString/@value", false);
			metadata.put(key, value);
			// add author role
			key = "authorRole";
			value = XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d'/Slot[@name='authorRole']/ValueList/Value",
							false);
			metadata.put(key, value);
			// add author specialty
			key = "authorSpecialty";
			value = XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:93606bcf-9494-43ec-9b4e-a7748d1a838d'/Slot[@name='authorSpecialty']/ValueList/Value",
							false);
			metadata.put(key, value);
			// add classCode
			key = "classCode";
			value = XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:41a5887f-8865-4c09-adf7-e362475b143a'/@nodeRepresentation",
							false);
			metadata.put(key, value);
			// add typeCode
			key = "typeCode";
			value = XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:f0306f51-975f-434e-a61c-c59651d33983'/@nodeRepresentation",
							false);
			metadata.put(key, value);
			// add confidentialityCode
			key = "confidentialityCode";
			value = XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:f4f85eac-e6cb-4883-b524-f2705394840f'/@nodeRepresentation",
							false);
			metadata.put(key, value);
			// add healthcareFacilityCode
			key = "healthcareFacilityCode";
			value = XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:f33fb8ac-18af-42cc-ae0e-ed0b0bdb91e1'/@nodeRepresentation",
							false);
			metadata.put(key, value);
			// add practiceSettingCode
			key = "practiceSettingCode";
			value = XPathAPI
					.selectNodeValue(
							node,
							"*[local-name()='Classification' and @*[local-name()='classificationScheme']='urn:uuid:cccf5598-8b07-4b77-a05e-ae952c785ead'/@nodeRepresentation",
							false);
			metadata.put(key, value);
			// add languageCode
			key = "languageCode";
			value = XPathAPI.selectNodeValue(node, "*[local-name()='ExtrinsicObject'/Slot[@name='languageCode']/ValueList/Value", false);
			metadata.put(key, value);

		} catch (XPathExpressionException e) {
			logger.warn("Error while retrieving document metadata from DSP.", e);
			return null;
		}
		return metadata;
	}

/**
	 * Transforms an incident entry and provides it to a pdf-a document. The pdf-a document is also assigned to the incident entry itself and can be accessed via {@link IncidentEntry#getGeneratedPdfContent()
	 * @param entry The incident entry object for which the pdf-a document should be generated
	 * @return The generated pdf-a document
	 */
	public static byte[] getPdfA(IncidentEntry entry) {
		if (entry.getGeneratedPdfContent() == null) {
			/*
			 * transform the content of the incident entry on the GECAMed server. (an open office installation is needed for that)
			 */
			entry.setGeneratedPdfContent(GecamedToPdfConverterClient.transformToPdfA(entry));
		}

		return entry.getGeneratedPdfContent();
	}

	public static void openLuxtrustMiddlewareDownloadPage() {
		String lang = Locale.getDefault().getLanguage();
		String link;

		if (GECAMedLocale.FRENCH.equalsIgnoreCase(lang))
			link = LUXTRUST_MIDDLEWARE_DOWNLOAD_LINK_FR;
		else if (GECAMedLocale.GERMAN.equalsIgnoreCase(lang))
			link = LUXTRUST_MIDDLEWARE_DOWNLOAD_LINK_DE;
		else
			link = LUXTRUST_MIDDLEWARE_DOWNLOAD_LINK_EN;

		UrlOpener.openURL(link);
	}

	public static void initialize() {
		if (isESanteModuleActivated() && initializer == null) {
			initializer = new ESanteInitializer();
			initializer.start();
		}
	}

	/* ======================================== */
	// CLASS: ESANTE INITIALIZER
	/* ======================================== */

	private static class ESanteInitializer extends Thread {
		public ESanteInitializer() {
			super("ESante initializer");
			// setPriority(MIN_PRIORITY);
		}

		@Override
		public void run() {
			logger.info("Start searching for LustTrust Middleware ...");
			long time = System.currentTimeMillis();
			// run this in a thread in background, as it might take quite some time on some machines
			Security.getInactivityMonitor();
			time = System.currentTimeMillis() - time;
			logger.info("Finished searching for LustTrust Middleware (took " + (time / 1000) + "." + (time % 1000) + " sec.)");
		}
	}
	
	public static void clearCache() {
		EHealthIDRequestDialog.getInstance().clearCache();
	}

	public static void deleteEsanteUser(String eSanteUserID) throws Exception {
		if (eSanteUserID == null || eSanteUserID.length() == 0) return; 
			
		ESanteConfigManagerBean.getInstance().deleteESanteUser(eSanteUserID);
		
		ESanteGuiUtils.clearCache();
		GECAMedLog.user("eSante", "DELETE USER", "Deleted eSante User with eSanteUserID: " + eSanteUserID);
	}

	/**
	 * delete PRACTICE_EHEALTH_ID if USER_EHEALTH_ID was wrong 
	 * and practice eHealth id was created from that USER_EHEALTH_ID.
	 */
	public static void deletePracticeIDifMatchesEhealthUserID() {
		
		String userEHealthId = getUserEHealthId();
		String practiceID = getPracticeEHealthId(userEHealthId);
		
		if ((WebserviceConstants.GENERAL_PRACTICE_IDENTIFIER + userEHealthId).equals(practiceID)) {
			ESanteConfigManagerBean.getInstance().deletePracticeEhealthID();
		}
		
		GECAMedLog.user("eSante", "DELETE PRACTICE_EHEALTH_ID", "Deleted PRACTICE_EHEALTH_ID: " + practiceID);
	}
}
