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

import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
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.ESanteProperty;
import lu.tudor.santec.gecamed.esante.ejb.session.beans.ESanteConfigManagerBean;
import lu.tudor.santec.gecamed.esante.ejb.session.interfaces.ESanteConfigManager;
import lu.tudor.santec.gecamed.esante.gui.IconNames;
import lu.tudor.santec.gecamed.esante.gui.utils.CdaCodeComboBoxRenderer;
import lu.tudor.santec.gecamed.esante.gui.utils.CodeFetcher;
import lu.tudor.santec.gecamed.esante.gui.utils.ESanteGuiUtils;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.IncidentEntry;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.IncidentManager;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Logger;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * 
 * @author donak
 * 
 * @version <br>
 *          $Log: ESanteUploadDialog.java,v $ Revision 1.28 2014-02-05 10:02:16 ferring Uploaded documents can now be blocked and normal, restricted or private
 *
 *          Revision 1.27 2014-01-28 17:53:10 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.26 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.25 2013-12-31 17:07:38 donak Fixed bug of eSanté Connection Kit specification Added mechanism to request mandate before final saml
 *          assertion is requested. DOES NOT WORK COMPLETELY, YET.
 *
 *          Revision 1.24 2013-12-10 17:27:58 donak extended capability of function for getting localized display name (multi-language is there but still
 *          disabled due to eSanté limitations) Fixed bug in classCode table
 *
 *          Revision 1.23 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.22 2013-12-05 18:44:04 donak Partial upload support for the eSanté platform, not yet functional
 *
 *          Revision 1.21 2013-12-05 15:19:25 donak Partial upload support for the eSanté platform, not yet functional
 *
 *          Revision 1.20 2013-11-26 08:15:44 ferring Property names changed
 *
 *          Revision 1.19 2013-11-19 09:54:18 donak Changed data in default title creation for prescription upload from current date to the date of the actual
 *          creation of the prescription
 *
 *          Revision 1.18 2013-11-14 18:17:17 donak Fixed versioning issue between JODConverter (Java 1.6) and JBOSS (Java 1.5). Server-side PDF ==> PDF/A
 *          conversion should work now. Fixed IncidentEntry getBinaryContent(). Now a call to this functions always provides the binary content of an incident
 *          entry, if any. function works on client and server side. Implemented function to update incident entry upload status w/o updating all bean
 *          properties. Fixed issue with retrieving default user inactivity logout delay from database
 *
 *          Revision 1.17 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.16 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.15 2013-10-23 07:32:32 ferring icon names changed to constants
 *
 *          Revision 1.14 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.13 2013-10-16 14:37:03 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.12 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.11 2013-10-11 14:59:09 ferring *** empty log message ***
 *
 *          Revision 1.10 2013-10-09 16:33:06 donak Added content validity checking Revision 1.8 2013-10-08 12:46:48 donak eSant� upload dialog and logging
 * 
 *          Revision 1.7 2013-10-08 08:57:36 ferring commit comments utf8 correction
 * 
 *          Revision 1.6 2013-10-07 16:12:12 donak Logging, message backup, and error protocol creation Revision 1.5 2013-10-04 17:22:43 donak eSanté upload
 *          dialog and integration of selected values Revision 1.4 2013-10-04 12:19:13 donak eSanté integration CDA document upload metadata configuration
 *          dialog - there is a problem w/ the entity beans (CdaCodeCategory is not found), which needs to be addressed
 * 
 *          Revision 1.3 2013-10-03 16:32:37 donak eSanté integration CDA document upload metadata configuration dialog - there is a problem w/ the entity beans
 *          (CdaCodeCategory is not found), which needs to be addressed Revision 1.2 2013-10-03 12:22:54 donak eSanté integration CDA document upload metadata
 *          configuration dialog
 * 
 * 
 */
public class ESanteUploadDialog extends GECAMedBaseDialogImpl implements CaretListener, ItemListener {

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

	private static final long serialVersionUID = 5603949255110390535L;

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

	private final static String metadataHcFacilityTypeDefault = "F-010";
	private final static String metadataExpertiseAreaDefault = "FP-000";
	private final static String formatCodeDefault = "urn:ihe:iti:xds-sd:pdf:2008";

	private static ESanteConfigManager eManager = null;
	private final int userId = GECAMedModule.getCurrentUser().getId();

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

	private MetaDataComboBox classCodeCB;
	private JTextField documentNameField;
	private MetaDataComboBox confidentialityCodeCB;
	// private MetaDataComboBox formatCodeCB;
	private MetaDataComboBox languageCodeCB;
	private MetaDataComboBox practiceSettingCodeCB;
	private MetaDataComboBox healthcareFacilityCodeCB;
	private MetaDataComboBox typeCodeCB;
	private MetaDataComboBox authorRoleCB;
	private MetaDataComboBox authorSpecialtyCB;
	private JCheckBox confidentialityBlockedCB;
	private CdaDocument metadata = null;
	private IncidentEntry incidentEntry = null;
	private CellConstraints cc = new CellConstraints();
	private String authorId = null;
	
	/** indicates if the current operation is an initial upload or a document update */
	private boolean isUpdate = false;

	private static CdaCode formatCode = null;

	private static Map<Integer, String> codeCache = null;

	/* ======================================== */
	// CONSTRUCTORS
	/* ======================================== */

	
	/**
	 * Initializes the dialog<br/>
	 * <br/>
	 * <i><b>This constructor should be used for uploading a document the first time to the dsp</i></b>
	 * 
	 * @throws Exception
	 *             Check stack trace for details
	 */
	public ESanteUploadDialog(IncidentEntry incidentEntry) throws Exception {
		super(MainFrame.getInstance(), Translatrix.getTranslationString("esante.actions.upload.dialog.title"), OK_CANCEL_BUTTON_MODE);
		eManager = ESanteConfigManagerBean.getInstance();

		// store the incident entry
		setIncidentEntry(incidentEntry);
		// load preset from db
		setMetadata(loadLastMetadataFromDb());
		createDialog();
	}
	
	/**
	 * Provides the unique identifier of the person that logged into the esanté platform
	 * 
	 * @return The unique id of the author (=the person that exchanges documents with the platform) or null if no id has been found
	 */
	private String getAuthorId() {
		if (this.authorId == null) {
			try {
				// The author id is either the serial number of the used smartcard or the id provided at login/password authentication
				this.authorId = LoginDialog.getConfiguration().getAuthorId();
			} catch (Exception e) {
			}
		}

		return this.authorId;
	}

	
	/**
	 * Initializes the dialog
	 * 
	 * @param cdaUniqueId
	 *            The unique id of a document that has already been uploaded to the DSP<br/>
	 * <br/>
	 *            <i><b>This constructor should be used for uploading a new version of a document or updating the metadata of a document of which the latest
	 *            version is <u>locally</u> available</i></b>
	 * @throws Exception
	 *             Check stack trace for details
	 */
//	public ESanteUploadDialog(String cdaUniqueId) throws Exception {
//		super(MainFrame.getInstance(), Translatrix.getTranslationString("esante.actions.upload.dialog.title"), OK_CANCEL_BUTTON_MODE);
//		eManager = ESanteConfigManagerBean.getInstance();
//
//		Patient patient = GECAMedModule.getCurrentPatient();
//
//		// 1. try to retrieve the metadata of the document from the latest version on the platform
//		setMetadata((XDS.retrieveDocumentMetaData(cdaUniqueId, CDAManagerBean.getInstance().getLinkedDsp(patient.getId()))));
//		
//		if(getMetadata()== null){
//		// There is no later version available:
//		// 2. No try to locally load it from the local db
//		setMetadata((CdaDocument) em.createNamedQuery(CdaDocument.GET_DOCUMENT_BY_OID)
//				.setParameter("oid", cdaUniqueId)
//				.setParameter("dspId", cda.getDspId())
//				.getSingleResult());
//		}
//		
//		// there is also no local file available. This means this file has obviously never been uploaded before or the unique id is incorrect, thus
//		// 3. use the value, the user has been using last time he uploaded a document to the dsp
//		if (getMetadata() == null) {
//			logger.warn("There was no document with uid " + cdaUniqueId + " found at the DSP for patient " + patient.getFirstName() + " "
//					+ patient.getSurName() + " (gecamed id: " + patient.getId() + ", DSP id: " + patient.getIdLuxembourg()
//					+ ").\nThus the metadata of the last upload will be used as preset.");
//			// load preset from db
//			setMetadata(loadLastMetadataFromDb());
//		}
//		createDialog();
//	}

	
	/**
	 * Initializes the dialog
	 * 
	 * @param document
	 *            The metadata of a document from the DSP. It does not have to be locally available.<br/>
	 * <br/>
	 *            <i><b>This constructor should be used for uploading a new version of a document or updating the metadata of a document of which the latest
	 *            version is <u>not</u> locally available</i></b>
	 * @throws Exception
	 *             Check stack trace for details
	 */
	public ESanteUploadDialog(IncidentEntry incidentEntry, CdaDocument document) throws Exception {
		super(MainFrame.getInstance(), Translatrix.getTranslationString("esante.actions.upload.dialog.title"), OK_CANCEL_BUTTON_MODE);
		eManager = ESanteConfigManagerBean.getInstance();

		setIncidentEntry(incidentEntry);
		setMetadata(document);
		setUpdate(true);
		CdaDocument cda = getMetadata();
		// System.out.println("INIT: \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());

		createDialog();
	}
	
	/**
	 * Reads the last used attribute values from the database and creates a document metadata object from it. The document title will automatically be generated based on the document type and content.
	 * 
	 * @return The metadata object containing the attribute values
	 */
	private CdaDocument loadLastMetadataFromDb() {

		CdaDocument doc = new CdaDocument();
		String authorId = getAuthorId();
		IncidentEntry entry = getIncidentEntry();

		
		// read the last values configured by the user from db
		doc.setLanguageCode(eManager.getESanteUserProperty(ESanteProperty.PROP_METADATA_LANGUAGE, authorId));
		// explicitly requested by the eSanté agency as there are no means to change the confidentiality code of a document from GECAMed side after it has
		// been committed accidentally with the wrong (last used) code
		doc.setConfidentialityStatus("N", false);
		// String metadataConfidentiality = eManager.getESanteUserProperty(ESanteProperty.PROP_METADATA_CONFIDENTIALITY, this.authorId);
		doc.setHcfTypeCode(eManager.getESanteUserProperty(ESanteProperty.PROP_METADATA_HC_FACILITY_TYPE, authorId));
		doc.setPracticeSettingCode(eManager.getESanteUserProperty(ESanteProperty.PROP_METADATA_EXPERTISE_AREA, authorId));
		doc.setAuthorRole(eManager.getESanteUserProperty(ESanteProperty.PROP_METADATA_AUTHOR_ROLE, authorId));
		doc.setAuthorSpecialty(eManager.getESanteUserProperty(ESanteProperty.PROP_METADATA_AUTHOR_SPECIALTY, authorId));
		
		// generate a default document title depending on the document type
		if (IncidentManager.PRESCRIPTION.equals(entry.getEntryType().getName())) {
			// create a generic title for the prescription
			doc.setTitle(String.format(Translatrix.getTranslationString("esante.actions.upload.dialog.label.prescriptionTitle"), entry.getCreated()));
		} else if (IncidentManager.LETTER.equals(entry.getEntryType().getName()) || IncidentManager.FILE.equals(entry.getEntryType().getName())) {
			String title = entry.getTextContent().replaceAll("\n", " ");

			// restrict the length
			if (title.length() > 40) {
				title = title.substring(0, 40) + "...";
			}
			doc.setTitle(title);
		}
		return doc;
	}
	
	
	/**
	 * Initializes the dialog
	 * 
	 * @param document
	 *            The document for which the CDA metadata should be specified
	 * @throws Exception
	 *             Check stack trace for details
	 */
	public void createDialog() throws Exception {

		if(getMetadata()==null){
			logger.error("Unable to determine document metadata. The metadata dialog will not be displayed.");
			return;
		}

		setResizingOptions(RESIZING_NONE);

		mainPanel.setLayout(new FormLayout(
		// column definition
				"15px, f:p, 15px, f:p, 10px, f:400px, 15px:g",
				// row definition
				"15px,f:p," + // document class
						" 5px,f:p," + // document name
						" 5px,f:p," + // document language
						" 5px,f:p," + // document class
						" 5px,f:p," + // document type
						// " 5px,f:p," + // document format
						" 5px,f:p," + // document confidentiality
						" 5px,f:p," + // document blocked
						" 5px,f:p," + // healtcare facility
						" 5px,f:p," + // practice setting
						" 5px,f:p," + // author role
						" 5px,f:p," + // author specialty
						"15px:g"));

		// create the eSanté icon
		JLabel iconLabel = new JLabel(ESanteGuiUtils.getIcon(IconNames.ESANTE_LOGO_SQUARE, ESanteGuiUtils.LARGEPIX));
		iconLabel.setOpaque(false);
		// and add it to the dialog
		mainPanel.add(iconLabel, cc.xywh(2, 2, 1, 5, CellConstraints.CENTER, CellConstraints.CENTER));

		// create document name label
		JLabel documentNameLabel = new JLabel(Translatrix.getTranslationString("esante.actions.upload.dialog.label.documentName"));
		documentNameLabel.setOpaque(false);

		// add an input field for defining the document title
		documentNameField = new JTextField();
		documentNameField.setFont(documentNameField.getFont().deriveFont(Font.BOLD, 12));
		this.mainPanel.add(documentNameLabel, cc.xy(4, 2));
		this.mainPanel.add(documentNameField, cc.xy(6, 2));

		// set the default title if any
		setDocumentName(getMetadata().getTitle());

		// create language code combobox
		this.languageCodeCB = createCodeCB(4, 4, "esante.actions.upload.dialog.label.languageCode", CodeFetcher.LANGUAGE_CODE, null);
		// create type code combobox
		this.typeCodeCB = createCodeCB(4, 8, "esante.actions.upload.dialog.label.typeCode", CodeFetcher.TYPE_CODE, null);
		// only allow changing the type code for the initial upload of a document
		this.typeCodeCB.setEnabled(!isUpdate());
		// create class code combobox (content of the type code combobox depends
		// on the selected item of the class code check box)
		this.classCodeCB = createCodeCB(4, 6, "esante.actions.upload.dialog.label.classCode", CodeFetcher.CLASS_CODE, null, this.typeCodeCB);
		// only allow changing the class code for the initial upload of a document
		this.classCodeCB.setEnabled(!isUpdate());
		// create confidentiality code combobox
		this.confidentialityCodeCB = createCodeCB(4, 10, "esante.actions.upload.dialog.label.confidentialityCode", CodeFetcher.CONFIDENTIALITY_CODE, null);
		// create and add the check box to define if this document is blocked for the patient
		this.confidentialityBlockedCB = new JCheckBox();
		this.confidentialityBlockedCB.setOpaque(false);
		mainPanel.add(new JLabel(Translatrix.getTranslationString("esante.actions.upload.dialog.label.blocked")), cc.xy(4, 12));
		mainPanel.add(confidentialityBlockedCB, cc.xy(6, 12));
		// create format code combobox
		// this.formatCodeCB = createCodeCB(4, 10, "esante.actions.upload.dialog.label.formatCode", CodeFetcher.FORMAT_CODE, null);
		// create healthcare facility code combobox
		this.healthcareFacilityCodeCB = createCodeCB(4, 14, "esante.actions.upload.dialog.label.healthcareFacilityCode",
				CodeFetcher.HEALTH_CARE_FACILITY_TYPE_CODE, null);
		// create practice setting code combobox
		this.practiceSettingCodeCB = createCodeCB(4, 16, "esante.actions.upload.dialog.label.practiceSettingCode", CodeFetcher.PRACTICE_SETTING_CODE, null);
		// create practice setting code combobox
		this.authorRoleCB = createCodeCB(4, 18, "esante.actions.upload.dialog.label.authorRole", CodeFetcher.AUTHOR_ROLE, null);
		// create practice setting code combobox
		this.authorSpecialtyCB = createCodeCB(4, 20, "esante.actions.upload.dialog.label.authorSpecialty", CodeFetcher.AUTHOR_SPECIALTY, null);
		// preset some combobox values
		presetComboboxes();

		// verify is the field contains valid content after it has been changed
		documentNameField.addCaretListener(this);
	}

	/**
	 * Provides the display name of a selected code in the language, the user selected in the metadata config dialog.<br/>
	 * This function also works for any other code however with lower performance as such codes first have to be fetched from db.
	 * 
	 * @param code
	 *            The CdaCode object of which the localized display name is required
	 * @return The localized display name of the code or null, if there is no display name in the required language in the database
	 */
	public String getLocalizedDisplayName(CdaCode code) {
		if (ESanteUploadDialog.codeCache == null) {
			Vector<Integer> codeIds = new Vector<Integer>(6);
			codeIds.add(getLanguageCode().getId());
			codeIds.add(getClassCode().getId());
			codeIds.add(getTypeCode().getId());
			codeIds.add(getFormatCode().getId());
			codeIds.add(getConfidentialityCode().getId());
			codeIds.add(getHealthcareFacilityCode().getId());
			codeIds.add(getPracticeSettingCode().getId());
			codeIds.add(getAuthorRole().getId());
			codeIds.add(getAuthorSpecialty().getId());
			// ESanteUploadDialog.codeCache = eManager.getCdaCodeDescription(codeIds, getLanguageCode().getCodeId());
			/**
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! LANGUAGE CODE IS CURRENTLY FIXED TO FRENCH AS CODES IN OTHER LANGUAGES ARE APPARENTLY MISSING !!!!!!!!!!!!!!!!!
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! THE FOLLOWING INSTRUCTION HAS TO BE REMOVED AS SOON AS THE AGENCE ESANTE FIXED THIS ISSUE !!!!!!!!!!!!!!!!!!!!
			 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			 */
			// TODO ENABLE MULTI LANGUAGE SUPPORT AS SOON AS CORRESPONDING CODES ARE PROVIDED BY THE AGENCE ESANTÉ
			ESanteUploadDialog.codeCache = eManager.getCdaCodeDescription(codeIds, "fr-LU");
		}

		// first try to fetch it from cache (there will always be a hit for codes coming from the config dialog)
		String localizedName = codeCache.get(code.getId());
		if (localizedName == null) {
			// a different code was requested
			Vector<Integer> codeRequest = new Vector<Integer>(6);
			codeRequest.add(code.getId());
			// TODO ENABLE MULTI LANGUAGE SUPPORT AS SOON AS CORRESPONDING CODES ARE PROVIDED BY THE AGENCE ESANTÉ
			// this one has first to be fetched from db
			// localizedName= eManager.getCdaCodeDescription(codeRequest, getLanguageCode().getCodeId()).get(code.getId());
			localizedName = eManager.getCdaCodeDescription(codeRequest, "fr-LU").get(code.getId());
			// also store it in cache for speeding up subsequent requests for the same code
			codeCache.put(code.getId(), localizedName);
		}
		return localizedName;
	}

	/**
	 * Sets the last values the user has chosen for confidentiality, healthcare facility, and expertise area.
	 */
	private void presetComboboxes() throws Exception {
		String metadataLanguage;
		String metadataClass;
		String metadataType;
		String metadataFormat;
		String metadataConfidentiality;
		boolean isBlocked;
		String metadataHcFacilityType;
		String metadataExpertiseArea;
		String metadataAuthorRole;
		String metadataAuthorSpecialty;

		CdaDocument cda = getMetadata();

		// use the metadata of the actual document as default values
		metadataLanguage = getMetadata().getLanguageCode();
		metadataConfidentiality = getMetadata().getConfidentialityCode();
		metadataHcFacilityType = getMetadata().getHcfTypeCode();
		metadataExpertiseArea = getMetadata().getPracticeSettingCode();
		metadataAuthorRole = getMetadata().getAuthorRole();
		metadataAuthorSpecialty = getMetadata().getAuthorSpecialty();
		
		metadataClass = getMetadata().getClassCode();
		metadataType = getMetadata().getTypeCode();
		metadataFormat = getMetadata().getFormatCode();
		isBlocked = getMetadata().isBlocked();
		
		if (metadataLanguage != null) {
			setLanguageCode(metadataLanguage);
		}
		if (metadataConfidentiality != null) {
			setConfidentialityCode(metadataConfidentiality);
		}
		confidentialityBlockedCB.setSelected(isBlocked);
		if (metadataHcFacilityType != null) {
			setHealthcareFacilityCode(metadataHcFacilityType);
		} else {
			setHealthcareFacilityCode(metadataHcFacilityTypeDefault);
		}
		if (metadataExpertiseArea != null) {
			setPracticeSettingCode(metadataExpertiseArea);
		} else {
			setPracticeSettingCode(metadataExpertiseAreaDefault);
		}
		if (metadataAuthorRole != null) {
			setAuthorRole(metadataAuthorRole);
		}
		if (metadataAuthorSpecialty != null) {
			setAuthorSpecialty(metadataAuthorSpecialty);
		}
		if (metadataClass != null) {
			setClassCode(metadataClass);
		}
		if (metadataType != null) {
			setTypeCode(metadataType);
		}
	}

	/**
	 * Creates a combobox and adds it to the dialog. This cb will be populated and enriched by a label.
	 * 
	 * @param xCoordinate
	 *            X-coordinate where the cb should be placed on the dialog (the actual combobox will be shifted to the right as it will be accompanied by a
	 *            label)
	 * @param yCoordinate
	 *            Y-coordinate where the cb should be placed on the dialog
	 * @param labelTextPath
	 *            (OPTIONAL) A tranlatrix path to a label that should prefix the combobox. If this parameter is null or an empty string, no label will be added.
	 * @param categoryName
	 *            The name of the category of which the codes will be used to populate the combobox
	 * @param toolTipPath
	 *            (OPTIONAL) A translatrix path to a tool tip label. If this parameter is null or an empty string, no tool tip will be added.
	 * @return The newly created combobox
	 */
	private MetaDataComboBox createCodeCB(int xCoordinate, int yCoordinate, String labelTextPath, String categoryName, String toolTipPath) {
		return createCodeCB(xCoordinate, yCoordinate, labelTextPath, categoryName, toolTipPath, null);
	}

	/**
	 * Creates a combobox and adds it to the dialog. This cb will be populated and enriched by a label.
	 * 
	 * @param xCoordinate
	 *            X-coordinate where the cb should be placed on the dialog (the actual combobox will be shifted to the right as it will be accompanied by a
	 *            label)
	 * @param yCoordinate
	 *            Y-coordinate where the cb should be placed on the dialog
	 * @param labelTextPath
	 *            (OPTIONAL) A tranlatrix path to a label that should prefix the combobox. If this parameter is null or an empty string, no label will be added.
	 * @param categoryName
	 *            The name of the category of which the codes will be used to populate the combobox
	 * @param toolTipPath
	 *            (OPTIONAL) A translatrix path to a tool tip label. If this parameter is null or an empty string, no tool tip will be added.
	 * @param dependentBox
	 *            (OPTIONAL)If a dependent combo-box is provided, the content of the dependent combo-box will be updated in respect to the currently chosen item
	 *            of this (the master) combo-box
	 * @return The newly created combobox
	 * @throws Exception
	 *             Please check stacktrace for further details
	 */
	private MetaDataComboBox createCodeCB(int xCoordinate, int yCoordinate, String labelTextPath, String categoryName, String toolTipPath,
			MetaDataComboBox dependentBox) {

		// check if a label should be added
		boolean hasLabel = (labelTextPath != null) && !labelTextPath.isEmpty();

		// create the combobox
		MetaDataComboBox comboBox = new MetaDataComboBox(categoryName);
		// adjust the font for the content
		comboBox.setFont(comboBox.getFont().deriveFont(Font.BOLD, 12));
		// and avoid transparency
		comboBox.setOpaque(true);
		if (toolTipPath != null) {
			// add tool tip if provided
			comboBox.setToolTipText(Translatrix.getTranslationString(toolTipPath));
		}
		// add the custom renderer
		comboBox.setRenderer(new CdaCodeComboBoxRenderer());
		comboBox.setPreferredSize(new Dimension(200, 20));

		// add an action listener to be able to react to changes
		if (dependentBox != null) {
			comboBox.addActionListener(new UploadComboBoxActionListener(dependentBox));
		}
		// populate the combo box
		ESanteUploadDialog.populateCB(comboBox);
		// check if ok button can be enabled after combo-box content had been changed
		comboBox.addItemListener(this);

		// if a label was requested
		if (hasLabel) {
			// create a new label
			JLabel label = new JLabel(Translatrix.getTranslationString(labelTextPath));
			// and set it non-opaque
			label.setOpaque(false);
			// add the label
			this.mainPanel.add(label, this.cc.xy(xCoordinate, yCoordinate));
			// and the combobox next to it
			this.mainPanel.add(comboBox, cc.xy(xCoordinate + 2, yCoordinate));
		} else {
			// Place the combobox on the panel
			this.mainPanel.add(comboBox, cc.xy(xCoordinate, yCoordinate));
		}

		return comboBox;
	}

	/**
	 * (Re)populates a MetaDataComboBox with all codes corresponding it's category. If a parent id is provided, these codes will be filtered in respect to this
	 * id
	 * 
	 * @param comboBox
	 *            The combobox that will be (re)populated
	 */
	public static void populateCB(MetaDataComboBox comboBox) {
		populateCB(comboBox, null);
	}

	/**
	 * (Re)populates a MetaDataComboBox with all codes corresponding it's category. If a parent id is provided, these codes will be filtered in respect to this
	 * id
	 * 
	 * @param comboBox
	 *            The combobox that will be (re)populated
	 * @param parentId
	 *            The parent id that will be used to restrict the valid items. If this id is <code>null</code> all cda codes corresponding to the category of
	 *            this
	 */
	public static void populateCB(MetaDataComboBox comboBox, Integer parentId) {

		// populate the combo box
		List<CdaCode> content = null;
		// if the logging category does not exist, indicate it in the log file
		if ((content = CodeFetcher.getCdaCodes(comboBox.getCategory())) == null) {
			logger.error(new Exception("There are no CDA codes for the category \"" + comboBox.getCategory() + "\"."));
		}

		// remove the block code, because it has its own checkbox
		if (comboBox.getCategory().equals(CodeFetcher.CONFIDENTIALITY_CODE)) {
			CdaCode code = null;
			for (Iterator<CdaCode> iter = content.iterator(); iter.hasNext();) {
				code = iter.next();
				if (CdaDocument.CONFIDENTIALITY_BLOCKED.equals(code.getCodeId())) {
					content.remove(code);
					break;
				}
			}
		}

		// clear the combobox content
		comboBox.removeAllItems();

		// and repopulate it
		for (CdaCode cdaCode : content) {
			// if a dependence from a parent CDA code has been defined,
			// populate the combobox only with cda codes that correspond to the
			// provided parent id

			if (parentId != null) {
				if (cdaCode.getParentId() == null)
					logger.error("Parent code of category " + parentId + " for code " + cdaCode.getCodeId() + " expected, but none was found.");
				else if (!cdaCode.getParentId().equals(parentId))
					continue;
			}
			// add the compliant item
			comboBox.addItem(cdaCode);
		}
	}

	/**
	 * Allows the user to set different cda document metadata
	 * 
	 * @return True if the dialog was left by pressing ok button, false otherwise
	 */
	public boolean configureDocumentAttributes() {

		// show the dialog
		pack();
		centerWindowOnOwner();
		setVisible(true);

		if (getButtonOption() != OK_OPTION) {
			return false;
		}

		return true;
	}

	/* ======================================== */
	// GETTER & SETTER
	/* ======================================== */

	/**
	 * Provides the selected class code
	 * 
	 * @return The selected class code
	 */
	public CdaCode getClassCode() {
		return (CdaCode) classCodeCB.getSelectedItem();
	}

	/**
	 * Sets the active item of the class code cb
	 * 
	 * @param classCode
	 *            The class code
	 */
	public void setClassCode(CdaCode classCode) {
		this.classCodeCB.setSelectedItem(classCode);
	}

	/**
	 * Sets the active item of the class code cb
	 * 
	 * @param classCodeId
	 *            The class code id
	 */
	public void setClassCode(String classCodeId) {
		CdaCode code = CodeFetcher.getCdaCode(this.classCodeCB.getCategory(), classCodeId);
		setClassCode(code);
	}

	/**
	 * Provides the document name the physician has entered
	 * 
	 * @return the document name
	 */
	public String getDocumentName() {
		return documentNameField.getText();
	}

	/**
	 * Sets the document name
	 * 
	 * @param documentName
	 *            The intended name of the document
	 */
	public void setDocumentName(String documentName) {
		this.documentNameField.setText(documentName);
	}

	/**
	 * Provides the active item of the language code cb
	 * 
	 * @return The selected language code
	 */
	public CdaCode getLanguageCode() {
		return (CdaCode) languageCodeCB.getSelectedItem();
	}

	/**
	 * Sets the active item of the language code cb
	 * 
	 * @param languageCodeId
	 *            The language code id
	 */
	public void setLanguageCode(String languageCodeId) {
		CdaCode code = CodeFetcher.getCdaCode(this.languageCodeCB.getCategory(), languageCodeId);
		setLanguageCode(code);
	}

	/**
	 * Sets the active item of the language code cb
	 * 
	 * @param languageCode
	 *            The language code
	 */
	public void setLanguageCode(CdaCode languageCode) {
		this.languageCodeCB.setSelectedItem(languageCode);
	}

	/**
	 * Provides the active item of the confidentiality code cb
	 * 
	 * @return The selected confidentiality code
	 */
	public CdaCode getConfidentialityCode() {
		return (CdaCode) confidentialityCodeCB.getSelectedItem();
	}

	public boolean isBlocked() {
		// TODO add a check box and remove the block value from the confidentiality combo box
		return confidentialityBlockedCB.isSelected();
	}

	/**
	 * Sets the active item of the confidentiality code cb
	 * 
	 * @param confidentialityCodeId
	 *            The confidentiality code id
	 */
	public void setConfidentialityCode(String confidentialityCodeId) {
		CdaCode code = CodeFetcher.getCdaCode(this.confidentialityCodeCB.getCategory(), confidentialityCodeId);
		setConfidentialityCode(code);
	}

	/**
	 * Sets the active item of the confidentiality code cb
	 * 
	 * @param confidentialityCode
	 *            The confidentiality code
	 */
	public void setConfidentialityCode(CdaCode confidentialityCode) {
		this.confidentialityCodeCB.setSelectedItem(confidentialityCode);
	}

	/**
	 * Provides thethe format code
	 * 
	 * @return The format code (currently always pdf)
	 */
	public CdaCode getFormatCode() {
		if (formatCode == null) {
			formatCode = CodeFetcher.getCdaCode(CodeFetcher.FORMAT_CODE, formatCodeDefault);
		}
		return formatCode;
	}

	/**
	 * Indicates if the dialog should fetch the metadata of a preexisting document from the patient EHR and display it as default values in the metadata dialog.
	 * 
	 * @return True, if the metadata should be fetched from a prexisting document from the patients EHR
	 */
//	public boolean fetchMetadata() {
//		return fetchMetadata;
//	}

	/**
	 * Sets a flag that triggers the dialog to load the metadata from an existing document from the patient EHR and display it as default values.<br/>
	 * <br/>
	 * <i><b>Default value:</b> false</i>
	 * 
	 * @param updateMetadata
	 *            True, if the metadata of a preexisting document should be loaded and displayed
	 */
//	public void setFetchMetadata(boolean updateMetadata) {
//		this.fetchMetadata = updateMetadata;
//	}

	// /**
	// * Sets the active item of the format code cb
	// *
	// * @param formatCodeId
	// * The format code id
	// */
	// public void setFormatCode(String formatCodeId) {
	// CdaCode code = CodeFetcher.getCdaCode(this.formatCodeCB.getCategory(), formatCodeId);
	// setFormatCode(code);
	// }
	//
	// /**
	// * Sets the active item of the format code cb
	// *
	// * @param formatCode
	// * The format code
	// */
	// public void setFormatCode(CdaCode formatCode) {
	// this.formatCodeCB.setSelectedItem(formatCode);
	// }

	/**
	 * Provides the selected item of the practice setting code cb
	 * 
	 * @return The selected practice setting code
	 */
	public CdaCode getPracticeSettingCode() {
		return (CdaCode) practiceSettingCodeCB.getSelectedItem();

	}

	/**
	 * Sets the active item of the practice setting code cb
	 * 
	 * @param practiceSettingCodeId
	 *            The practice setting code id
	 */
	public void setPracticeSettingCode(String practiceSettingCodeId) {

		CdaCode code = CodeFetcher.getCdaCode(this.practiceSettingCodeCB.getCategory(), practiceSettingCodeId);
		setPracticeSettingCode(code);
	}

	/**
	 * Sets the active item of the practice setting code cb
	 * 
	 * @param practiceSettingCode
	 *            The practice setting code
	 */
	public void setPracticeSettingCode(CdaCode practiceSettingCode) {
		this.practiceSettingCodeCB.setSelectedItem(practiceSettingCode);
	}

	/**
	 * Provides the selected item of the type code cb
	 * 
	 * @return The selected type code
	 */
	public CdaCode getTypeCode() {
		return (CdaCode) typeCodeCB.getSelectedItem();
	}

	/**
	 * Sets the active item of the type code cb
	 * 
	 * @param typeCodeId
	 *            The type code id
	 */
	public void setTypeCode(String typeCodeId) {

		CdaCode code = CodeFetcher.getCdaCode(this.typeCodeCB.getCategory(), typeCodeId);
		setTypeCode(code);
	}

	/**
	 * Sets the active item of the type code cb
	 * 
	 * @param typeCode
	 *            The type code
	 */
	public void setTypeCode(CdaCode typeCode) {
		this.typeCodeCB.setSelectedItem(typeCode);
	}

	/**
	 * Provides the selected item of the healthcare facility code cb
	 * 
	 * @return The selected healthcare facility code
	 */
	public CdaCode getHealthcareFacilityCode() {
		return (CdaCode) this.healthcareFacilityCodeCB.getSelectedItem();
	}

	/**
	 * Sets the active item of the healthcare facility code cb
	 * 
	 * @param healthcareFacilityCodeId
	 *            The healthcare facility code id
	 */
	public void setHealthcareFacilityCode(String healthcareFacilityCodeId) {

		CdaCode code = CodeFetcher.getCdaCode(this.healthcareFacilityCodeCB.getCategory(), healthcareFacilityCodeId);
		setHealthcareFacilityCode(code);
	}

	/**
	 * Sets the active item of the healthcare facility code cb
	 * 
	 * @param healthcareFacilityCode
	 *            The healthcare facility code
	 */
	public void setHealthcareFacilityCode(CdaCode healthcareFacilityCode) {
		this.healthcareFacilityCodeCB.setSelectedItem(healthcareFacilityCode);
	}

	/**
	 * Provides the selected item of the author role cb
	 * 
	 * @return The selected author role
	 */
	public CdaCode getAuthorRole() {
		return (CdaCode) this.authorRoleCB.getSelectedItem();
	}

	/**
	 * Sets the active item of the author role cb
	 * 
	 * @param authorRoleId
	 *            The healthcare author role id
	 */
	public void setAuthorRole(String authorRoleId) {

		CdaCode code = CodeFetcher.getCdaCode(this.authorRoleCB.getCategory(), authorRoleId);
		setAuthorRole(code);
	}

	/**
	 * Sets the active item of the author role cb
	 * 
	 * @param authorRole
	 *            The author role
	 */
	public void setAuthorRole(CdaCode authorRole) {
		this.authorRoleCB.setSelectedItem(authorRole);
	}

	/**
	 * Provides the selected item of the author specialty cb
	 * 
	 * @return The selected author specialty
	 */
	public CdaCode getAuthorSpecialty() {
		return (CdaCode) this.authorSpecialtyCB.getSelectedItem();
	}

	/**
	 * Sets the active item of the author specialty cb
	 * 
	 * @param healthcareFacilityCodeId
	 *            The author specialty id
	 */
	public void setAuthorSpecialty(String authorSpecialtyId) {

		CdaCode code = CodeFetcher.getCdaCode(this.authorSpecialtyCB.getCategory(), authorSpecialtyId);

		setAuthorSpecialty(code);
	}

	/**
	 * Sets the active item of the author specialty cb
	 * 
	 * @param authorSpecialty
	 *            The author specialty
	 */
	public void setAuthorSpecialty(CdaCode authorSpecialty) {
		this.authorSpecialtyCB.setSelectedItem(authorSpecialty);
	}

	@Override
	public void prepareToShowUp() {
		okButton.setEnabled(checkFieldValidity());

		super.prepareToShowUp();
	}

	public void caretUpdate(CaretEvent e) {
		okButton.setEnabled(checkFieldValidity());
	}

	/**
	 * If a combobox value has been changed, it is checked if the ok button could be enabled. Further, if the user has changed the value of confidentiality,
	 * healthcare facility, or expertise area, the new values are stored to db.
	 */
	public void itemStateChanged(ItemEvent e) {
		if (e.getStateChange() == ItemEvent.SELECTED) {
			
			String authorId = getAuthorId();
			okButton.setEnabled(checkFieldValidity());
			
			if (e.getSource() == this.languageCodeCB) {
				eManager.setESanteUserProperty(ESanteProperty.PROP_METADATA_LANGUAGE, getLanguageCode().getCodeId(), authorId);
			}
			// should not be prepopulated with the last confidentially code used as this might be the wrong one. Default value will now be NORMAL
			// if (e.getSource() == this.confidentialityCodeCB) {
			// eManager.setESanteUserProperty(ESanteProperty.PROP_METADATA_CONFIDENTIALITY, getConfidentialityCode().getCodeId(), authorId);
			// }
			if (e.getSource() == this.healthcareFacilityCodeCB) {
				eManager.setESanteUserProperty(ESanteProperty.PROP_METADATA_HC_FACILITY_TYPE, getHealthcareFacilityCode().getCodeId(), authorId);
			}
			if (e.getSource() == this.practiceSettingCodeCB) {
				eManager.setESanteUserProperty(ESanteProperty.PROP_METADATA_EXPERTISE_AREA, getPracticeSettingCode().getCodeId(), authorId);
			}
			if (e.getSource() == this.authorRoleCB) {
				eManager.setESanteUserProperty(ESanteProperty.PROP_METADATA_AUTHOR_ROLE, getAuthorRole().getCodeId(), authorId);
			}
			if (e.getSource() == this.authorSpecialtyCB) {
				eManager.setESanteUserProperty(ESanteProperty.PROP_METADATA_AUTHOR_SPECIALTY, getAuthorSpecialty().getCodeId(), authorId);
			}

			ESanteUploadDialog.codeCache = null;
		}
	}

	/**
	 * Indicates if the metadata should be used for a document update.
	 * 
	 * @return True, if metadata should be used for update, false otherwise.
	 */
	public boolean isUpdate() {
		return isUpdate;
	}

	/**
	 * Defines if the current metadata should be used for an update or initial upload
	 * 
	 * @param isUpdate
	 *            True, if metadata should be used for update, false otherwise.
	 */
	public void setUpdate(boolean isUpdate) {
		this.isUpdate = isUpdate;
	}
	
	/**
	 * Provides the metadata description of the document
	 * 
	 * @return the metadata description of the document
	 */
	public CdaDocument getMetadata() {
		return this.metadata;
	}

	/**
	 * Sets the metadata description of the document
	 * 
	 * @param metadata
	 *            the metadata description of the document
	 */
	public void setMetadata(CdaDocument metadata) {
		this.metadata = metadata;
	}

	/**
	 * Provides the incident entry to which the document belongs
	 * 
	 * @return the metadata description of the document
	 */
	public IncidentEntry getIncidentEntry() {
		return this.incidentEntry;
	}
	
	/**
	 * Provides the unique id of the document in the DSP, if existing.
	 * @return The unique id of the document or null if it (or an older version of this document) has not yet been uploaded to the dsp.
	 */
	public String getDocumentUniqueId(){
		return getMetadata().getDocumentUniqueId();
	}

	/**
	 * Sets the incident entry to which the document belongs
	 * 
	 * @param incidentEntry
	 *            incident entry to which the document belongs
	 */
	public void setIncidentEntry(IncidentEntry incidentEntry) {
		this.incidentEntry = incidentEntry;
	}
	
	/**
	 * Checks if all fields respectively combo-boxes contain a value
	 * 
	 * @return True if all fields are valid, false otherwise
	 */
	private boolean checkFieldValidity() {

		try {
			return this.documentNameField.getText().trim().length() > 0 && this.languageCodeCB.getSelectedIndex() > -1
					&& this.classCodeCB.getSelectedIndex() > -1 && this.confidentialityCodeCB.getSelectedIndex() > -1
					// && this.formatCodeCB.getSelectedIndex() > -1 && this.practiceSettingCodeCB.getSelectedIndex() > -1
					&& this.practiceSettingCodeCB.getSelectedIndex() > -1 && this.healthcareFacilityCodeCB.getSelectedIndex() > -1
					&& this.typeCodeCB.getSelectedIndex() > -1 && this.authorRoleCB.getSelectedIndex() > -1 && this.authorSpecialtyCB.getSelectedIndex() > -1;
		} catch (Exception e) {
			// using try/catch is much faster and (convenient ;-) that checking every item for null
			return false;
		}

	}
}
