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

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.StringWriter;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.Gender;
import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.widgets.ErrorDialog;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.CdaCode;
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.CDAManagerBean;
import lu.tudor.santec.gecamed.esante.ejb.session.beans.ESanteConfigManagerBean;
import lu.tudor.santec.gecamed.esante.ejb.session.interfaces.CDAManager;
import lu.tudor.santec.gecamed.esante.ejb.session.interfaces.ESanteConfigManager;
import lu.tudor.santec.gecamed.esante.gui.data.Attachment;
import lu.tudor.santec.gecamed.esante.gui.data.Configuration;
import lu.tudor.santec.gecamed.esante.gui.dialogs.ConnectingDialog;
import lu.tudor.santec.gecamed.esante.gui.dialogs.ESanteDialog;
import lu.tudor.santec.gecamed.esante.gui.dialogs.ESanteUploadDialog;
import lu.tudor.santec.gecamed.esante.gui.dialogs.LoginDialog;
import lu.tudor.santec.gecamed.esante.gui.dialogs.PdfPreviewDialog;
import lu.tudor.santec.gecamed.esante.gui.tab.CdaHandler;
import lu.tudor.santec.gecamed.esante.gui.utils.CodeFetcher;
import lu.tudor.santec.gecamed.esante.gui.utils.ESanteGuiUtils;
import lu.tudor.santec.gecamed.esante.gui.worker.CdaUtils;
import lu.tudor.santec.gecamed.esante.gui.worker.DocumentWorker;
import lu.tudor.santec.gecamed.esante.utils.ESanteUtils;
import lu.tudor.santec.gecamed.esante.utils.XmlHelper;
import lu.tudor.santec.gecamed.esante.utils.exceptions.SendingStoppedException;
import lu.tudor.santec.gecamed.importexport.utils.XPathAPI;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Physician;
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.ejb.entity.beans.PatientAddress;
import lu.tudor.santec.gecamed.patient.ejb.session.beans.IncidentManagerBean;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.IncidentManager;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version <br>
 *          $Log: XDS.java,v $ Revision 1.86 2014-02-13 15:43:07 ferring Old changes recommitted
 *
 *          Revision 1.84 2014-02-13 14:59:30 ferring upload allowed cases changed
 *
 *          Revision 1.83 2014-02-12 12:17:02 ferring short and long eHealth IDs introduced
 *
 *          Revision 1.82 2014-02-06 14:33:59 ferring SendingStoppedException handling and logging changed Documents are order by creation time again If
 *          download file of the document is still in cache, it will be set and the meta data will be read, when loading the documents
 *
 *          Revision 1.81 2014-02-05 10:02:16 ferring Uploaded documents can now be blocked and normal, restricted or private
 *
 *          Revision 1.80 2014-02-04 10:08:29 ferring eSante ID management completed Only those documents will be shown, that are retrieved by the RSQ
 *
 *          Revision 1.79 2014-01-31 16:29:43 donak Added error dialog when document cannot be uploaded due to invalid access privileges Fixed bug that
 *          prevented saml assertions from being renewed after they exceeded in cache Fixed bug that prevented documents from being uploaded (gecamed id has not
 *          been written to template due to renaming of placeholder) SMART UPLOAD (TM) feature: Upload option is added to context menu dependent on dsp access
 *          permissions and upload success probability calculations Upload support for images added
 *
 *          Revision 1.78 2014-01-30 11:58:22 ferring replace all occurrences instead of only the first
 *
 *          Revision 1.77 2014-01-29 16:57:50 donak Renamed references to HPD to eHealth in respect to the request of the eHealth Agency Removed template
 *          DSP-11-12 as it is not needed and was created only for internal testing purposes
 *
 *          Revision 1.76 2014-01-28 17:53:11 donak Practice eHealth id is now set automatically Document format is now determined automatically Text of eHealth
 *          id input dialog has been changed DSP id --> eHealth ID eHealth id support link is now obtained from db
 *
 *          Revision 1.75 2014-01-28 14:10:32 ferring App name and version taken from system properties
 *
 *          Revision 1.74 2014-01-27 13:13:47 donak * Properties are now saved in db in respect to the following contexts: - specific to a GECAMed user -
 *          specific to a GECAMed physician - specific to an eSanté plattform user - general (independend of GECAMed user, physician, eSanté platform user) *
 *          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. * Naming of message loggings had been adapted to the naming in the connection kit (e.g. DSP-10, DSP-22) * 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.
 *
 *          Revision 1.73 2014-01-27 10:05:49 ferring query since prepared
 *
 *          Revision 1.72 2014-01-17 08:48:19 ferring check, whether presence password is required
 *
 *          Revision 1.71 2014-01-13 14:11:11 ferring accept 1 or more digits as HPID
 *
 *          Revision 1.70 2014-01-13 13:44:45 ferring Matcher searches for 10 to 12 digits
 *
 *          Revision 1.69 2014-01-10 10:50:52 ferring *** empty log message ***
 *
 *          Revision 1.68 2014-01-07 13:19:44 donak Adding initial version of pdf preview dialog Several bug fixes
 *
 *          Revision 1.67 2014-01-02 15:07:58 donak Implemented authentication workflow: user-saml request --> check ehr access permissions --> dsp saml request
 *          --> actual webservice request All saml assertions independent of type are cached.
 *
 *          Revision 1.66 2013-12-27 18:08:15 donak Cleanup of imports
 *
 *          Revision 1.65 2013-12-24 17:33:15 donak Changed exception handling to focus assertion related exceptions to gerValidSamlAssertionForPatient()
 *          Included exception handling for SmartCardExceptions Fixed bug in UserInactivityMonitor which caused inactive sessions Partially rewrote
 *          SamlAssertion to ease and centralize logic
 *
 *          Revision 1.64 2013-12-19 12:30:09 ferring Nullpointer caught for patient without birthday
 *
 *          Revision 1.63 2013-12-18 09:28:21 ferring null and empty checks added
 *
 *          Revision 1.62 2013-12-17 17:41:49 donak Fix: ITI-41 creationTime tag contains now the real creation time of the document instead of the submission
 *          time Fix: ITI-41 the full oid of the transmitted document is now written to the database (synchronization with document download should work again)
 *          Stabilized smartcard management
 *
 *          Revision 1.61 2013-12-17 16:49:51 ferring RSQ handling changed
 *
 *          Revision 1.60 2013-12-17 13:22:21 ferring replacing improved
 *
 *          Revision 1.59 2013-12-17 12:54:28 ferring SendingStoppedException caught
 *
 *          Revision 1.58 2013-12-16 12:05:12 ferring logging eSanté actions
 *
 *          Revision 1.57 2013-12-13 12:31:42 ferring Exception handling changed
 *
 *          Revision 1.56 2013-12-10 11:59:27 ferring webservice error handling improved
 *
 *          Revision 1.55 2013-12-09 17:15:37 donak Bug-fixing for upload to eSanté platform
 *
 *          Revision 1.54 2013-12-08 22:14:40 donak Upload for CDA documents to eSanté platform finished. However upload is still not working as required codes
 *          do not seem to be implemented at the platform repository
 *
 *          Revision 1.53 2013-12-06 16:25:23 ferring CDA synch fixed
 *
 *          Revision 1.52 2013-12-05 18:50:34 donak Partial upload support for the eSanté platform, not yet functional
 *
 *          Revision 1.51 2013-12-05 15:19:25 donak Partial upload support for the eSanté platform, not yet functional
 *
 *          Revision 1.50 2013-12-05 12:27:19 ferring CDA columns renamed Login data of user stored Checking if CDA doc exists, creating a new one
 *
 *          Revision 1.49 2013-12-04 13:12:36 ferring Checking eSante server status
 *
 *          Revision 1.48 2013-11-28 16:11:16 ferring wrong flag for upload fixed
 *
 *          Revision 1.47 2013-11-28 10:38:23 ferring GECAMedUtils split into utils and GUI utils classes
 *
 *          Revision 1.46 2013-11-22 14:51:13 ferring Restructured CDA tree and list notification, in order not to use an EntityBean as model. Buttons added to
 *          delete the CDA file and remove the incident entry
 *
 *          Revision 1.45 2013-11-21 10:50:35 ferring bug fixed, where last document was downloaded at every refresh
 *
 *          Revision 1.44 2013-11-21 09:43:10 ferring hand over DSP to CDA documents
 *
 *          Revision 1.43 2013-11-19 17:36:44 ferring MIME response data extraction changed and bug fixed, where wrong SAML assertion was added to request
 *
 *          Revision 1.42 2013-11-18 15:36:53 ferring Restructured methods with SoapSender and DocumentWorker restructured. Checking the IPID, if it changes and
 *          refreshing the data.
 *
 *          Revision 1.41 2013-11-14 18:23:11 donak belongs to the last commit (resolved conflicts)
 *
 *          Revision 1.40 2013-11-12 15:37:53 ferring downloads can now be stopped
 *
 *          Revision 1.39 2013-11-12 12:48:22 donak Document upload: * conversion to pdf/a using open office has been moved to the server. OpenOffice 4 has to
 *          be located in the jboss work directory. ATTENTION: it still has to be evaluated if the license agreement dialog occurs when instance is started the
 *          first time on the server. * If document contains a description, the first forty characters of the description followed by three dots will be used as
 *          title instead of the filename * Upload of incident type letters has been fixed * upload for docx files has been added
 *
 *          Upload parameters: * database does now support storage of user dependent properties * The system will automatically remember the last chosen values
 *          for confidentiality, facility type, and speciality and propose them as default when the next document will be uploaded.
 *
 *          Inactivity Monitor: * the event mouse wheel scrolling is now taken into account for resetting the logoff timer * the logoff delay is now stored in
 *          the database. If the database does not contain this parameter, it will be created
 *
 *          General: * Optimized incident entry bean handling. Caching will now avoid copying the binary content and the generated pdf content of an incident
 *          entry as these elements should only be loaded when needed. Now it should be save to re-implement a proper getBinaryContent() handling.
 *
 *          Revision 1.38 2013-11-08 08:51:22 ferring *** empty log message ***
 *
 *          Revision 1.37 2013-11-05 09:47:56 ferring ESantePatient changed to Dsp. The Dsp object will be stored in the database, if a patient is linked with
 *          eSanté.
 *
 *          Revision 1.36 2013-10-31 16:35:21 donak Adjusted formatting of date in default name for prescriptions in upload metadata dialog Additional fixes for
 *          inactivity monitor. Remember: card watchdog becomes only active after getAuthentificationCertificate() was called. (has to be re-called after
 *          watchdog fired). Additional fixes for 2 pass conversion to pdf/a format
 *
 *          Revision 1.35 2013-10-29 10:04:08 donak Fixed: Unlinked patient records possessed the possibility to upload documents to the DSP, which is
 *          impossible Fixed: User will now be informed if client time is out of sync with the server and thus the saml assertion is denied
 *
 *          Revision 1.34 2013-10-24 15:11:56 donak Added LuxTrust card watchdog. Harmonized "invalid authentication data" dialogs optimized upload process
 *
 *          Revision 1.33 2013-10-23 14:56:25 donak Inactivity watchdog integration (currently timeout is set to 5 minutes). 30 seconds before automated logout
 *          takes place, a warning message will be displayed on screen. Watchdos is activated as soon as a saml assertion is cached and disabled after it purged
 *          the saml cache and reset the user password for accessing the DSP. Availability of LuxTrust card is currently not checked but will be added to the
 *          next version. Added specifc error message if user logs in with invalid authentication information.
 *
 *          Revision 1.32 2013-10-22 17:11:12 donak Under certain circumstances the reference to the triggering component could be lost which would result in a
 *          null pointer exception - fixed.
 *
 *          Revision 1.31 2013-10-22 14:33:19 ferring Thread canceling prepared
 *
 *          Revision 1.30 2013-10-22 05:54:31 ferring imports removed
 *
 *          Revision 1.29 2013-10-21 10:58:28 donak Fixed receive file content bug Fixed bug about not displaying progress indicator (animated gif) when context
 *          is not on JTable Fixed upload menu item for letters where item text was not language dependent (also removed ProvideDocumentAction as it was
 *          redundant)
 *
 *          Revision 1.28 2013-10-18 16:58:04 donak Improved transformation performance Incident Entries are now transformed to pdf/a instead of pdf
 *
 *          Revision 1.27 2013-10-17 14:54:30 donak Corrected upload handler for letters Defined incident entry dependent presets for upload metadata Bug fixing
 *          and documentation
 *
 *          Revision 1.26 2013-10-16 14:37:02 donak Finished document uploading. Increased performance, more intuitive upload process including progress
 *          indicator Created a separate library-like class for IncidentEntry to pdf conversion
 *
 *          Revision 1.25 2013-10-15 10:27:41 donak Fixed missing type code filtering at upload metadata dialog start Fixed missing file content of error file
 *          in case of eSanté error Added repaint for history item to directly display "my DSP" if document was sucessfully uploaded
 *
 *          Revision 1.24 2013-10-10 16:31:35 donak Started to add eSanté "uploaded" indicators to incident entries
 *
 *          Revision 1.23 2013-10-10 11:28:50 donak Integration of cda unique document id is now persistent context menu now adapts to the upload status of a
 *          document Revision 1.22 2013-10-09 16:29:35 donak Integration of cda unique document id
 * 
 *          Revision 1.21 2013-10-09 12:14:06 donak Integration of the error dialog in the eSant� document upload process Revision 1.20 2013-10-08 16:43:43
 *          donak Adjusted ErrorDialog to allow user to copy error details to clipboard or save them to file system Changed order of code display names in
 *          CdaUpload dialog for desc. to asc.
 * 
 *          Revision 1.19 2013-10-08 12:46:48 donak eSant� upload dialog and logging
 * 
 *          Revision 1.18 2013-10-08 08:54:14 ferring class name refactoring and tree view implementation (first steps)
 * 
 *          Revision 1.17 2013-10-07 16:12:12 donak Logging, message backup, and error protocol creation
 * 
 * 
 */
public class XDS {

	/* ======================================== */
	// CONSTANTS
	/* ======================================== */

	private static final String UNKNOWN = "UNKNOWN";
	final static String typeSystemEsanteDoctype = "eSanté doctype";				
	final static String typeSystemLoinc = "LOINC";
	final static String CustomTypeSystem = "1.3.182.5.9";

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

	private static DateFormat formatUTCDate = new SimpleDateFormat("yyyyMMdd");
	private static DateFormat formatUTCTimeWithOffset = new SimpleDateFormat("yyyyMMddHHmmssZ");
	private static DateFormat formatUTCTime = new SimpleDateFormat("yyyyMMddHHmmss");

	private static Logger logger = Logger.getLogger(XDS.class.getName());

	// internal document counter
	private static int uidCounter = 1;
	// needed for logging on the server (sent or erroneous soap messages)
	private static CDAManager manager = CDAManagerBean.getInstance();
	// needed for updating incident entry status
	protected static IncidentManager incidentManager = IncidentManagerBean.getInstance();

	// the application id of GECAMed. This is currently "1337" and has been defined by the vendor
	private static String gecamedApplicationId = null;

	private static ESanteConfigManager eManager = ESanteConfigManagerBean.getInstance();
	// private static Integer userId = GECAMedModule.getCurrentUser().getId();

	// Extracts relevant certificate owner data from saml assertion
	private final static Pattern certOwnerInfoPat = Pattern.compile("(\\d+\\^[.[^\\^]]+\\^[.[^\\^]]+)\\^.+\\^\\^\\^EI");
	// Extracts the document oid from the upload template after the template has been filled
	private final static Pattern documentOidPat = Pattern
			.compile("<rim:ExternalIdentifier[.[^>]]+identificationScheme=\"urn:uuid:2e82c1f6-a085-4c72-9da3-8640a32e42ab\"[.[^>]]+value=\"([.[^\"]]+)\"");
	// Extracts the document oid from the upload template after the template has been filled (alternative pattern if value attribute comes before urn attribute)
	private final static Pattern documentOidAltPat = Pattern
			.compile("<rim:ExternalIdentifier[.[^>]]+value=\"([.[^\"]]+)\"[.[^>]]+identificationScheme=\"urn:uuid:2e82c1f6-a085-4c72-9da3-8640a32e42ab\"");

	/* ======================================== */
	// STATIC METHODS
	/* ======================================== */

	/**
	 * Starts a new thread that handles the complete upload procedure
	 * 
	 * @param actionEvent
	 *            The event that triggered the upload request
	 * @param document
	 *            The incident entry containing the data that should be uploaded to the eSanté DSP
	 * @param uploadEntry
	 *            Defines if the document belonging to this incident should be uploaded. If set to false, only the metadata of the document will be updated at
	 *            the DSP (if document was uploaded)
	 * @param uploadMode
	 *            Defines in case of an update if metadata, document, or both should be updated on the DSP
	 */
	public static void initiateUpload(final ActionEvent actionEvent, final IncidentEntry entry, final CdaDocument documentMetadata, final int uploadMode) {
		new Thread() {
			public void run() {

				// this id is set when the document has already been uploaded to the dsp
				String uniqueDocId = entry.getCdaUniqueId();
				// identify the graphical element that triggered the function
				Component source = ((JPopupMenu) ((JComponent) actionEvent.getSource()).getParent()).getInvoker();

				// create the sender object
				Patient patient = GECAMedModule.getCurrentPatient();
				Dsp uploadDsp = CDAManagerBean.getInstance().getLinkedDsp(patient.getId());
				SoapSender sender = new SoapSender(uploadDsp, true);

				// set the id of the document, that should be uploaded
				sender.setDocumentUniqueId(entry.getCdaUniqueId());

				// create a dialog that allows the user to config document meta data for the eSanté DSP
				ESanteUploadDialog metaData = null;
				try {
					
					// just to be sure that we have a date.
					if (entry.getCreated() == null) {
						entry.setCreated(new Date());
					}
					if (entry.getEntryDate() == null) {
						entry.setEntryDate(new Date());
					}
					
					// visually indicate that this item is currently uploaded or updated
					entry.setCdaUniqueId("uploading");
					// update the underlying table to show the "in progress" icon
					source.repaint();

					try {
						// is user is not yet logged into the eSanté platform, he has to log in now as the smartcard id is needed for further processing
						Security.getUserSamlAssertion(sender, false);
					} catch (SendingStoppedException e) {
						return;
					}

					if (uploadMode == WebserviceConstants.UPLOAD_INITIALE_UPLOAD) {
						metaData = new ESanteUploadDialog(entry);
						// for a prescription it is possible to generate a default name for the upload (as document type is known)
						if (IncidentManager.PRESCRIPTION.equals(entry.getEntryType().getName())) {
							// create a generic title for the prescription
							metaData.setDocumentName(String.format(Translatrix.getTranslationString("esante.actions.upload.dialog.label.prescriptionTitle"), entry.getCreated()));
							metaData.setClassCode("65");
							metaData.setTypeCode("57833-6");
						} else if (IncidentManager.LETTER.equals(entry.getEntryType().getName())) {
							// preset the document class to "Documents de gestion"
							// metaData.setClassCode("95");
						}

					} else {
						// this part treats the update of an already uploaded document or its metadata
						metaData = new ESanteUploadDialog(entry, documentMetadata);
					}
					// upload the document to the electronic health record of the patient
					if (!XDS.uploadDocumentToDsp(sender, metaData)) {
						logger.error("UPLOAD ERROR - resetting dsp id of the document");
						entry.setCdaUniqueId(uniqueDocId);
					}
				} catch (Exception e) {
					e.printStackTrace();
					// indicate error to the user
					ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(), "Unable to find document in dsp:", e.getMessage(),
							GECAMedModule.getCurrentPatient().getId(), null);
					// reset the document id to the initial value for removing the upload indicator
					entry.setCdaUniqueId(uniqueDocId);
				}

				// delete it as it is no longer needed but occupies memory
				entry.setGeneratedPdfContent(null);

				// repaint triggering element to visually indicate changes
				source.repaint();
			}
		}.start();
	}

	/**
	 * Extracts the full document oid from the template
	 * 
	 * @param template
	 *            The upload template the document oid should be extracted from
	 * @return The full document oid or null if none was found (it is importent that the tag features the urn - "urn:uuid:2e82c1f6-a085-4c72-9da3-8640a32e42ab")
	 */
	private static String extractDocumentOid(String template) {
		String docOid = null;
		Matcher oidFinder = null;

		oidFinder = documentOidPat.matcher(template);
		if (oidFinder.find()) {
			docOid = oidFinder.group(1);
		} else {
			oidFinder = documentOidAltPat.matcher(template);
			if (oidFinder.find()) {
				docOid = oidFinder.group(1);
			}
		}

		return docOid;
	}

	/**
	 * Extracts information about the certificate owner from the saml assertion
	 * 
	 * @param samlAssertion
	 *            The saml assertion from which the information will be extracted
	 * @return A String like <Cert-ID>^<Family Name>^<Prename^^^^^^^^^^EI
	 * @throws Exception
	 *             If the saml assertion does not contain the required information
	 */
	private static String getCertOwnerProperties(String samlAssertion) throws Exception {
		Matcher infoMatcher = certOwnerInfoPat.matcher(samlAssertion);

		if (!infoMatcher.find()) {
			throw new Exception("SAML token is missing and thus no user information can be extracted.");
		}
		// System.out.println(infoMatcher.);
		return infoMatcher.group(1);
	}
	

	/**
	 * Provides the value of an internal document counter. This counter is round robin. When max integer is reached, it restarts at 1;
	 * 
	 * @return The counter
	 */
	private static int getUidCounter() {
		if (XDS.uidCounter == Integer.MAX_VALUE) {
			XDS.uidCounter = 1;
		}
		return XDS.uidCounter++;
	}

	public static String getGecamedApplicationId() {
		if (XDS.gecamedApplicationId == null) {
			XDS.gecamedApplicationId = eManager.getUserProperty(ESanteProperty.PROP_GECAMED_APPLICATION_ID, null);
		}
		return XDS.gecamedApplicationId;
	}

	/**
	 * Provides a guaranteed unique uid. <br/>
	 * <br/>
	 * The syntax of such an uid has to be compliant to the following format: "((?:0|[1-9][0-9]*)(?:\.(?:0|[1-9][0-9]*))+)(?:\^(\w{1,16}))?"
	 * 
	 * @return The UID
	 */
	public static String getUID() {
		// assemble a new unique OID extension using the following pattern:<counter>.<system milliseconds>
		return new StringBuilder(20).append(XDS.getUidCounter()).append(System.currentTimeMillis()).toString();
	}

	static {
		// formatUTCDate.setTimeZone(TimeZone.getTimeZone("UTC"));
		formatUTCTime.setTimeZone(TimeZone.getTimeZone("UTC"));
		formatUTCTimeWithOffset.setTimeZone(TimeZone.getTimeZone("UTC"));
	}

	// public static void loadDocuments(Dsp dsp, Configuration config) throws Exception {
	// loadDocuments(null, dsp, config);
	// }

	/**
	 * Retrieves all documents of a patient and assigns them to him. The document list is directly assigned to provided patient object. Thus this function does
	 * not provide any return value.
	 * 
	 * @param dsp
	 *            The patient of whom the document list should be generated
	 * @param config
	 *            Configuration parameters needed for accessing the eSanté platform
	 * @throws Exception
	 *             Check stack trace for further details
	 */
	public static void loadDocuments(SoapSender sender, CdaHandler cdaHandler) throws Exception {
		// get list of documents
		Collection<CdaDocument> documents = XDS.callITI18FindDocuments(sender);

		// add documents to patients
		// sender.getDsp().addDocuments(documents);
		// cdaHandler.addDocuments(documents);
		Dsp dsp = sender.getDsp();
		dsp.setDocuments(documents);
		cdaHandler.setDsp(dsp);

		ESanteGuiUtils.trace("Documents",
				"Meta data of all CDA documents for patient " + dsp.getPatientId() + " (DSP \"" + dsp.getDspOid() + "\") downloaded.");
	}

	/**
	 * Downloads the content of a document of a patients from his eSanté DSO
	 * 
	 * @param patientESanteId
	 *            The unique identifier of the patient at the eSanté platform
	 * @param document
	 *            The meta-data describing the document that should be downloaded
	 * @param config
	 *            Configuration information for accessing the eSanté DSP via SOAP
	 * @return The document content encoded as base64 string
	 * @throws Exception
	 *             Please check stack trace for details
	 */
	public static byte[] loadContentOfDocument(SoapSender sender, CdaDocument document) throws Exception {
		byte[] content = XDS.callITI43RetrieveDocumentSet(sender, document);

		Dsp dsp = ESanteGuiUtils.getDspOfDocument(document);
		String dspOid;
		if (dsp != null)
			dspOid = dsp.getDspOid();
		else
			dspOid = null;

		if (content != null) {
			ESanteGuiUtils.trace("Documents",
					"CDA document \"" + document.getDocumentUniqueId() + (dspOid == null ? "" : "\" of DSP \"" + dspOid) + "\" opened successfully.");
		} else {
			logger.error("Couldn't download the content of the CDA document \"" + document.getDocumentUniqueId()
					+ (dspOid == null ? "" : "\" of DSP \"" + dspOid) + "\".");
		}

		return content;
	}

	/**
	 * Uploads a document of a given patient to his eSanté DSP
	 * 
	 * @param dsp
	 *            Reference to the GECAMed patient of whom a document should be uploaded
	 * @param metaData
	 *            The dialog object containing the meta data for the document upload request. The function will open the dialog and ask the user vor
	 *            validation/configuration of the metadata. The developer has the possibility to set some default values before calling this function.
	 * @return True if the upload was successful, false otherwise
	 */
	public static boolean uploadDocumentToDsp(SoapSender sender, ESanteUploadDialog metaData) {

		/******************************************************************
		 * Prepare templates and complex values that have to be filled in *
		 ******************************************************************/

		byte[] data = null;
		boolean isUpdate = metaData.getMetadata() != null && metaData.getMetadata().getDocumentOid() != null;
		boolean success = false;
		String errorDetails = "";
		int localPatientId = -1;
		IncidentEntry document;
		Patient gecamedPatient = null;
		String userEHealthId = null;
		String practiceEHealthId = null;
		String gecamedId = null;
		String certOwnerInfo = null;
		String certOwnerName = null;
		String certOwnerFirstName = null;
		StringBuilder errorBuilder = new StringBuilder();
		Dsp dsp = null;

		try {
			// the document is actually the incident entry
			document = metaData.getIncidentEntry();
			// generate a unique document id. (if no filename is provided, this
			// id will also be used as part of the filename)
			String documentUniqueId = XDS.getUID();
			// load unique ids from database
			// obtain reference to the patient to whom this document belongs
			gecamedPatient = GECAMedModule.getCurrentPatient();
			// first, a reference to the corresponding dsp is needed
			dsp = sender.getDsp();
			// get the GECAMed patient id (this one differs from the one of the
			// eSanté platform)
			localPatientId = dsp.getPatientId();
			// The eSanté patient id is also needed for addressing the right DSP
			String eSantePID = dsp.getDspOid();
//			if(eSantePID.indexOf('^')!= -1){
//				// the remaining part is not needed
//				eSantePID= eSantePID.substring(0, eSantePID.indexOf('^'));
//			}

			// obtain the eHealth Id of the user
			userEHealthId = ESanteGuiUtils.getUserEHealthId();
			if (userEHealthId == null) {
				return false;
			}
			String userEHealthIdShort = ESanteUtils.getShortEHealthId(userEHealthId);

			// obtain the eHealth Id of the practice
			practiceEHealthId = ESanteGuiUtils.getPracticeEHealthId(userEHealthId);
			if (practiceEHealthId == null) {
				return false;
			}
			String practiceEHealthIdShort = ESanteUtils.getShortEHealthId(practiceEHealthId);
			
			// create the custodian element with
			String custodian = null;
			if (practiceEHealthId.startsWith(WebserviceConstants.GENERAL_PRACTICE_IDENTIFIER)) {
				// if practiceEHealthId is GENERAL_PRACTICE_IDENTIFIER + officecode -> use nullFlavor  
				custodian = "<id nullFlavor=\"NI\"/>";
			} else {
				// for other special practices like MM use their own code.
				custodian = "<id extension=\"" + practiceEHealthId + "\" root=\"1.3.182.4.1\"/>";
			}

			// finally the GECAMed id is the last element needed for building valid OIDs
			gecamedId = getGecamedApplicationId();
			if (gecamedId == null) {
				throw new Exception("The GECAMed id is missing and needs to be configured in the database. Please contact the support for further assistence.");
			}

			// get a reference to the current access privileges for the patient DSP
			// This is done before the saml assertion is obtained for being able to differentiate if the user is already logged in.
			// If the user is not yet logged in to the dsp, there are no cached access privileges
			DspPrivileges accessPrivileges = Security.getCachedDspPrivileges(sender.getDsp().getDspOid());

			// Now request a valid saml token for the patient of whom the document should be uploaded
			// (if there still is a cached valid token it will be used, otherwise a template is filled and the token is requested)
			// if a new token is requested, GECAMed also requests the presence password from the user - if that would help gaining sufficient access to the DSP
			String samlAssertion = null;

			try {
				samlAssertion = Security.getDspSAMLAssertion(sender, false);
			} catch (SendingStoppedException e) {
				return false;
			}

			// We want to avoid annoying the user twice with the presence password dialog. Thus, such a password will not be requested if the code above
			// requested a new saml assertion from the platform.
			// however, if the user was already logged into the DSP (== there were already access privileges before the assertion had been requested), GECAMed
			// will now give him another chance to enter his present password
			if ((accessPrivileges != null) && accessPrivileges.isPresencePasswordAllowed()) {
				// Ask the user for the presence password as it is needed for uploading
				sender.setPresencePassword(ESanteDialog.showInputMessageDialog(MainFrame.getInstance(), Translatrix._("esante.exceptions.notAuthorized"),
						sender.getPresencePassword() == null ? Translatrix._("esante.exceptions.notAuthorized.enterPresenceCode")
								: Translatrix._("esante.exceptions.notAuthorized.reenterPresenceCode"),
						sender.getPresencePassword()));

				// if the user actually provided a presence password
				if (sender.getPresencePassword() != null) {
					try {
						// obtain an updated assertion (if password has been provided)
						samlAssertion = Security.getDspSAMLAssertion(sender, false);
					} catch (SendingStoppedException e) {
						return false;
					}
				}
			}

			// Now request the updated privileges from cache
			accessPrivileges = Security.getCachedDspPrivileges(sender.getDsp().getDspOid());
			// and check if the user now has the permission to upload a document. If not, indicate the reason to the user
			if (!accessPrivileges.isUploadAllowed()) {
				String message = Translatrix._("esante.actions.upload.failure.message",
						new String[] { gecamedPatient.getFirstName() + ' ' + gecamedPatient.getSurName() });
				String reason = "";
				if (accessPrivileges.isOnBlacklist()) {
					// the user has been set on the blacklist by the patient
					reason = Translatrix._("esante.actions.upload.failure.reason.blacklisted");
				} else if (accessPrivileges.getEhrState() != DspPrivileges.DSP_STATE_OPEN) {
					// the DSP is not accessible because it is not open
					reason = Translatrix._("esante.actions.upload.failure.reason.dspNotOpen");

					if (accessPrivileges.getEhrState() == DspPrivileges.DSP_STATE_DELETED) {
						// dsp is deleted
						reason += Translatrix._("esante.accessRights.dspState.deleted");
					} else if (accessPrivileges.getEhrState() == DspPrivileges.DSP_STATE_CLOSED) {
						// dsp is closed
						reason += Translatrix._("esante.accessRights.dspState.closed");
					}
				} else {
					// the user did not provide a presence password and therefore does not possess a mandate that is eligible for upload
					reason = Translatrix._("esante.actions.upload.failure.reason.unsufficientMandate");
				}
				message += reason;
				// display error dialog
				ESanteDialog.showMessageDialog(MainFrame.getInstance(), Translatrix._("esante.actions.upload.failure.title"), message,
						GECAMedBaseDialogImpl.OK_BUTTON_MODE, GECAMedModule.getBigIcon(GECAMedIconNames.WARNING));

				return false;
			}

			// extract information about the certificate owner from the saml assertion. Those have to be reinserted in the XDS message
			certOwnerInfo = getCertOwnerProperties(samlAssertion);
			if (certOwnerInfo == null) {
				certOwnerInfo = UNKNOWN;
				certOwnerFirstName = UNKNOWN;
				certOwnerName = UNKNOWN;
			} else {
				String[] fields = certOwnerInfo.split("\\^");
				try {
					certOwnerName 		= fields[1];
					certOwnerFirstName 	= fields[2];
				} catch (Exception e) {
					logger.warn("Error extracting Author name from luxthrust card", e);
					certOwnerName = UNKNOWN;
					certOwnerFirstName = UNKNOWN;
				}
			}

			// obtain the pdf/a document
			data = ESanteGuiUtils.getPdfA(document);
			if (data == null) {
				return false;
			}

			if (!PdfPreviewDialog.validatePdf(data, (document.getOriginalFilename() != null) ? document.getOriginalFilename() : "generated document")) {
				return false;
			}

			// Allow the physician to configure meta data of the document to be transferred
			if (!metaData.configureDocumentAttributes()) {
				// User pressed cancel button
				return false;
			}

			// encode the pdf as base64 for cda submission
			String pdfBase64 = Security.encodeBase64(data);
			// Now some templates have to be loaded: First, the already
			// soap-wrapped cda-template used for uploading a user document to the eSanté DSP
			String uploadTemplate = (isUpdate) ? SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_ITI_41_REPLACE_DOCUMENT)
					: SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_ITI_41_UPLOAD_DOCUMENT);
			// finally the document template is needed for sending the pdf as mime attachment
			String uploadPdf = SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_CDA_DOCUMENT);
			// assemble an IETF RFC 3066 compliant language code
			String languageCode = metaData.getLanguageCode().getCodeId();

			/**
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! LANGUAGE CODE IS CURRENTLY FIXED TO FRENCH AS CODES IN OTHER LANGUAGES ARE PARENTLY MISSING !!!!!!!!!!!!!!!!!!!
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! THE FOLLOWING INSTRUCTION HAS TO BE REMOVED AS SOON AS THE AGENCE ESANTE FIXED THIS ISSUE !!!!!!!!!!!!!!!!!!!!
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			 */
			// TODO REMOVE THIS HACK TO ENABLE MULTI LANGUAGE SUPPORT AS SOON AS THE AGENCE ESANTE PROVIDES THE PROPER CODE VALUES
			// if (languageCode == null)
			languageCode = "fr-LU";
			// and also the patient gender
			String patientGender = gecamedPatient.getGender();
			String patientGenderName = null;
			String patientGenderCode = null;
			String patientGenderCodeXds = null;

			// TODO REPLACE THIS BY A LOCALIZED REQUEST TO THE DATABASE AS SOON AS THE AGENCE ESANTE PROVIDES PROPER MULTI LANGUAGE CODES
			if (Gender.MALE.equalsIgnoreCase(patientGender)) {
				patientGenderCodeXds = "M";
				patientGenderCode = "M";
				patientGenderName = "Masculin";
			} else if (Gender.FEMALE.equalsIgnoreCase(patientGender)) {
				patientGenderCodeXds = "F";
				patientGenderCode = "F";
				patientGenderName = "Féminin";
			} else {
				patientGenderCodeXds = "U";
				patientGenderCode = "UN";
				patientGenderName = "Indifférencié";
			}

			// obtain reference to the physician who added the incident to which
			// this document belongs. This physician will be assumed as author
			Physician author = null;

			// identify the author of the document
			author = MainFrame.getPhysicianById(document.getIncident().getPhysicianId());

			// find a valid patient address
			PatientAddress patientAddress = ESanteGuiUtils.getPatientForDsp(dsp).getHomeAddress();
			if (patientAddress == null) {
				patientAddress = dsp.getPatient().getBillingAddress();
			}

			// check patient address on validity
			if (patientAddress == null) {
				errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noPatientAddress")).append("</li>\n");
			} else {
				if (GECAMedUtils.isEmpty(patientAddress.getCountry()))
					errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noPatientAddressCountry")).append("</li>\n");
				if (GECAMedUtils.isEmpty(patientAddress.getZip()))
					errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noPatientAddressZip")).append("</li>\n");
				if (GECAMedUtils.isEmpty(patientAddress.getLocality()))
					errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noPatientAddressLocation")).append("</li>\n");
				if (GECAMedUtils.isEmpty(patientAddress.getStreetName()))
					errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noPatientAddressStreetname")).append("</li>\n");
			}

			// check author on validity
			if (author == null) {
				errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noAuthor")).append("</li>\n");
			} else {
				if (GECAMedUtils.isEmpty(author.getFirstName()))
					errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noAuthorFirstName")).append("</li>\n");
				if (GECAMedUtils.isEmpty(author.getName()))
					errorBuilder.append("<li>").append(Translatrix._("esante.actions.upload.cda.error.noAuthorName")).append("</li>\n");
			}

			if (errorBuilder.length() > 0) {
				logger.warn("Missing data for CDA:\n" + errorBuilder.toString().replace("<li>", "  - ").replace("</li>", ""));
				ESanteDialog.showMessageDialog(MainFrame.getInstance(), Translatrix._("esante.actions.upload.cda.error.title"),
						Translatrix._("esante.actions.upload.cda.error.message", new String[] { errorBuilder.toString() }), ESanteDialog.OK_BUTTON_MODE);
				return false;
			}

			// here the information of the user who requested the saml token have to be provided. This should basically be the user information of the
			// LuxTrust certificate.
			// This information has to be identical with the one stored in the platform LDAP directory.
			// String authorPersonId = StringEscapeUtils.escapeXml(new StringBuilder().append(config.getAuthorId()).append('^').append(config.getLogin())
			// .append('^').append(config.get).append("^^^^^^").append("&1.3.88&ISO").append("^^^^").append("EI").toString());
			// Structure: <eHealthID>^<lastname>^<firstname>^^^^^^<oid>^^^^DN

			//
			// String submissionTime =
			// formatUTC.format(document.getIncident().getCreated());
			String submissionTime = formatUTCTime.format(new Date());
			String effectiveTime = formatUTCTimeWithOffset.format(document.getCreated());
			String creationTime = formatUTCTime.format(document.getCreated());

			// obtain reference to the configuration
			Configuration config = LoginDialog.getConfiguration();

			/************************************
			 * Fill the upload request template *
			 ************************************/

			CdaCode confBlock;
			try {
				if (metaData.isBlocked()) {
					confBlock = CodeFetcher.getCdaCode(CodeFetcher.CONFIDENTIALITY_CODE, CdaDocument.CONFIDENTIALITY_BLOCKED);
				} else {
					// remove the additional block tag
					int start = uploadTemplate.indexOf("CONFIDENTIALITY_BLOCKER_START");
					if (start >= 0) {
						int end = uploadTemplate.indexOf("CONFIDENTIALITY_BLOCKER_END", start);
						if (end > start) {
							uploadTemplate = new StringBuilder(start + uploadTemplate.length() - end).append(uploadTemplate.substring(0, start))
									.append(uploadTemplate.substring(end)).toString();
						}
					}
					confBlock = null;
				}

				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE,
						config.getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_41));
				// first add the saml assertion to the header to authenticate the user at the server
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_SAML_ASSERTION, samlAssertion);
				// set the mime type for the request cda (it's always application/pdf)
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_MIME_TYPE, Attachment.CONTENT_TYPE_XML);
				// indicate the creation time of the document
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CREATION_TIME, creationTime);
				// set the language code in compliance to IETF RFC 3066 (should
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_LANGUAGE, languageCode);
				// set all locations where ids of the patient, the doctor, the practice, or the GECAMed instance occure in OIDs
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_GECAMED_PATIENT_ID, String.valueOf(localPatientId));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_APP_ID, gecamedId);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_USER_EHEALTH_ID_LONG, userEHealthId);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_USER_EHEALTH_ID_SHORT, userEHealthIdShort);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PRACTICE_EHEALTH_ID_LONG, practiceEHealthId);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PRACTICE_EHEALTH_ID_SHORT, practiceEHealthIdShort);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CUSTODIAN, custodian);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_USER_CERT_INFO, certOwnerInfo);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_USER_CERT_FIRSTNAME, certOwnerFirstName);
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_USER_CERT_NAME, certOwnerName);
				
				// Corresponding Tec Doc NOT required but it actually is...
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_FIRST_NAME,
						StringEscapeUtils.escapeXml(GECAMedUtils.stringValue(gecamedPatient.getFirstName(), " ")));
				// Corresponding Tec Doc NOT required but it actually is...
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_SURNAME,
						StringEscapeUtils.escapeXml(GECAMedUtils.stringValue(gecamedPatient.getSurName(), " ")));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_STREET,
						StringEscapeUtils.escapeXml(GECAMedUtils.stringValue(patientAddress.getStreetName() + " " + patientAddress.getStreetNumber(), " ")));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_CITY,
						StringEscapeUtils.escapeXml(GECAMedUtils.stringValue(patientAddress.getLocality(), " ")));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_ZIP_CODE,
						StringEscapeUtils.escapeXml(GECAMedUtils.stringValue(patientAddress.getZip(), " ")));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_COUNTRY,
						StringEscapeUtils.escapeXml(GECAMedUtils.stringValue(patientAddress.getCountry(), " ")));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_MSG_UUID,
						StringEscapeUtils.escapeXml(java.util.UUID.randomUUID().toString()));

				// Corresponding Tec Doc NOT required but it actually is...
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_BIRTHDATE,
						gecamedPatient.getBirthDate() == null ? " " : formatUTCDate.format(gecamedPatient.getBirthDate()));
				// Corresponding Tec Doc NOT required but it actually is...
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PATIENT_GENDER_CODE, patientGenderCodeXds);
				// Document title - no clue why it also has to be added here
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_DOC_TITLE, StringEscapeUtils.escapeXml(metaData.getDocumentName()));
				// not required - but in contrary to the tec doc, the repository
				// requires it
				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHOR_FIRST_NAME,
						StringEscapeUtils.escapeXml(author.getFirstName()));
				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHOR_SURNAME, StringEscapeUtils.escapeXml(author.getName()));

				/**
				 * For all provided codes, it is crucial that the accompanying display name exactly corresponds to the provided code as defined in the
				 * respective table. !! Otherwise the request will be rejected by the repository !!
				 */
				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHOR_ROLE, metaData.getAuthorRole().getCodeId());

				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHOR_SPECIALITY, metaData.getAuthorSpecialty().getCodeId());

				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_PRACTICE_NAME, MainFrame.getCurrentOffice().getName() +  
						(MainFrame.getCurrentSiteId() != null ? " - " +MainFrame.getCurrentSite().getName():"") );
				
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CLASSCODE, metaData.getClassCode().getCodeId());
				// REQUIRED - Classcode narrative - 1.2.250.1.213.1.1.4.1
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CLASSCODE_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getClassCode())));

				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CLASSCODE_OID, metaData.getClassCode().getCodeOid());
				// REQUIRED: A-administrative, N-medical, V-secret,
				// R-stigmatizing,
				// U-utilities (does not use the standard 3 CDA levels) -
				// 2.16.840.1.113883.5.25
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY, metaData.getConfidentialityCode().getCodeId());
				// NOT MENTIONED IN TEC DOCU - 2.16.840.1.113883.5.25
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getConfidentialityCode())));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY_OID, metaData.getConfidentialityCode().getCodeOid());
				if (confBlock != null) {
					uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY_BLOCK_NAME,
							StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(confBlock)));
					uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY_BLOCK_CODE, confBlock.getCodeId());
				}
				// REQUIRED - fixed format code for pdf in level 1 cda format -
				// 1.3.6.1.4.1.19376.1.2.3
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_FORMAT_CODE, metaData.getFormatCode().getCodeId());
				// REQUIRED - fixed display name for pdf in level 1 cda format -
				// 1.3.6.1.4.1.19376.1.2.3
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_FORMAT_CODE_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getFormatCode())));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_FORMAT_CODE_OID, metaData.getFormatCode().getCodeOid());
				// REQUIRED - Code like shown in EHEALTH-SM-I, Annex 2 -
				// 1.2.250.1.71.4.2.4
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_HCF_TYPE, metaData.getHealthcareFacilityCode().getCodeId());
				// REQUIRED - Display name like shown in EHEALTH-SM-I, Annex 2 -
				// 1.2.250.1.71.4.2.4
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_HCF_TYPE_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getHealthcareFacilityCode())));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_HCF_TYPE_OID, metaData.getHealthcareFacilityCode().getCodeOid());
				// REQUIRED - Code like shown in EHEALTH-SM-I, Annex 3 -
				// 1.2.250.1.213.1.1.4.9
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PRACTICE_SETTING, metaData.getPracticeSettingCode().getCodeId());
				// REQUIRED - Display name like shown in EHEALTH-SM-I, Annex 3 -
				// 1.2.250.1.213.1.1.4.9
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PRACTICE_SETTING_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getPracticeSettingCode())));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_PRACTICE_SETTING_OID, metaData.getPracticeSettingCode().getCodeOid());
				// REQUIRED - table "CDA code / XDS.b typeCode" in document
				// "INFOMED Classification of Documents" - 2.16.840.1.113883.6.1
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_TYPE, metaData.getTypeCode().getCodeId());
				// REQUIRED - table "CDA code / XDS.b typeCode" in document
				// "INFOMED Classification of Documents" - 2.16.840.1.113883.6.1
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_TYPE_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getTypeCode())));
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_TYPE_OID, metaData.getTypeCode().getCodeOid());
				/*
				 * Set the patient id as HL7 CX data type (e.g. 6578946^^^&2.16.756.5.30.1.120.10.1&ISO 1. ipid of the patient (Infomed Patient ID - id of the
				 * patient in the eSanté DSP) 2. <not used> 3. <not used> 4. Assigning Authority (eSanté platform)
				 * 
				 * ! This tag occurs 2 times in the template !
				 */
				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_ID, StringEscapeUtils.escapeXml(eSantePID));
				// REQUIRED
				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_DOC_UID, documentUniqueId);
				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_OLD_DOC_OID, metaData.getMetadata().getDocumentOid());
				// REQUIRED -
				uploadTemplate = uploadTemplate.replace(WebserviceConstants.PLACEHOLDER_SUBMISSION_TIME, submissionTime);

				uploadTemplate = uploadTemplate.replaceAll(WebserviceConstants.PLACEHOLDER_ATTACHMENT_CID, StringEscapeUtils.escapeXml("Attachment1"));
			} catch (Exception e2) {
				errorDetails = new StringBuilder(500).append("\nConcerned document: ").append("\"" + metaData.getDocumentName() + "\"")
						.append("\nUpload time: ").append(submissionTime).append("\nConcerned patient: ").append(gecamedPatient.getFirstName()).append(' ')
						.append(gecamedPatient.getSurName()).append("\nUploading physician: ").append(author.getTitle()).append(' ')
						.append(author.getFirstName()).append(' ').append(author.getName()).append("\n\nEncountered errors:\n===================\n")
						.append("Unable to generate CDA document upload request.\n\nException stack trace:\n\n").append(ExceptionUtils.getStackTrace(e2))
						.append("\n\nCurrent state of CDA document metadata:\n\n").append(uploadTemplate).toString();

				// indicate error to the user
				ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(), "Unable to generate CDA document upload request.", errorDetails, localPatientId,
						metaData.getDocumentName());
				// end processing and indicate failure
				return false;
			}

			// Utility.debugSaveContent(uploadTemplate, "SAVED UPLOAD TEMPLATE", "d:/TEMP/", "UploadTemplateSunday.xml");
			/************************************
			 * Fill the PDF attachment template *
			 ************************************/

			try {


				// fill the template fields
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_APP_ID, gecamedId);
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_USER_EHEALTH_ID_LONG, userEHealthId);
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_USER_EHEALTH_ID_SHORT, userEHealthIdShort);
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PRACTICE_EHEALTH_ID_LONG, practiceEHealthId);
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PRACTICE_EHEALTH_ID_SHORT, practiceEHealthIdShort);
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_CUSTODIAN, custodian);
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_DOC_UID, documentUniqueId);
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_USER_CERT_FIRSTNAME, certOwnerFirstName);
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_USER_CERT_NAME, certOwnerName);
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_TYPE, metaData.getTypeCode().getCodeId());
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_TYPE_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getTypeCode())));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_TYPE_OID, metaData.getTypeCode().getCodeOid());
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_TYPE_SYSTEM, (XDS.CustomTypeSystem.equals(metaData.getTypeCode().getCodeOid()))
						? StringEscapeUtils.escapeXml(XDS.typeSystemEsanteDoctype) : XDS.typeSystemLoinc);
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_DOC_TITLE, StringEscapeUtils.escapeXml(metaData.getDocumentName()));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_EFFECTIVE_TIME, effectiveTime);
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY, metaData.getConfidentialityCode().getCodeId());
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY_NAME,
						StringEscapeUtils.escapeXml(metaData.getLocalizedDisplayName(metaData.getConfidentialityCode())));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_CONFIDENTIALITY_OID, metaData.getConfidentialityCode().getCodeOid());
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_LANGUAGE, languageCode);
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_GECAMED_PATIENT_ID, String.valueOf(localPatientId));
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_MATRICULE, dsp.getPatient().getSocialSecurityNumber13Digits(false));
				if (eSantePID.indexOf('^') != -1) {
					// the remaining part is not needed in the cda document
					eSantePID = eSantePID.substring(0, eSantePID.indexOf('^'));
				}
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_ID, StringEscapeUtils.escapeXml(eSantePID));
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_STREET,
						StringEscapeUtils.escapeXml(patientAddress.getStreetName()) + ' ' + patientAddress.getStreetNumber());
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_ZIP_CODE, patientAddress.getZip());
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_CITY, StringEscapeUtils.escapeXml(patientAddress.getLocality()));
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_COUNTRY_CODE,
						SOAPUtilities.getCountryCodeISO3166Alpha3(patientAddress.getCountry()));
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PATIENT_COUNTRY, StringEscapeUtils.escapeXml(patientAddress.getCountry()));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_PATIENT_FIRST_NAME, StringEscapeUtils.escapeXml(gecamedPatient.getFirstName()));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_PATIENT_SURNAME, StringEscapeUtils.escapeXml(gecamedPatient.getSurName()));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_PATIENT_GENDER_CODE, patientGenderCode);
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_PATIENT_GENDER_NAME, StringEscapeUtils.escapeXml(patientGenderName));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_PATIENT_BIRTHDATE,
						gecamedPatient.getBirthDate() == null ? "" : formatUTCDate.format(gecamedPatient.getBirthDate()));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_SUBMISSION_TIME, submissionTime);

				// Creation date has to be obtained from incident as it is missing for the incident entry ==> GECAMed bug
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_CREATED,
						document.getIncident().getCreated() == null ? "" : formatUTCTimeWithOffset.format(document.getIncident().getCreated()));

				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHOR_FIRST_NAME, StringEscapeUtils.escapeXml(author.getFirstName()));
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHOR_SURNAME, StringEscapeUtils.escapeXml(author.getName()));
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_TITLE, StringEscapeUtils.escapeXml(author.getTitle()));
				uploadPdf = uploadPdf.replaceAll(WebserviceConstants.PLACEHOLDER_PRACTICE_NAME, MainFrame.getCurrentOffice().getName() +  
						(MainFrame.getCurrentSiteId() != null ? " - " +MainFrame.getCurrentSite().getName():"") );

				// this is fix for CDA level 1 documents
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_MIME_TYPE, "application/pdf");
				uploadPdf = uploadPdf.replace(WebserviceConstants.PLACEHOLDER_BASE64_CONTENT, pdfBase64);
			} catch (Exception e1) {
				errorDetails = new StringBuilder(500).append("\nConcerned document: ").append("\"" + metaData.getDocumentName() + "\"")
						.append("\nUpload time: ").append(submissionTime).append("\nConcerned patient: ").append(gecamedPatient.getFirstName()).append(' ')
						.append(gecamedPatient.getSurName()).append("\nUploading physician: ").append(author.getTitle()).append(' ')
						.append(author.getFirstName()).append(' ').append(author.getName()).append("\n\nEncountered errors:\n===================\n")
						.append("Unable to generate CDA document meta data.\n\nException stack trace:\n\n").append(ExceptionUtils.getStackTrace(e1))
						.append("\n\nCurrent state of CDA document metadata:\n\n").append(uploadPdf).toString();

				// indicate error to the user
				ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(), "Unable to generate CDA document meta data.", errorDetails, localPatientId,
						metaData.getDocumentName());
				// end processing and indicate failure
				return false;
			}


			/************************
			 * send soap message *
			 ************************/

			try {

				// reset sender object to default values and indicate suffix for message logging
				if (metaData.getMetadata().getDocumentOid() != null) {
					sender.resetSender("ITI-41_REPLACE");
				} else {
					sender.resetSender("ITI-41_UPLOAD");
				}
				
				// it is approved, add it to the data that should be sent
				sender.setAttachment(uploadPdf);
				// this is only needed for a specific message log level
				sender.setAttachmentPayload(data);
				// Utility.debugSaveContent(uploadPdf, "SAVED UPLOAD CDA", "d:/TEMP/", "UploadCDASunday.xml");

				// set the webservice url
				sender.setWebserviceUrl(config.getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_41));
				// check if user aborted the action
				sender.checkThreadStopped();
				// add headers and call the service
				SOAPUtilities.callSOAP(uploadTemplate, sender, true);

				// upload has been successful

				// Create and send ATNA log message
				String t = SOAPUtilities.getTemplateContent(WebserviceConstants.ATNA_ITI_41);

				t = t.replace(WebserviceConstants.PLACEHOLDER_SENDER_IP, InetAddress.getLocalHost().getHostAddress());
				t = t.replace(WebserviceConstants.PLACEHOLDER_PROCESS_ID, WebserviceConstants.ATNA_PROCESS_ID);
				t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE,
						LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_41));
				t = t.replace(WebserviceConstants.PLACEHOLDER_DESTINATION_DNS_NAME, sender.getHostname());
				t = t.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_ID, author.getUcmCode());
				t = t.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_TITLE, author.getTitle());
				t = t.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_FIRST_NAME, author.getFirstName());
				t = t.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_SURNAME, author.getName());
				t = t.replace(WebserviceConstants.PLACEHOLDER_PATIENT_ID, StringEscapeUtils.escapeXml(eSantePID));
				t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_UID, documentUniqueId);

				SOAPUtilities.sendAtnaMessage(t, "EXPORT");

				// set the new cda document uid in the entry metadata
				document.setCdaUniqueId(extractDocumentOid(uploadTemplate));
				// and store a copy of the soap message on server
				ESanteGuiUtils.trace("Documents",
						((isUpdate) ? "Update of incident entry " : "Incident entry ") + document.getId() + " of patient " + dsp.getPatientId()
								+ " successfully uploaded to DSP \"" + dsp.getDspOid() + "\" as CDA document \"" + document.getCdaUniqueId() + "\".\n"
								+ "(copy of transferred message is available under \""
								+ manager.logUploadedMessage(XmlHelper.formatPretty(uploadTemplate), metaData.getDocumentName(), localPatientId) + "\")");

				// and store it in the db
				incidentManager.saveUploadIndicator(document);
				// finally indicate success by return value
				success = true;

				// } else {
				// // response was an error w/o specific information or error codes (most probably some issues with the saml token or the soap envelope)
				// errorDetails = new StringBuilder(500).append("\nConcerned document: ").append("\"" + metaData.getDocumentName() + "\"")
				// .append("\nUpload time: ").append(submissionTime).append("\nConcerned patient: ").append(gecamedPatient.getFirstName()).append(' ')
				// .append(gecamedPatient.getSurName()).append("\nUploading physician: ").append(author.getTitle()).append(' ')
				// .append(author.getFirstName()).append(' ').append(author.getName()).append("\n\nEncountered errors:\n===================\n")
				// .append("Upload request has not been excepted by server - probably saml-token or malformed soap message issue.\n\n")
				// .append(XmlHelper.formatPretty(uploader.getMessage())).toString();
				//
				// ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(),
				// "Unable to upload document to eSanté platform - probably saml-token or malformed soap message issue.", errorDetails, localPatientId,
				// metaData.getDocumentName());
				// }
			} catch (WebserviceException wse) {
				// check if the webservice returned an error
				if (wse.getErrorType() == WebserviceException.TYPE_USER_NOT_AUTHORIZED) {
					ESanteDialog.showMessageDialog(MainFrame.getInstance(), Translatrix.getTranslationString("esante.exceptions.actionNotAuthorized.title"),
							Translatrix.getTranslationString("esante.exceptions.actionNotAuthorized.message"), ESanteDialog.OK_BUTTON_MODE,
							GECAMedModule.getScaledIcon(GECAMedIconNames.ERROR, 32));

				} else if (wse.getErrorType() == WebserviceException.TYPE_DENIED_BY_RIGHT_SERVICE) {
					ESanteDialog.showMessageDialog(MainFrame.getInstance(), Translatrix.getTranslationString("esante.exceptions.noRightsForDocumentType.title"),
							Translatrix.getTranslationString("esante.exceptions.noRightsForDocumentType.message",
									new String[] { metaData.getClassCode().getDescription().getDisplayName() + "/"
											+ metaData.getTypeCode().getDescription().getDisplayName() }),
							ESanteDialog.OK_BUTTON_MODE, GECAMedModule.getScaledIcon(GECAMedIconNames.ERROR, 32));
				} else {
					// response was an error but the server thankfully provided some error codes create the error indication string for the log
					// message, the detail display message, and also the error file that will be stored at the server
					StringBuilder errorMsdBuilder = new StringBuilder(500).append("\nConcerned document: ").append("\"" + metaData.getDocumentName() + "\"")
							.append("\nUpload time: ").append(submissionTime).append("\nConcerned patient: ").append(gecamedPatient.getFirstName()).append(' ')
							.append(gecamedPatient.getSurName()).append("\nUploading physician: ").append(author.getTitle()).append(' ')
							.append(author.getFirstName()).append(' ').append(author.getName()).append("\n\nEncountered error:\n===================\n")
							.append("Error Code:\t").append(sender.getErrorCode()).append("\nError Message:\t").append(sender.getErrorMessage())
							.append("\nError details:\t").append(sender.getErrorDetail()).append("\n").append(XmlHelper.formatPretty(uploadTemplate));

					errorDetails = errorMsdBuilder.toString();
					// indicate error to the user
					ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(),
							"Unable to upload document \"" + metaData.getDocumentName()
									+ "\" to eSanté platform.\n\nThe server returned errors. Please view details for further information.",
							errorDetails, localPatientId, metaData.getDocumentName());
				}
			} catch (Exception e) {
				errorDetails = new StringBuilder(500).append("\nConcerned document: ").append("\"" + metaData.getDocumentName() + "\"")
						.append("\nUpload time: ").append(submissionTime).append("\nConcerned patient: ").append(gecamedPatient.getFirstName()).append(' ')
						.append(gecamedPatient.getSurName()).append("\nUploading physician: ").append(author.getTitle()).append(' ')
						.append(author.getFirstName()).append(' ').append(author.getName()).append("\n\nEncountered errors:\n===================\n")
						.append("Upload generated the following exception:\n").append(ExceptionUtils.getStackTrace(e)).append("\n\n").toString();
				if ((uploadTemplate != null) && (uploadTemplate.length() > 0)) {
					// if the message is available add it to the error report
					errorDetails += XmlHelper.formatPretty(uploadTemplate);

				}
				ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(), "Unable to upload document to eSanté platform.", errorDetails, localPatientId,
						metaData.getDocumentName());
			}

		} catch (Exception e) {
			if (e instanceof WebserviceException && ((WebserviceException) e).getErrorType() == WebserviceException.TYPE_INVALID_LOGIN_DATA) {
				// indicate error to the user and allow the user to enter correct authentication information
				LoginDialog.incorrectLoginData();
			}
			// if code execution reached this part, the preparation of the
			// request data went wrong
			errorDetails = new StringBuilder(500)
					.append("An error at preparing the data for creating an eSanté document upload request occurred.\n\nException stack trace:\n\n")
					.append(ExceptionUtils.getStackTrace(e)).toString();

			// indicate error to the user
			ErrorDialog.showESanteErrorDialog(MainFrame.getInstance(), "Unable to generate CDA document upload request.", errorDetails, localPatientId,
					"UploadRequestDataPreparationError");
		}

		return success;
	}

	/* ======================================== */
	// HELP METHODS
	/* ======================================== */

	/**
	 * Request a list of metadata of all available documents of a specific patient from the eSanté DSP
	 * 
	 * @param dsp
	 *            The metadata of a patient who has a DSP at the eSanté platform (basically only the local GECAMed patient id and the eSanté patient id are
	 *            needed)
	 * @param config
	 *            Metadata for accessing the eSanté DSP
	 * @return A list containing the metadata of all documents that are assigned to the patient at the eSanté DSP and that are accessible by the requesting
	 *         physician
	 * @throws Exception
	 *             Check error stack for further details
	 */
	private static Collection<CdaDocument> callITI18FindDocuments(SoapSender sender) throws Exception {
		// CDAManager manager = CDAManagerBean.getInstance();
		Dsp dsp = sender.getDsp();

		if (!dsp.isPersistent())
			throw new RuntimeException("The DSP is not yet saved. It has to be persistent, in order to retrieve documents!");

		// get template
		String t = SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_ITI_18_FIND_DOCUMENTS);

		// **** replace the needed variable
		t = t.replace(WebserviceConstants.PLACEHOLDER_SAML_ASSERTION, Security.getDspSAMLAssertion(sender, false));

		// add the consuming service to the template
		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));

		// msg uuid
		t = t.replace(WebserviceConstants.PLACEHOLDER_MSG_UUID, StringEscapeUtils.escapeXml(java.util.UUID.randomUUID().toString()));

		// patient id
		t = t.replace(WebserviceConstants.PLACEHOLDER_PATIENT_ID, StringEscapeUtils.escapeXml(dsp.getDspOid()));

		// time from
		// Date lastDate = manager.lastUpdated(dsp.getId());
		Date querySinceDate = sender.getQuerySince(); // querySinceDate = new GregorianCalendar(2013, 1, 1).getTime();
		// document age shall not be limited if a specific document is queried by its id
		if ((querySinceDate == null) || (sender.getDocumentUniqueId() != null)) {
			// remove the date tag
			int start = t.indexOf("<rim:Slot name=\"$XDSDocumentEntryCreationTimeFrom\">");
			int end = t.indexOf("</rim:Slot>", start) + 11;
			t = t.substring(0, start) + t.substring(end);
		} else {
			t = t.replace(WebserviceConstants.PLACEHOLDER_TIME_FROM, StringEscapeUtils.escapeXml(formatUTCDate.format(querySinceDate)));
		}

		// If the appropriate parameter has been set, only request the metadata of a specific document
		// if (sender.getDocumentUniqueId() == null) {
		// // remove the unique id tag
		// int start = t.indexOf("<rim:Slot name=\"$XDSDocumentEntryUniqueId\">");
		// int end = t.indexOf("</rim:Slot>", start) + 11;
		// t = t.substring(0, start) + t.substring(end);
		// } else {
		// t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_UID, sender.getDocumentUniqueId());
		// }

		// remember the actual query for the ATNA log message
		int start = t.indexOf("<query:AdhocQueryRequest");
		int end = t.indexOf("</query:AdhocQueryRequest>", start) + "</query:AdhocQueryRequest>".length();
		// atna log requires the query to be base64 encoded
		String queryDataB64 = Security.encodeBase64(t.substring(0, start) + t.substring(end));

		// **** call the WS
		sender.setWebserviceUrl(LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));
		sender.resetSender("ITI-18");
		SOAPUtilities.callSOAP(t, sender);

		// ****** Create and send ATNA message
		t = SOAPUtilities.getTemplateContent(WebserviceConstants.ATNA_ITI_18);

		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));
		t = t.replace(WebserviceConstants.PLACEHOLDER_DESTINATION_DNS_NAME, sender.getHostname());
		t = t.replace(WebserviceConstants.PLACEHOLDER_PROCESS_ID, WebserviceConstants.ATNA_PROCESS_ID);
		t = t.replace(WebserviceConstants.PLACEHOLDER_SENDER_IP, InetAddress.getLocalHost().getHostAddress());
		t = t.replace(WebserviceConstants.PLACEHOLDER_QUERY_BASE64, queryDataB64);

		SOAPUtilities.sendAtnaMessage(t, "QUERY_FindDocuments");

		// import the documents from the list retrieved from the webservice
		ConnectingDialog importerDialog = ConnectingDialog.getImportCdaDialog();
		Importer importer = new Importer(sender, importerDialog);
		importer.start();
		if (importerDialog.showCenteredDialog() != ConnectingDialog.OK_OPTION) {
			try {
				// wait and give the importer a chance to stop itself
				for (int i = 0; i < 4 && importer.isAlive(); i++)
					Thread.sleep(500);
			} catch (InterruptedException e) {
				// simply go on
			}

			if (importer.isAlive())
				importer.interrupt();
		}

		// return the newly stored documents to add them to the list of documents
		return importer.getDocuments();
	}

	/**
	 * Provides the metadata of the latest vesion of the provided document
	 * 
	 * @param sender
	 *            The sender object containing the information needed to generate the request to the webservice
	 * @return The meta data of the latest version of the provided document
	 * @throws Exception
	 *             Check error stack for further details
	 */
	public static CdaDocument retrieveLatestVersionMetaData(SoapSender sender) throws Exception {
		// CDAManager manager = CDAManagerBean.getInstance();
		Dsp dsp = sender.getDsp();

		if (!dsp.isPersistent())
			throw new RuntimeException("The DSP is not yet saved. It has to be persistent, in order to retrieve documents!");

		// get template
		String t = SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_ITI_18_GET_RELATED_DOCUMENTS);

		// **** replace the needed variable
		t = t.replace(WebserviceConstants.PLACEHOLDER_SAML_ASSERTION, Security.getDspSAMLAssertion(sender, false));

		// add the consuming service to the template
		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));

		// msg uuid
		t = t.replace(WebserviceConstants.PLACEHOLDER_MSG_UUID, StringEscapeUtils.escapeXml(java.util.UUID.randomUUID().toString()));

		// patient id
		t = t.replace(WebserviceConstants.PLACEHOLDER_PATIENT_ID, StringEscapeUtils.escapeXml(dsp.getDspOid()));

		// the id of the document of which the related documents should be found
		t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_UID, sender.getDocumentUniqueId());

		// remember the actual query for the ATNA log message
		int start = t.indexOf("<query:AdhocQueryRequest");
		int end = t.indexOf("</query:AdhocQueryRequest>", start) + "</query:AdhocQueryRequest>".length();
		// atna log requires the query to be base64 encoded
		String queryDataB64 = Security.encodeBase64(t.substring(0, start) + t.substring(end));

		// **** call the WS
		sender.setWebserviceUrl(LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));
		sender.resetSender("ITI-18_GetRelatedDocuments");
		SOAPUtilities.callSOAP(t, sender);

		// ****** Create and send ATNA message
		t = SOAPUtilities.getTemplateContent(WebserviceConstants.ATNA_ITI_18);

		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));
		t = t.replace(WebserviceConstants.PLACEHOLDER_DESTINATION_DNS_NAME, sender.getHostname());
		t = t.replace(WebserviceConstants.PLACEHOLDER_PROCESS_ID, WebserviceConstants.ATNA_PROCESS_ID);
		t = t.replace(WebserviceConstants.PLACEHOLDER_SENDER_IP, InetAddress.getLocalHost().getHostAddress());
		t = t.replace(WebserviceConstants.PLACEHOLDER_QUERY_BASE64, queryDataB64);

		SOAPUtilities.sendAtnaMessage(t, "QUERY_GetRelatedDocuments");

		CdaDocument cda = null;
		Document response;
		Node latestVersion;
		try {
			// first get the response of the webservice
			response = SOAPUtilities.createDocument(sender.getResponseMessage());
			// then identify the node representing the latest version of the document (the one with status 'approved')
			latestVersion = XPathAPI.selectSingleNode(response,
					"//*[local-name()='ExtrinsicObject' and @*[local-name()='status']='urn:oasis:names:tc:ebxml-regrep:StatusType:Approved']", false);

			// if a node was found, a newer vesion is available
			if (latestVersion != null) {
				// and parse the node data into the new cda document
				cda = ESanteGuiUtils.createCdaDocument(latestVersion, dsp);
			} else {
				// load cda from local database
				cda = retrieveDocumentMetaData(sender);
			}
		} catch (SendingStoppedException e) {
			return null;
		} catch (Exception e) {
			logger.error("", e);
			return null;
		}

		// System.out.println("GET LATEST VERSION: \nAN: "+cda.getAuthorName()+"\nDT: "+cda.getTitle()+"\nLC: "+cda.getLanguageCode()+"\nTC:
		// "+cda.getTypeCode()+"\nCC: "+cda.getClassCode()+"\nCoC: "+cda.getConfidentialityCode()+"\nHCFTC: "+cda.getHcfTypeCode()+"\nPSC:
		// "+cda.getPracticeSettingCode()+"\nAR: "+cda.getAuthorRole()+"\nAS: "+cda.getAuthorSpecialty());

		return cda;
	}

	/**
	 * Provides the metadata of the document with the provided id
	 * 
	 * @param sender
	 *            The sender object containing the information needed to generate the request to the webservice.<br/>
	 *            <br/>
	 *            <i>(the id of the required document is set in {@link SoapSender#setDocumentUniqueId(String)}})</i>
	 * @return The meta data of the document with the provided id
	 * @throws Exception
	 *             Check error stack for further details
	 */
	public static CdaDocument retrieveDocumentMetaData(SoapSender sender) throws Exception {
		// CDAManager manager = CDAManagerBean.getInstance();
		Dsp dsp = sender.getDsp();

		if (!dsp.isPersistent())
			throw new RuntimeException("The DSP is not yet saved. It has to be persistent, in order to retrieve documents!");

		// get template
		String t = SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_ITI_18_GET_DOCUMENTS);

		// **** replace the needed variable
		t = t.replace(WebserviceConstants.PLACEHOLDER_SAML_ASSERTION, Security.getDspSAMLAssertion(sender, false));

		// add the consuming service to the template
		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));

		// msg uuid
		t = t.replace(WebserviceConstants.PLACEHOLDER_MSG_UUID, StringEscapeUtils.escapeXml(java.util.UUID.randomUUID().toString()));

		// the id of the document of which the related documents should be found
		t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_UID, sender.getDocumentUniqueId());

		// remember the actual query for the ATNA log message
		int start = t.indexOf("<query:AdhocQueryRequest");
		int end = t.indexOf("</query:AdhocQueryRequest>", start) + "</query:AdhocQueryRequest>".length();
		// atna log requires the query to be base64 encoded
		String queryDataB64 = Security.encodeBase64(t.substring(0, start) + t.substring(end));

		// **** call the WS
		sender.setWebserviceUrl(LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));
		sender.resetSender("ITI-18_GetDocuments");
		SOAPUtilities.callSOAP(t, sender);

		// ****** Create and send ATNA message
		t = SOAPUtilities.getTemplateContent(WebserviceConstants.ATNA_ITI_18);

		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_18));
		t = t.replace(WebserviceConstants.PLACEHOLDER_DESTINATION_DNS_NAME, sender.getHostname());
		t = t.replace(WebserviceConstants.PLACEHOLDER_PROCESS_ID, WebserviceConstants.ATNA_PROCESS_ID);
		t = t.replace(WebserviceConstants.PLACEHOLDER_SENDER_IP, InetAddress.getLocalHost().getHostAddress());
		t = t.replace(WebserviceConstants.PLACEHOLDER_QUERY_BASE64, queryDataB64);

		SOAPUtilities.sendAtnaMessage(t, "QUERY_GetDocuments");

		CdaDocument cda = null;
		Document response;
		Node documentInfo;
		try {
			// first get the response of the webservice
			response = SOAPUtilities.createDocument(sender.getResponseMessage());
			// then identify the node representing the latest version of the document (the one with status 'approved')
			documentInfo = XPathAPI.selectSingleNode(response,
					"//*[local-name()='ExtrinsicObject' and @*[local-name()='status']='urn:oasis:names:tc:ebxml-regrep:StatusType:Approved']", false);

			// if a node was found, a newer vesion is available
			if (documentInfo != null) {
				// and parse the node data into the new cda document
				cda = ESanteGuiUtils.createCdaDocument(documentInfo, dsp);
			} else {
				throw new Exception("Document with id " + sender.getDocumentUniqueId() + " is not available at the dsp.");
			}
		} catch (SendingStoppedException e) {
			return null;
		} catch (Exception e) {
			logger.error("", e);
			return null;
		}

		// System.out.println("CURRENT DOC METADATA: \nAN: "+cda.getAuthorName()+"\nDT: "+cda.getTitle()+"\nLC: "+cda.getLanguageCode()+"\nTC:
		// "+cda.getTypeCode()+"\nCC: "+cda.getClassCode()+"\nCoC: "+cda.getConfidentialityCode()+"\nHCFTC: "+cda.getHcfTypeCode()+"\nPSC:
		// "+cda.getPracticeSettingCode()+"\nAR: "+cda.getAuthorRole()+"\nAS: "+cda.getAuthorSpecialty());

		return cda;
	}

	static private String nodeToString(Node node) {
		StringWriter sw = new StringWriter();
		try {
			Transformer t = TransformerFactory.newInstance().newTransformer();
			t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			t.transform(new DOMSource(node), new StreamResult(sw));
		} catch (TransformerException te) {
			logger.error("nodeToString Transformer Exception");
		}
		return sw.toString();
	}

	/**
	 * Downloads a specific document from the eSanté DSP of a patient
	 * 
	 * @param patientESanteId
	 *            The unique id of the DSP from which the document should be requested
	 * @param document
	 *            The metadata of the document that should be requested from the eSanté DSP
	 * @param config
	 *            Metadata for accessing the eSanté DSP
	 * @return The base64 encoded document
	 * @throws Exception
	 *             Check exception stack for further details
	 */
	private static byte[] callITI43RetrieveDocumentSet(SoapSender sender, CdaDocument document) throws Exception {
		String t = SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_ITI_43);
		String ownerProps = getCertOwnerProperties(Security.getUserSamlAssertion(sender, false));
		String authorProps = document.getAuthor();

		sender.checkThreadStopped();

		if (ownerProps != null && authorProps != null) {
			String ownerId = ownerProps.substring(0, ownerProps.indexOf("^"));
			String authorId = authorProps.substring(0, authorProps.indexOf("^"));

			if (!GECAMedUtils.isEmpty(ownerId) && !GECAMedUtils.isEmpty(authorId) && ownerId.trim().equals(authorId.trim()))
				sender.setPresencePasswordRequired(false);
		}

		// **** replace the needed variable
		t = t.replace(WebserviceConstants.PLACEHOLDER_SAML_ASSERTION, Security.getDspSAMLAssertion(sender, false));
		// add the consumer service
		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_43));
		// msg uuid
		t = t.replace(WebserviceConstants.PLACEHOLDER_MSG_UUID, StringEscapeUtils.escapeXml(UUID.randomUUID().toString()));
		// repository id
		t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_REPOSITORY_UID, StringEscapeUtils.escapeXml(document.getRepositoryUniqueId()));
		// doc id
		t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_UID, StringEscapeUtils.escapeXml(document.getDocumentUniqueId()));
		// if there is a home community id add it to the request as it is a cross platform access
		t = SOAPUtilities.replaceInRequestTemplate(t, WebserviceConstants.PLACEHOLDER_HOME_COMMUNITY_ID,
				(document.getHomeCommunity() != null) ? WebserviceConstants.PH_TEMP_HOME_COMMUNITY_ID : null, document.getHomeCommunity());

		// **** call the WS
		Configuration config = LoginDialog.getConfiguration();
		sender.setWebserviceUrl(config.getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_43));
		sender.resetSender("ITI-43");
		sender.checkThreadStopped();
		// send it as multipart mime message even if it is no multipart. However specification requires that if one communication partner sends multipart, the
		// other has to do the same
		SOAPUtilities.callSOAP(t, sender, true);

		// ****** Create and send ATNA message
		t = SOAPUtilities.getTemplateContent(WebserviceConstants.ATNA_ITI_43);

		t = t.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, LoginDialog.getConfiguration().getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_43));
		t = t.replace(WebserviceConstants.PLACEHOLDER_SENDER_IP, InetAddress.getLocalHost().getHostAddress());
		t = t.replace(WebserviceConstants.PLACEHOLDER_PROCESS_ID, WebserviceConstants.ATNA_PROCESS_ID);
		t = t.replace(WebserviceConstants.PLACEHOLDER_DESTINATION_DNS_NAME, sender.getHostname());
		t = t.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_ID, "123456-78");
		t = t.replace(WebserviceConstants.PLACEHOLDER_PATIENT_ID, StringEscapeUtils.escapeXml(sender.getDsp().getDspOid()));
		t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_UID, StringEscapeUtils.escapeXml(document.getDocumentUniqueId()));
		t = t.replace(WebserviceConstants.PLACEHOLDER_DOC_REPOSITORY_UID, Security.encodeBase64(document.getRepositoryUniqueId()));
		if (document.getHomeCommunity() != null) {
			// if there is a home community id add it to the request as it is a cross platform access
			t = SOAPUtilities.replaceInRequestTemplate(t, WebserviceConstants.PLACEHOLDER_HOME_COMMUNITY_ID, WebserviceConstants.PH_TEMP_ATNA_HOME_COMMUNITY_ID,
					Security.encodeBase64(document.getHomeCommunity()));
		} else {
			// no home community had been provided, so this tag is not needed
			t = t.replace(WebserviceConstants.PLACEHOLDER_HOME_COMMUNITY_ID, "");
		}

		SOAPUtilities.sendAtnaMessage(t, "IMPORT");

		// ******extract MTOM
		// String MTOM_href = (String) ESanteUtils.parseXPath("//*[local-name()='Include']/@href", sender.getResponseMessage(), true);
		// MTOM_href = MTOM_href.substring("cid:".length()); // remove starting
		// // cid:
		// URLCodec c = new URLCodec();
		// MTOM_href = c.decode(MTOM_href);

		return sender.getAttachment();
	}

	private static class Importer extends Thread {
		private SoapSender sender;
		private ConnectingDialog dialog;
		private Set<CdaDocument> documents = new TreeSet<CdaDocument>();;

		public Importer(SoapSender sender, ConnectingDialog dialog) {
			this.sender = sender;
			this.dialog = dialog;
		}

		public Set<CdaDocument> getDocuments() {
			return documents;
		}

		@Override
		public void run() {
			try {
				Node header;
				CdaDocument cdaDoc;
				CdaDocument cdaUpdate;
				Dsp dsp = sender.getDsp();
				Date downloadTime = new GregorianCalendar(TimeZone.getTimeZone("UTC")).getTime();

				// check the response and create the CDADocuments from the node (without
				// context)
				Document response;
				NodeList cdaHeaders;
				try {
					response = SOAPUtilities.createDocument(sender.getResponseMessage());
					cdaHeaders = XPathAPI.selectNodeIterator(response, "//*[local-name()='ExtrinsicObject']", false);
				} catch (SendingStoppedException e) {
					return;
				} catch (Exception e) {
					logger.error("", e);
					return;
				}

				documents.clear();
				for (int i = 0; i < cdaHeaders.getLength(); i++) {
					if (sender.isStopped())
						return;

					try {
						header = cdaHeaders.item(i);
						cdaDoc = ESanteGuiUtils.createCdaDocument(header, dsp);
						cdaDoc.setDownloadTime(downloadTime);
						// cdaDoc.setDspId(dsp.getId());
						// cdaDoc.readMetaData();
						cdaUpdate = manager.saveCdaDocument(cdaDoc);
						cdaUpdate.updateNonTransient(cdaDoc);
						cdaDoc.setClientFile(DocumentWorker.getLocaleCachedCdaFile(cdaDoc));
						if (cdaDoc.getClientFile() != null)
							CdaUtils.getContent(cdaDoc);

						// document already existed in the database and wasn't added
						documents.add(cdaDoc);
					} catch (WebserviceException e) {
						logger.log(Level.WARN, e.getMessage());
					} catch (Exception e) {
						logger.error("Error while creating CDA document out of eSante XML and storing it into database.", e);
					}
				}
			} finally {
				EventQueue.invokeLater(new Runnable() {
					public void run() {
						dialog.okActionCalled();
					}
				});
			}
		}
	}
}
