package lu.tudor.santec.gecamed.formeditor.gui.view.dialog;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialog;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedDialogActionListener;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.Form;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.FormTemplate;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.FormValue;
import lu.tudor.santec.gecamed.formeditor.ejb.session.beans.FormManagerImpl;
import lu.tudor.santec.gecamed.formeditor.ejb.session.interfaces.FormManager;
import lu.tudor.santec.gecamed.formeditor.gui.FormWidgets;
import lu.tudor.santec.gecamed.formeditor.gui.component.EditableChart;
import lu.tudor.santec.gecamed.formeditor.gui.component.EditableComponent;
import lu.tudor.santec.gecamed.formeditor.gui.model.ComboBoxElement;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormModel;
import lu.tudor.santec.gecamed.formeditor.gui.view.FormTab;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.IncidentEntry;
import lu.tudor.santec.gecamed.patient.ejb.session.beans.IncidentManagerBean;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.IncidentManager;
import lu.tudor.santec.gecamed.patient.gui.PatientManagerModule;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

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

public class NewFormDialog implements ListSelectionListener, GECAMedDialogActionListener {
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(NewFormDialog.class.getName());
	
	
	private DefaultListModel listModel;
	
	private ComboBoxElement<FormTemplate> element;
	
	private GECAMedBaseDialogImpl dialog;
	
	private JButton loadButton;
	
	
	// IMORT
	private JButton importButton;
	
	private SortedSet<HistorySorter> importHistorySet;
	
	private JProgressBar savingProgressBar;
	
	private List<IncidentEntry> storedIncidentEntries;
	
	private boolean importCanceled;
	
	

	public NewFormDialog(JComponent parent) 
	{
		init(parent, null);
	}
	
	public NewFormDialog(JComponent parent, Collection<FormTemplate> templates) 
	{
		init(parent, templates);
	}
	
	public void init (JComponent parent, Collection<FormTemplate> templates) 
	{
		String text = Translatrix.getTranslationString("formeditor.create_new_form")+":";
		JTextArea textArea = FormWidgets.getFormatedTextArea(text);
		
		listModel = new DefaultListModel();
		
		if (templates == null) {
			// get the available templates:
			FormManager manager = (FormManager)ManagerFactory.getRemote(FormManagerImpl.class);
			templates = manager.getAllTemplatesExceptSubformOnly();
		}
		
		ComboBoxElement<FormTemplate> element = null;
		
		for (FormTemplate template : templates) {
			/* ---------------------------------------- */
//			FormTemplate template = iter.next();
			element = new ComboBoxElement<FormTemplate>(template.getName(), template);
			listModel.addElement(element);
			/* ---------------------------------------- */
		}
		
		// create the JList containing the templates in the list model:
		JList templateList = new JList(listModel);
		templateList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		templateList.addListSelectionListener(this);
		templateList.addMouseListener(new MouseListener() {
			
			public void mouseReleased(MouseEvent e) {}
			public void mousePressed(MouseEvent e) {}
			public void mouseExited(MouseEvent e) {}
			public void mouseEntered(MouseEvent e) {}
			
			public void mouseClicked(MouseEvent e) {
				/* ---------------------------------------- */
				if (e.getClickCount() >= 2) {
					loadButton.doClick();
				}
				/* ---------------------------------------- */
			}
		});
		
		JPanel mainPanel = new JPanel(new FormLayout("2dlu, min(300px; p):g, 2dlu", 
				"2dlu, p, " + // description text
				"2dlu, p, " + // "available templates:" label
				"5dlu, min(150px;p):g, " + // JList
				"10dlu"));
		mainPanel.add(textArea, new CellConstraints(2, 2));
		mainPanel.add(new JLabel(
				Translatrix.getTranslationString("formeditor.available_templates")), 
				new CellConstraints(2, 4));
		mainPanel.add(new JScrollPane(templateList), 
				new CellConstraints(2, 6));
//		mainPanel.add(new JScrollPane(templateList), new CellConstraints(2, 6));
		
		// create dialog:
		dialog = GECAMedBaseDialogImpl.getNewInstance(parent, 
				Translatrix.getTranslationString("formeditor.create_form"), 
				GECAMedBaseDialog.OK_CANCEL_BUTTON_MODE, 
				mainPanel);
		dialog.setResizingOptions(GECAMedBaseDialog.RESIZING_NONE);
		dialog.setResizable(true);
		
		// create buttons:
		// load button
		loadButton = dialog.getOKButton();
		
//		dialog.setAction(GECAMedBaseDialog.OK_OPTION, new AbstractAction() {
//			private static final long serialVersionUID = 1L;
//
//			public void actionPerformed(ActionEvent e) {
//				/* ---------------------------------------- */
////				dialog.setVisible(false);
//				dialog.hideDialog();
//				/* ---------------------------------------- */
//			}
//		});
		loadButton.setEnabled(false);
		
		// import button
		importButton = new JButton(
				Translatrix.getTranslationString("formeditor.import"), 
				GECAMedModule.getMiniIcon(GECAMedIconNames.IMPORT));
		importButton.setEnabled(false);
		importButton.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				CSVImportDialog dialog = new FormEditorCSVImportDialog(
						GECAMedModule.getCurrentModule());
				
				
				FormTemplate template = NewFormDialog.this.element.getValue();
				
//				while (true) {
					Vector<Vector<ComboBoxElement<Object>>> data = dialog.showDialog(getMappedValuesForFormTemplate(template));
					
				if (data != null) {
					startSavingProgressBar(data, template);
					
					PatientManagerModule.getInstance().getPatientPanel()
					.showPanel(FormTab.NAME);
				} 
				
				NewFormDialog.this.element = null;
				NewFormDialog.this.dialog.setVisible(false);
			}
		});
		dialog.addButton(importButton);
		
		
//		dialog.setAction(GECAMedBaseDialog.CANCEL_OPTION, new AbstractAction() {
//			private static final long serialVersionUID = 1L;
//
//			public void actionPerformed(ActionEvent e) {
//				/* ---------------------------------------- */
//				NewFormDialog.this.element = null;
////				dialog.hideDialog();
//				dialog.setVisible(false);
//				/* ---------------------------------------- */
//			}
//		});
		
		dialog.setDialogActionListener(this);
	}

	public FormTemplate show() 
	{
		// format and show dialog
		dialog.pack();
		dialog.pack();
		dialog.pack();
		
		// center the window
//		GECAMedBaseDialog.centerWindowOnOwner(dialog, dialog.getOwner());
		
		dialog.setResizable(false);
		dialog.showCenteredDialog();
//		dialog.setVisible(true);
		
		if (element == null) {
			return null;
		}
		
		return (FormTemplate)element.getValue();
	}
	
	@SuppressWarnings("unchecked")
	public void valueChanged(ListSelectionEvent e) {
		element = (ComboBoxElement<FormTemplate>)((JList)e.getSource()).getSelectedValue();
		loadButton.setEnabled(element != null);
		importButton.setEnabled(element != null);
	}
	
	
	
	
	/* ======================================== */
	// 			HELP METHODS FOR IMPORT
	/* ======================================== */
	
	
	
	private Vector<ComboBoxElement<Class<?>>> getMappedValuesForFormTemplate (FormTemplate template) {
		/* ======================================== */
		// get the stored components out of the XML
		StringReader reader = new StringReader(template.getXml());
		SAXBuilder builder = new SAXBuilder();
		
		Vector<ComboBoxElement<Class<?>>> componentKeys = new Vector<ComboBoxElement<Class<?>>>();
		
		componentKeys.add(new ComboBoxElement<Class<?>>(EditableChart.CREATION_DATE_REF, Date.class));
//		componentKeys.add("");
		
		try {
			/* ---------------------------------------- */
			Document doc = builder.build(reader);
			
			importHistorySet = new TreeSet<HistorySorter>();
			
			List<?> componentElements = doc.getRootElement().getChildren("component");
			for (Object componentElement : componentElements) {
				
				Attribute storeInDB = ((Element)componentElement).getAttribute("storeInDB");
				
				
				if (storeInDB != null && storeInDB.getBooleanValue()) {
					// the component with this key is supposed to be stored in the db
					String componentKey = ((Element)componentElement).getAttributeValue("key");
					String componentType = ((Element)componentElement).getChildText("type");
					
					Class<?> clazz = String.class;
					try {
						clazz = Class.forName(componentType);
					} catch (ClassNotFoundException e) {
						logger.log(Level.ERROR, e.getMessage(), e);
					}
					componentKeys.add(new ComboBoxElement<Class<?>>(componentKey, clazz));
					
					// prepare the history text
					Attribute showInHistoy = ((Element)componentElement).getAttribute("showInHistory");
					if (showInHistoy != null && showInHistoy.getBooleanValue()) {
						Attribute historyIndex = ((Element)componentElement).getAttribute("historyIndex");
						Attribute historyName = ((Element)componentElement).getAttribute("labelText");
						
						HistorySorter hs = new HistorySorter(historyIndex.getFloatValue(), componentKey, historyName.getValue());
						
						this.importHistorySet.add(hs);
					}
				}
				
			}
			/* ---------------------------------------- */
		} catch (JDOMException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		} catch (IOException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		
		return componentKeys;
		/* ======================================== */
	}
	
	
	private void convertDataToForms (Vector<Vector<ComboBoxElement<Object>>> data, FormTemplate template) {
		/* ======================================== */
		
		Iterator<Vector<ComboBoxElement<Object>>> iter = data.iterator();
		
		HashMap<Integer, String> indexToKeyMap = new HashMap<Integer, String>();
		Vector<ComboBoxElement<Object>> header = iter.next();
		for (int index = 0; index < header.size(); index++) {
			
			ComboBoxElement<Object> elem = header.get(index);
			if (elem != null) {
				
				String key = elem.getTranslation();
				if (key != null) {
					indexToKeyMap.put(index, key);
				}
			}
		}
		
		int processSize = data.size() - 1;
		int currentProcessPosition = 0;
		this.savingProgressBar.setMaximum(processSize * 2);
		this.savingProgressBar.setString("");
		this.savingProgressBar.setStringPainted(true);
		
		
		List<Form> formsToSave = new LinkedList<Form>();
		
		while (iter.hasNext()) {

//			String msg = "preparing form " + ++currentProcessPosition + " of " + processSize;
			String msg = Translatrix.getTranslationString("formeditor.progressbarPreparingForm")
					.replace("$pos", String.valueOf(++currentProcessPosition))
					.replace("$size", String.valueOf(processSize));
			this.savingProgressBar.setString(msg);
			this.savingProgressBar.setValue(currentProcessPosition);
			
			
			// create a new form
			Form form = new Form();
			form.setTemplateId(template.getId());
			form.setPatientId(GECAMedModule.getCurrentPatient().getId());
			form.setValues(new LinkedList<FormValue>());
			
			// set the values for the components
			Vector<ComboBoxElement<Object>> rowValues = iter.next();
			
			
			for (Integer index : indexToKeyMap.keySet()) {
				/* ---------------------------------------- */
				String key = indexToKeyMap.get(index);
				if (key.equals(EditableComponent.CREATION_DATE_REF)) {
					form.setDateCreated((Date) rowValues.get(index).getValue());
				}
				
				String value = objectToString(rowValues.get(index).getValue());
				
				
				FormValue formValue = new FormValue();
				
				formValue.setKey(key);
				formValue.setValue(value);
				form.getValues().add(formValue);
				
				for (HistorySorter hs : importHistorySet) {
					if (hs.equals(key)) {
						hs.value = value;
						
						// the object is found and the value is set -> jump out of the loop
						break;
					}
				}
				/* ---------------------------------------- */
			}
			
			// set the history text
			form.setHistoryText(createHistoryText(template.getName()));
			
			formsToSave.add(form);
		}
		
		saveForms(formsToSave, template);
		
		/* ======================================== */
	}
	
	
	private String objectToString (Object o) {
		/* ======================================== */
		if (o == null) {
			return EditableComponent.MISSING_VALUE;
		} else if (o instanceof Date) {
			return FormWidgets.formatAsInternationalDateAndTimeFormat((Date)o);
		} else {
			return String.valueOf(o);
		}
		/* ======================================== */
	}
	
	
	private String createHistoryText (String formTitle) {
		
		StringBuffer historyText = new StringBuffer("<html><b>")
				.append(formTitle)
				.append("</b>");
		
		for (HistorySorter hs : importHistorySet) {
			if (hs != null 
					&& hs.key != null 
					&& hs.value != null) {
				historyText.append("<br>")
						.append(hs.name)
						.append(" <i>")
						.append(hs.value)
						.append("</i>");
			}
		}
		
		return historyText.toString();
	}
	
	
	private void saveForms (List<Form> forms, FormTemplate template) {
		/* ======================================== */
		// get the manager beans
		FormManager formManager = (FormManager) ManagerFactory.getRemote(FormManagerImpl.class);
		IncidentManager incidentManager = (IncidentManager) ManagerFactory.getRemote(IncidentManagerBean.class);
		
		this.storedIncidentEntries = new LinkedList<IncidentEntry>();
		
		int currentProcessPosition = 0;
		int processSize = forms.size();
		
		for (Form f  : forms) {
			
			if (this.importCanceled) {
				// the cancel button was pressed
				break;
			}
			
//			String msg = "saving form " + ++currentProcessPosition + " of " + processSize;
			String msg = Translatrix.getTranslationString("formeditor.progressbarSavingForm")
					.replace("$pos", String.valueOf(++currentProcessPosition))
					.replace("$size", String.valueOf(processSize));
			
			this.savingProgressBar.setString(msg);
			this.savingProgressBar.setValue(processSize + currentProcessPosition);
			
			saveForm(f, template, formManager, incidentManager);
		}
		/* ======================================== */
	}
	
	
	/**
	 * @param form 
	 * @param template
	 * @param formManager can be null
	 * @param incidentManager can be null
	 * @return
	 */
	private boolean saveForm (Form form, FormTemplate template, 
			FormManager formManager, IncidentManager incidentManager) {
		/* ======================================== */
		// if form or template is null, the form cannot be saved
		if (form == null || template == null) {
			return false;
		}
		
		// get instances of the manager beans, if they're null
		if (formManager == null) {
			formManager = (FormManager) ManagerFactory.getRemote(FormManagerImpl.class);
		}
		if (incidentManager == null) {
			incidentManager = (IncidentManager) ManagerFactory.getRemote(IncidentManagerBean.class);
		}
		
		try {
			// need to be set here, because after saving the form it will be null
			String historyText = form.getHistoryText();
			
			if (form.getId() == null 
					&& form.getValues() != null 
					&& form.getValues().size() > 0) {
				/* ---------------------------------------- */
				// prepare the FormValues to be saved
				Collection<FormValue> values = form.getValues();
				form.setValues(null);
				
				form = formManager.saveForm(form);
				
				for (FormValue formValue : values) {
					formValue.setFormId(form.getId());
				}
				
				form.setValues(values);
				/* ---------------------------------------- */
			}

			// save the created Form
			FormWidgets.addFormEntryForCurrentIncident(form);
			FormModel.changeIncidentEntryCreationDate(form, incidentManager);
			
			// set the history text
			form.getIncidentEntry().setTextContent(historyText);
			
			// save the form
			form = formManager.saveForm(form);
			
			// put all incident entries of the saved forms into this list
			storedIncidentEntries.add(form.getIncidentEntry());
			
		} catch (Exception e) {
			logger.log(Level.ERROR, e.getMessage(), e);
			return false;
		}
		
		return true;
		/* ======================================== */
	}
	
	
	private void deleteStoredEntries () {
		/* ======================================== */
		if (storedIncidentEntries == null
				|| storedIncidentEntries.isEmpty()) {
			return;
		}
		
		
		int entriesToDelete = storedIncidentEntries.size();
		int index = 0;
		
		savingProgressBar.setMaximum(entriesToDelete);
//		savingProgressBar.setString("Deleting forms. " + (entriesToDelete-index++) + " of " + entriesToDelete + " left.");
		savingProgressBar.setString(Translatrix.getTranslationString("formeditor.progressbarDeletingForm")
				.replace("$pos", String.valueOf(entriesToDelete-index++))
				.replace("$size", String.valueOf(entriesToDelete)));
		savingProgressBar.setForeground(Color.RED);
		
		
		IncidentManager manager = (IncidentManager)ManagerFactory.getRemote(IncidentManagerBean.class);
		
		for (IncidentEntry entry : storedIncidentEntries) {
			// delete the form
			manager.removeIncidentEntry(entry);
			
			// set the progress bar
			savingProgressBar.setString("Deleting forms. " + (entriesToDelete-index++) + " of " + entriesToDelete + " left.");
			savingProgressBar.setValue(entriesToDelete-index);
		}
		
		/* ======================================== */
	}
	
	
	private void startSavingProgressBar (final Vector<Vector<ComboBoxElement<Object>>> data, final FormTemplate template) {
		JPanel mainPanel = new JPanel(new FormLayout("10px, 1px:g, 10px", "1px:g, 25px, 1px:g"));
		
		
		savingProgressBar = new JProgressBar(0, 100);
		mainPanel.add(savingProgressBar, new CellConstraints(2, 2));
		
		GECAMedBaseDialogImpl dialog = new GECAMedBaseDialogImpl(NewFormDialog.this.dialog, 
				Translatrix.getTranslationString("formeditor.importingForms"), 
				GECAMedBaseDialog.OK_CANCEL_BUTTON_MODE, 
				mainPanel);
		
		
		final JButton okButton = dialog.getOKButton();
		
		final JButton cancelButton = dialog.getCancelButton();
		
		cancelButton.setAction(new AbstractAction(cancelButton.getText(), cancelButton.getIcon()) {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				importCanceled = true;
				cancelButton.setEnabled(false);
				/* ---------------------------------------- */
			}
		});
		
		
		dialog.setSize(300, 120);
//		FormEditorDialog.centerWindowOnOwner(dialog, dialog.getOwner());
		
		
		new Thread(new Runnable() {
			/* **************************************** */
			public void run() {
				/* ---------------------------------------- */
				okButton.setEnabled(false);
				
				importCanceled = false;
				
				convertDataToForms(data, template);
				
				if (importCanceled) {
					deleteStoredEntries();
				}
				
				PatientManagerModule.getInstance().getPatientPanel()
						.reloadHistory();
				
				cancelButton.setEnabled(false);
				okButton.setEnabled(true);
				/* ---------------------------------------- */
			}
			/* **************************************** */
		}).start();
		
		dialog.showCenteredDialog();
	}
	

	public void okActionCalled()
	{
		dialog.setVisible(false);
	}
	

	public void cancelActionCalled()
	{
		NewFormDialog.this.element = null;
		dialog.setVisible(false);
	}
	
	
	public void applyActionCalled() {}
	
	public void yesActionCalled() {}
	
	public void noActionCalled() {}
	
	public void closeActionCalled() 
	{
		cancelActionCalled();
	}
	
	public void prepareToShowUp () {}
	
	/* ======================================== */
	/* ======================================== */
}

class HistorySorter implements Comparable<HistorySorter> {
	
	public float index;

	public String key;
	
	public String name;
	
	public String value;
	
	
	public HistorySorter () {}
	
	public HistorySorter (float index, String key, String name) {
		this.index = index;
		this.key = key;
		this.name = name;
	}
	
	public int compareTo(HistorySorter o) {
		return this.index < o.index ? -1 : (this.index == o.index ? 0 : 1);
	}
	
	@Override
	public boolean equals (Object o) {
		if (o instanceof HistorySorter) {
			return this.key.equals(((HistorySorter)o).key);
		} else if (o instanceof String) {
			return this.key.equals((String)o);
		} else {
			return super.equals(o);
		}
	}
}
