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

import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.event.CaretEvent;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

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.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.luxtrust.UserInactivityMonitor;
import lu.tudor.santec.gecamed.esante.gui.utils.ESanteGuiUtils;
import lu.tudor.santec.gecamed.esante.utils.exceptions.SendingStoppedException;
import lu.tudor.santec.i18n.Translatrix;

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

/**
 * 
 * @author donak
 * 
 * @version <br>
 *          $Log: ParameterRequestDialog.java,v $
 *          Revision 1.11  2014-02-06 14:26:21  ferring
 *          user will be logged out, when he presses cancel
 *
 *          Revision 1.10  2014-02-03 10:27:37  ferring
 *          Caching the eHealth IDs corrected
 *
 *          Revision 1.9  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.8  2014-01-29 12:48:41  ferring
 *          typing mistake corrected
 *
 *          Revision 1.7  2014-01-28 17:53:09  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.6  2014-01-28 10:20:12  donak
 *          Added another hack to bypass erroneous dsp assertion request with saml token till sqli fixed the bug in the platform software
 *          Save user info from smartcard to db for displaying it in the config panel
 *
 *          Revision 1.5  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.4  2013-12-27 18:09:26  donak
 *          Cleanup of imports
 *
 *          Revision 1.3  2013-12-09 10:31:12  donak
 *          Assured that the returned part of the eHealth id has exactly 8 figures
 *
 *          Revision 1.2  2013-12-05 18:44:04  donak
 *          Partial upload support for the eSanté platform, not yet functional
 *
 *          Revision 1.1  2013-12-05 15:19:25  donak
 *          Partial upload support for the eSanté platform, not yet functional
 *
 */
public class ParameterRequestDialog extends GECAMedBaseDialogImpl implements KeyListener {

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

	private static final long serialVersionUID = 5603949255110390535L;
//	private static Logger logger = Logger.getLogger(GECAMedBaseDialogImpl.class.getName());

	private static ESanteConfigManager eManager = null;
//	private final int userId = GECAMedModule.getCurrentUser().getId();
	// stores all parameters that have been fetched from db via this dialog to speed up processing.
	private Map<String, Map<String, String>> eSanteUserPropertiesCache	= new HashMap<String, Map<String, String>>();

	/* ======================================== */
	// MEMBERS
	/* ======================================== */
	private static ParameterRequestDialog dialog = new ParameterRequestDialog();
	private IntegerField parameterField = null;
	private CellConstraints cc = new CellConstraints();
	private JLabel descriptionLabel = null;
	private JLabel parameterLabel = null;
	private JLabel linkLabel = null;
	private String linkText = null;
	private String url = null;

	/* ======================================== */
	// CONSTRUCTORS
	/* ======================================== */
	/**
	 * Creates a dialog that asks the user to enter a parameter that will be stored in the database.
	 */
	public ParameterRequestDialog() {

		super(MainFrame.getInstance(), null, OK_CANCEL_BUTTON_MODE);
		eManager = ESanteConfigManagerBean.getInstance();

		setResizingOptions(RESIZING_NONE);

		mainPanel.setLayout(new FormLayout(
		// column definition
				"15px, f:p, 15px, f:p, 10px, f:150px:g, 15px",
				// row definition
				"15px,f:p," + // icon + description
				"15px,f:p," + // link
				"15px,f:p," + // parameter input field
				"15px:g")); 

		// create the eSanté icon
		JLabel iconLabel = new JLabel(ESanteGuiUtils.getIcon(IconNames.ESANTE_LOGO_SQUARE, ESanteGuiUtils.LARGEPIX));
		iconLabel.setOpaque(false);

		// create document name label
		parameterLabel = new JLabel();
		parameterLabel.setOpaque(false);

		// add an input field for defining the document title
		parameterField = new IntegerField();
		parameterField.setFont(parameterField.getFont().deriveFont(Font.BOLD, 12));

		// add the label with the description
		descriptionLabel = new JLabel();
		descriptionLabel.setOpaque(false);
		descriptionLabel.setHorizontalAlignment(JLabel.CENTER);
		descriptionLabel.setFont(descriptionLabel.getFont().deriveFont(Font.BOLD, 12));

		// create the link that allows the user to obtain further information about the parameter and how to obtain it
		linkLabel = createLinkLabel();

		// add the components to the dialog
		this.mainPanel.add(iconLabel,			cc.xywh(2, 2, 1, 5, CellConstraints.CENTER, CellConstraints.TOP));
		this.mainPanel.add(descriptionLabel,	cc.xyw (4, 2, 3,  CellConstraints.LEFT, CellConstraints.CENTER));
		this.mainPanel.add(linkLabel, 			cc.xyw (4, 4, 3,  CellConstraints.LEFT, CellConstraints.CENTER));
		this.mainPanel.add(parameterLabel, 		cc.xy  (4, 6));
		this.mainPanel.add(parameterField, 		cc.xy  (6, 6));
		// verify is the field contains valid content after it has been changed
		parameterField.addKeyListener(this);
		
		
	}
	
	public static ParameterRequestDialog getInstance(){
		return dialog;
	}

	/**
	 * Tries to provide an eSanté parameter by using the following approach:<br>
	 * <ul>
	 * <li>1. Checks if the requested parameter has already been cached. If this is the case, it will directly be returned from cache</li>
	 * <li>2. Try to obtain the requested parameter from the database</li>
	 * <li>3. If the parameter does not exist in the database, a dialog is opened that asks the user to enter the parameter value manually</li>
	 * </ul>
	 * 
	 * @param windowTitlePath
	 *            The translatrix path to the label of the dialog
	 * @param labelPath
	 *            The translatrix path to the label of the parameter input field
	 * @param descriptionPath
	 *            The translatrix path to the description of the parameter
	 * @param linkTextkPath
	 *            The translatrix path to the link text to further information about the parameter and about how to obtain it.
	 * @param linkUrlPath
	 *            The translatrix path to the url to which the link will point. (will be opened in the system browser)
	 * @param eSanteParameter
	 *            The parameter for which the user should enter a value
	 * @param authenticationId
	 *            The authentication id of the esante platform user to whom the parameter belongs. It might be null, if the parameter is not user dependent. The
	 *            authentication id is usually either the smartcard serial number or the id the user provides whith his username and password - depending on the
	 *            chosen login method
	 * @return The value of the requested parameter or null, if none could be obtained, neither from DB or user
	 * @throws SendingStoppedException If the cancel button was pressed
	 */
	public String requestParameter(String windowTitlePath, String labelPath, String descriptionPath, String linkTextkPath, String linkUrlPath,
			String eSanteParameter, String authenticationId) {
		String value = null;
		// first step is to check if the requested value is already available in the cache
		value = getCachedParameter(eSanteParameter, authenticationId);
		if (value != null) {
			return value;
		}
		// this was not the case thus request it from db
		value = eManager.getESanteUserProperty(eSanteParameter, authenticationId);
		// only put it to cache if there was a usable value in the db
		if ((value != null) && (value.length() == 10)) {
			// add the new value to the cache
			cacheParameter(eSanteParameter, authenticationId, value);
			return value;
		}

		// neither in cache, nor in the database thus the user has to provide it.
		// open the dialog and allow the user to enter the parameter value manually
		descriptionLabel.setText(Translatrix.getTranslationString(descriptionPath));
		parameterLabel.setText(Translatrix.getTranslationString(labelPath));
		linkText = Translatrix.getTranslationString(linkTextkPath);
		url = Translatrix.getTranslationString(linkUrlPath);
		linkLabel.setText("<HTML><FONT color=\"#000099\">" + linkText + "</FONT></HTML>");
		this.setTitle(Translatrix.getTranslationString(windowTitlePath));
		
		// show the dialog
		pack();
		centerWindowOnOwner();
		setVisible(true);
		
		if (getButtonOption() != OK_OPTION) {
			UserInactivityMonitor.logout();
			return null;
		}

		// the user entered a valid value
		value = getParameter();
		// write it to the database
		eManager.setESanteUserProperty(eSanteParameter, value, authenticationId);
		// if the requested parameter has been the user eHealth id, also store the user name to the db
		if (ESanteProperty.PROP_USER_EHEALTH_ID.equals(eSanteParameter)) {
			try {
				// save the eSanté user name to the database for being able to indicate it in the configuration panel along with the eHealth-ID
				eManager.setESanteUserProperty(ESanteProperty.PROP_ESANTE_USER_INFO, LoginDialog.getConfiguration().getLogin(), authenticationId);
			} catch (Exception e) {
			}
		}
		// and also to the cache in order to serve future requests faster
		cacheParameter(eSanteParameter, authenticationId, value);

		return value;
	}
	
	
	/**
	 * Caches a value by a property name and the eSante user ID
	 * 
	 * @param propertyName The name of the ESanteProperty to cache
	 * @param eSanteUserId The eSante user ID of the ESanteProperty to cache
	 * @param value The value of the ESanteProperty to cache
	 */
	private void cacheParameter (String propertyName, String eSanteUserId, String value)
	{
		Map<String, String> subCache = eSanteUserPropertiesCache.get(propertyName);
		
		if (subCache == null)
		{
			subCache = new HashMap<String, String>();
			eSanteUserPropertiesCache.put(propertyName, subCache);
		}
		
		subCache.put(eSanteUserId, value);
	}
	
	
	/**
	 * Loads a cached value by property name and eSante user ID, if available.
	 * @param propertyName The name of the ESanteProperty to load from cache
	 * @param eSanteUserId The eSante user ID of the ESanteProperty to load from cache
	 * @return The cached value or <code>null</code> if no value was cached for the given parameter.
	 */
	private String getCachedParameter (String propertyName, String eSanteUserId)
	{
		Map<String, String> subCache = eSanteUserPropertiesCache.get(propertyName);
		
		if (subCache == null)
			return null;
		
		return subCache.get(eSanteUserId);
	}

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

	/**
	 * Provides the eHealth id the physician typed in
	 * 
	 * @return The eHealth id (10 figures)
	 */
	public String getParameter() {
		return parameterField.getText();
	}

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

		super.prepareToShowUp();
	}

	public void keyPressed(CaretEvent e) {}

	public void keyTyped(KeyEvent e) {}

	public void keyPressed(KeyEvent e) {}

	public void keyReleased(KeyEvent e) {
		okButton.setEnabled(checkFieldValidity());
	}

	/**
	 * Checks if all fields respectively combo-boxes contain a value
	 * 
	 * @return True if all fields are valid, false otherwise
	 */
	private boolean checkFieldValidity() {

		try {
			// The eHealth id has to have exactly 10 figures (8 figured id and 2 figures checksum)
			return this.parameterField.getText().length() == 10;
		} catch (Exception e) {
			// using try/catch is much faster and (convenient ;-) that checking every item for null
			return false;
		}
	}

	/**
	 * Creates a label that represents a link, which opens an URL in the system browser when clicked.
	 * 
	 * @return A label that is able to hold a clickable link
	 */
	public JLabel createLinkLabel() {

		final JLabel link = new JLabel();

		// currently no tool tip is needed
		// if(!toolTip.equals("")) link.setToolTipText(toolTip);
		link.setCursor(new Cursor(Cursor.HAND_CURSOR));
		link.addMouseListener(new MouseAdapter() {

			public void mouseExited(MouseEvent arg0) {
				// reset link to normal when mouse leaves
				link.setText("<HTML><FONT color=\"#000099\">" + linkText + "</FONT></HTML>");
			}

			public void mouseEntered(MouseEvent arg0) {
				// underline link when mouse enters
				link.setText("<HTML><FONT color=\"#000099\"><U>" + linkText + "</U></FONT></HTML>");
			}

			// fire up the browser and display the link
			public void mouseClicked(MouseEvent arg0) {
				if (Desktop.isDesktopSupported()) {
					try {
						Desktop.getDesktop().browse(new URI(url));
					} catch (Exception e) {
						e.printStackTrace();
					}
				} else {
					// Occurs if system does not support desktop api
					JOptionPane pane = new JOptionPane("Could not open link.");
					JDialog dialog = pane.createDialog(new JFrame(), "");
					dialog.setVisible(true);
				}
			}
		});
		return link;
	}

}

class IntegerField extends JTextField {

	private static final long serialVersionUID = 6269194316108617980L;

	public IntegerField() {
		super();
	}

	public IntegerField(int cols) {
		super(cols);
	}

	@Override
	protected Document createDefaultModel() {
		return new UpperCaseDocument();
	}

	static class UpperCaseDocument extends PlainDocument {

		private static final long serialVersionUID = 1L;

		@Override
		public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {

			if (str == null) {
				return;
			}

			char[] chars = str.toCharArray();
			boolean ok = true;

			for (int i = 0; i < chars.length; i++) {

				try {
					Integer.parseInt(String.valueOf(chars[i]));
				} catch (NumberFormatException exc) {
					ok = false;
					break;
				}

			}

			if (ok)
				super.insertString(offs, new String(chars), a);

		}
	}

}