package lu.tudor.santec.gecamed.formeditor.gui.model;

import java.awt.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.swing.DefaultListModel;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.filechooser.FileFilter;

import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.IconFetcher;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
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.StatusBar;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.FormTemplate;
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.FormEditorModule;
import lu.tudor.santec.gecamed.formeditor.gui.FormWidgets;
import lu.tudor.santec.gecamed.formeditor.gui.component.BorderComponent;
import lu.tudor.santec.gecamed.formeditor.gui.component.DummyComponent;
import lu.tudor.santec.gecamed.formeditor.gui.component.EditableComponent;
import lu.tudor.santec.gecamed.formeditor.gui.component.EditableLabel;
import lu.tudor.santec.gecamed.formeditor.gui.controller.EditorPanelController;
import lu.tudor.santec.gecamed.formeditor.gui.controller.FormEditorPopupController;
import lu.tudor.santec.gecamed.formeditor.gui.view.dialog.SelectFormDialog;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import com.jgoodies.forms.layout.CellConstraints;
import com.l2fprod.common.demo.Main;

public class FormEditorModel {

	/**
	 * @author Jens Ferring 2009-11-16
	 * 
	 *         This class represents the main part of the form-editor. It is the
	 *         model and the view, containing most of the dynamic data used by
	 *         the FormEditor.
	 */
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(FormEditorModel.class.getName());

	/*
	 * the view with all its components
	 */
	private FormEditorModule view;
	// only contains the "actual"-components (no dummy or border elements) -
	// these are the components, that have to be stored in the end
	private Map<String, EditableComponent> 	editableComponents 	= new TreeMap<String, EditableComponent>();
	private Map<Component, FormModel> 		previews 			= new HashMap<Component, FormModel>();
	private int previewNo = 1;

	// /*
	// * Drag&Drop
	// * the editable components in the editPanel can be dragged around in the
	// edit panel.
	// * An editable component can only be dropped on a dummy component
	// */
	// private JComponent dragedComponent;

	/*
	 * ScriptEditor:
	 */
	private ScriptEditorModel 		scriptEditorModel;
	private String 					initScript = "";
	private Map<String, String> 	scriptFunctions = new TreeMap<String, String>();

	// this is the currently selected component
	private EditableComponent 		curSelComp;
	private FormModel 				formModel;
	private DefaultListModel 		templateListModel;
	private SelectFormDialog 		selectFormDialog;

	// controls the mouse-over and select effects
	private EditorPanelController 	editorCtrl;

	/***************************************************************
	 ***************** CONSTRUCTOR AND INIT-METHOD *****************
	 **************************************************************/

	public FormEditorModel(FormEditorModule view) 
	{
		this.view = view;
		editorCtrl = new EditorPanelController(this);
	}

	/**************************************************
	 ***************** PUBLIC-METHODS *****************
	 *************************************************/

	/**
	 * adds a component into the map and panel and returns, if the component was
	 * added successfully
	 * 
	 * @param ec
	 *            : the Component to add
	 * @param addToMap
	 *            : will the component be also added to the map or not (if not
	 *            indicated, the component will be added to the map by default)
	 * @return
	 */
	public boolean addComponent(EditableComponent ec) {
		return addComponent(ec, true);
	}

	public boolean addComponent(EditableComponent ec, boolean addToMap) 
	{
		int col = ec.getColumn();
		int row = ec.getRow();
		int width = ec.getColumnWidth();
		int height = ec.getRowHeight();
		if (addToMap && (width > 1 || height > 1)) 
		{
			/*
			 * only the rearrangePanel-method doesn't add components to the map.
			 * If the rearrangePanel-method adds a component, the panel is
			 * empty. If not there possibly are dummy-components, which have to
			 * be deleted before adding or there are editable-components, which
			 * cannot be delete and lead to the component not being added.
			 */
			if (!ec.canAddComponent(width, height)) 
			{
				/*
				 * there are editable-components in range of the component. The
				 * component will not be added and the method returns false
				 */
				return false;
			}
			
			/*
			 * there only are dummy-components in range of the component. The
			 * dummy-components will be removed and the component will be added.
			 */
			removeComponents(col, row, ec.getEndColumn(), ec.getEndRow(), false);
		}

		/*
		 * the component gets a new name, because it has a new position this is
		 * necessary for the TransferHandler / DnD and is added to the editPnl
		 */
		ec.setName(col + ":" + row);

		if (addToMap) 
		{
			// if the components are added by the rearrangeEditPanel-method,
			// this part mustn't be executed
			if (ec.isRealComponent()) 
			{
				/*
				 * if the added component is a "real"-component (a non-dummy-
				 * and non-border-component) the component will be add to the
				 * editableComponents-map, where all "real"-components are
				 * stored.
				 */
				editableComponents.put(ec.getKey(), ec);
			}
			
			ec.addMouseListener(editorCtrl);
			ec.getComponent().addMouseListener(editorCtrl);
			
			/*
			 * the added component will be added to the map now. If there was
			 * another component with this key the listener of this component
			 * will be removed
			 */
			EditableComponent toRemove = view.putComponent(col + ":" + row, ec);
			if (toRemove != null)
			{
				toRemove.removeMouseListener(editorCtrl);
				toRemove.getComponent().removeMouseListener(editorCtrl);
				view.getEditPanel().remove(toRemove);
			}
			
			if (ec.isRealComponent()) 
				update(ec, true);
		}
		view.getEditPanel().add(ec, ec.getConstraints());
		
		return true;
	}

	/**
	 * remove all components in the specified area
	 * 
	 * @param fromCol
	 * @param fromRow
	 * @param toCol
	 * @param toRow
	 * @param addDummy
	 */
	public void removeComponents(int fromCol, int fromRow, int toCol,
			int toRow, boolean addDummy) {
		/* ======================================== */
		for (int column = fromCol; column <= toCol; column++) {
			for (int row = fromRow; row <= toRow; row++) {
				removeComponent((column) + ":" + (row), addDummy);
			}
		}
		/* ======================================== */
	}

	/**
	 * removes a component from the map and panel (at the specified coordinates)
	 * 
	 * @param coordinates
	 *            : this are the coordinates of the component, to be removed
	 * @param c
	 *            : instead of indicate the coordinates you can directly
	 *            indicate the EditableComponent itself
	 * @param addDummy
	 *            : if false it won't be added a dummy-component at the former
	 *            position of the component
	 */
	public void removeComponent(String coordinates) {
		removeComponent(coordinates, true);
	}

	public void removeComponent(String coordinates, boolean addDummy) {
		int col = Integer.parseInt(coordinates.split(":")[0]);
		int row = Integer.parseInt(coordinates.split(":")[1]);
		if (view.getComponent(col+":"+row) != null) {
			removeComponent(view.getComponent(col + ":" + row), addDummy);
//		} else {
//			System.out.println(col+":"+row);
		}
	}

	public void removeComponent(EditableComponent c) {
		removeComponent(c, true);
	}

	public void removeComponent(EditableComponent c, boolean addDummy) {
		int col = c.getColumn();
		int row = c.getRow();
		view.getEditPanel().remove(c);
		view.putComponent(col + ":" + row, null);
		c.removeMouseListener(editorCtrl);

		if (c.isRealComponent()) {

			// if the removed component was a "real"-component (a non-border-
			// and non-dummy-component)
			// it was added to the editableComponents list and has to be removed
			// from there
			editableComponents.remove(c.getKey());

			// this is done, to have a component selected, after a
			// real-component was removed
			// Iterator<EditableComponent> iter =
			// editableComponents.values().iterator();
			// EditableComponent e = c;
			// if (iter.hasNext()) {
			// e = iter.next();
			// }
			//
			// update(e, true);
			update(null, true);
			// }
			// if (addDummy) {

			/*
			 * a dummy is / dummies are created and add to the former position
			 * of the removed component (only if the addDummy-parameter is true)
			 */
			for (int i = col; i <= c.getEndColumn()
					&& i <= view.getEditorColumnCount(); i++) {
				for (int j = row; j <= c.getEndRow()
						&& j <= view.getEditorRowCount(); j++) {
					addComponent(createDummy(i, j));
				}
			}
		}
	}

	/**
	 * moves a component (not a dummy or a border) from one position to another.
	 * Dummy- and border-components cannot be moved.
	 * 
	 * @param fromCoordinates
	 *            : coordinates of the dragged component
	 * @param toCoordinates
	 *            : coordinates of the target component
	 * @return returns true, if the moving was successful, otherwise false
	 */
	public boolean moveComponent(String fromCoordinates, String toCoordinates) {
		int fromCol = Integer.parseInt(fromCoordinates.split(":")[0]);
		int fromRow = Integer.parseInt(fromCoordinates.split(":")[1]);
		int toCol = Integer.parseInt(toCoordinates.split(":")[0]);
		int toRow = Integer.parseInt(toCoordinates.split(":")[1]);
		EditableComponent move = view.getComponent(fromCol + ":" + fromRow);
		EditableComponent target = view.getComponent(toCol + ":" + toRow);
		return moveComponent(move, target);
	}

	public boolean moveComponent(EditableComponent movableComponent,
			EditableComponent targetComponent) {
		/*
		 * A component can only be moved, if the target is a dummy (components
		 * and border-components are no drop targets -> must not be checked) and
		 * if there is no other "real"-component in range (is checked by
		 * addComponent-methods) - override protection. All dummy-components in
		 * target range will be removed by addCompont-methods
		 */

		// the component to be moved, is removed from its start position
		removeComponent(movableComponent);
		
		// the old coordinates are saved, if the moving cannot be done
		int oldCol = movableComponent.getColumn();
		int oldRow = movableComponent.getRow();
		movableComponent.setColumn(targetComponent.getColumn());
		movableComponent.setRow(targetComponent.getRow());
		if (addComponent(movableComponent, true)) {

			/*
			 * the component was added successfully, what means, the moving will
			 * be successful. The edit- and settingsPanel will be rearranged for
			 * updating and the method returns true.
			 */
			view.rearrangeEditPanel();
//			view.rearrangeSettingsPanel();
			
//			view.getEditPanel().validate();
//			view.getEditPanel().repaint();
			view.getSettingsPanel().validate();
			view.getSettingsPanel().repaint();
			
			return true;

		} else {

			/*
			 * The moving cannot be done for some reason. But the component was
			 * already removed. It has to be added again at the old coordinates.
			 * The method returns false.
			 */
			movableComponent.setColumn(oldCol);
			movableComponent.setRow(oldRow);
			addComponent(movableComponent);
			
			return false;
		}
	}

	/**
	 * This method checks, if a component can be added a position with the
	 * specified column width and row height.
	 * 
	 * @param ec
	 *            : the component to be added
	 * @param width
	 *            : the column width of the component
	 * @param height
	 *            : the row height of the component
	 * @return returns true if the component can be add
	 */
	public boolean canAddComponent(EditableComponent ec, int width, int height) {
		if (ec.getColumn() + width - 1 > view.getEditorColumnCount()
				|| ec.getRow() + height - 1 > view.getEditorRowCount()) {

			/*
			 * if the component is too large and a part of it would be out of
			 * the panel it cannot be added.
			 */
			return false;
		}
		for (int i = ec.getColumn(); i <= ec.getColumn() + width - 1; i++) {
			for (int j = ec.getRow(); j <= ec.getRow() + height - 1; j++) {
				EditableComponent c = view.getComponent(i + ":" + j);
				if (c == null || !c.isDummy()) {

					/*
					 * another component was found in range of the component to
					 * be added. The component cannot be add.
					 */
					return false;
				}
			}
		}

		/*
		 * no obstacle was found, the component can be added.
		 */
		return true;
	}

	/**
	 * creates a new "real"-component at the specified position with the
	 * specified column width and row height. This component will be visible and
	 * usable in the final form.
	 */
	public EditableComponent createComponent(int col, int row, 
			int width, int height, String className) {
		/* ======================================== */
		EditableComponent comp = null;
		try {

			/*
			 * Here an Object, extending EditableComponent is called. Which
			 * Object it is, depends on the class name.
			 * 
			 * The package name is predefined, only the class name (e.g.
			 * EdiableLabel) has to be added
			 */
			Class<?> objClass = Class.forName(className, true,
					Main.class.getClassLoader());
			comp = (EditableComponent) objClass.newInstance();
			comp.init(new CellConstraints(col, row, width, height), this, FormModel.TYPE_EDITOR);

			/*
			 * after the component is initialized, the default options can be
			 * set on the component
			 */
//			comp.setOpaque(true);
//			((JPanel) comp).setForeground(Color.white);
			// comp.getComponent().setBackground(Color.white);
			comp.setName(col + ":" + row);
		} catch (ClassNotFoundException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		} catch (InstantiationException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		} catch (IllegalAccessException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		return comp;
		/* ======================================== */
	}

	/**
	 * Creates a new Tab with the components and layout with the current editor.
	 * Here the user can have a preview on what he created. This preview has the
	 * whole functionality as the final tab.
	 */
	public void addPreviewTab() 
	{
		try {
			updateFormModel();
			Document 		doc 			= formModel.convertModelToXML();
			XMLOutputter 	outputter 		= new XMLOutputter(Format.getPrettyFormat());
			FormModel 		previewModel 	= new FormModel(FormModel.TYPE_PREVIEW);
			FormTemplate 	template 		= new FormTemplate();
			
			previewModel.convertXMLToModel(doc, this);
			template.setXml(outputter.outputString(doc));
			previewModel.setTemplate(template);
			previewModel.setForm(formModel.getForm());
			previewModel.load(this, true);
			
			addPreviewTab(previewModel);
		}
		catch (Exception e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
	}

	public void addPreviewTab(FormModel previewModel) 
	{
		/*
		 * transfer the current editor-layout to the preview
		 */
		JScrollPane previewPnl = previewModel.getView();
		previewPnl.getVerticalScrollBar().setUnitIncrement(16);

		/*
		 * enlarge the TabbedPane with a new tab
		 */
		JTabbedPane tabbedPane = view.getTabbedPane();
		tabbedPane.add(previewPnl);
		previews.put(previewPnl, previewModel);
		int size = tabbedPane.getTabCount();
		if (size == 3) 
			previewNo = 1;

		tabbedPane.setSelectedIndex(size - 1);

		JLabel tabLabel = new JLabel(
				Translatrix.getTranslationString("formeditor.preview") + " "
						+ previewNo++, IconFetcher.getMiniIcon(
						GECAMedIconNames.class, GECAMedIconNames.TEMPLATE),
				JLabel.CENTER);
		tabbedPane.setTabComponentAt(size - 1, tabLabel);

		tabLabel.addMouseListener(new FormEditorPopupController(this,
				tabbedPane, tabbedPane.getComponentAt(size - 1)));

		// previewModel.setContextId((-1) * (size-1));
		previewModel.executeInitMethod();
		formModel.getView().validate();
	}
	
	
	private void closePreviewTab (int index)
	{
		Component 	previewComponent 	= view.getTabbedPane().getComponentAt(index);
		FormModel 	previewModel 		= previews.get(previewComponent);
		
		previewModel.close();
		view.getTabbedPane().removeTabAt(index);
	}

	/**
	 * This method closes the currently selected preview tab
	 */
	public void closePreviewTab () 
	{
		closePreviewTab(view.getTabbedPane().getSelectedIndex());
	}

	/**
	 * Closes all preview tabs
	 */
	public void closeAllPreviewTabs() {
		JTabbedPane tabs = view.getTabbedPane();
		while (tabs.getTabCount() > 2) 
			closePreviewTab(2);
	}

	/**
	 * Updates the form- and script-editor by changing the reference of the
	 * currently selected component to the component in the parameter
	 * 
	 * @param component
	 *            the new currently selected component
	 * @param compCountChanged
	 *            true, if a new component was added
	 */
	public void update(EditableComponent component, boolean compCountChanged) 
	{
		if (scriptEditorModel != null
				&& scriptEditorModel.getView().isVisible()) 
		{
			// if the scriptEditor exists, it has to be informed about the
			// change
			if (compCountChanged) 
				scriptEditorModel.changeScripts(ScriptEditorModel.COMPONENT_KEYS);

			/*
			 * update the ComboBox of the script editor and with that also the
			 * current component or just the current component
			 */
			if ((component != null && component.isRealComponent() && !component
					.equals(curSelComp))
					|| (component == null && editableComponents.isEmpty())) 
				 scriptEditorModel.getView().selectInComponentBox(component);
			else setCurrentlySelectedComponent(component);
		} 
		else setCurrentlySelectedComponent(component);
	}

	public void updateKeys() {
		if (scriptEditorModel != null) {
			EditableComponent ec = this.getCurrentlySelectedComponent();
			scriptEditorModel.changeScripts(ScriptEditorModel.COMPONENT_KEYS);
			this.update(ec, false);
		}
	}

	/**
	 * creates a template from the model, given in the parameter and loads it
	 * into the editor.
	 * 
	 * @param formModel
	 *            = the loaded or imported FormModel, with the information about
	 *            the template
	 */
	public void loadTemplate(FormModel model) {
		/* ======================================== */
		// reset the map and the panel
		if (formModel != model)
		{
			formModel.close();
			formModel = model;
		}

		// calculate the column- and row-count of the editor
		String[] colSpec = formModel.getColSpec().split(",");
		String[] rowSpec = formModel.getRowSpec().split(",");
		
//		for (EditableComponent ec : view.getEditPanelComponents()) {
//			this.removeComponent(ec, false);
//		}
		
		view.installSpinnerListeners(false);
		view.setEditorColumnCount(colSpec.length);
		view.setEditorRowCount(rowSpec.length);
		view.installSpinnerListeners(true);
		
		view.setFormLayoutDefinition(formModel.getColSpec(), formModel.getRowSpec());
		view.rearrangeEditPanel();

		// add the loaded components
		for (EditableComponent component : formModel.getEditableComponents()
				.values()) {
			addComponent(component);
		}

		// set the script-functions and the init-script
		scriptFunctions = formModel.getFunctionMap();
		initScript = formModel.getInitScript();

		/*
		 * define the form-layout
		 */
		// make the changes visible
//		view.rearrangeEditPanel();
		view.rearrangeSettingsPanel();

		view.updateTitle(formModel.getName());
		view.setEditPanelBackgroundSettings();
		/* ======================================== */
	}

	
	public void chooseTemplate(Integer templateId) {
		chooseTemplate(templateId, true);
	}
	
	/**
	 * creates and opens a dialog with a JList, containing the available
	 * templates in the DB
	 */
	public void chooseTemplate(final Integer templateId, final boolean askToLoadIfModified) {
		/* ======================================== */
		// LoadTemplateDialog dialog = new LoadTemplateDialog(this);
		// Integer templateId = dialog.show();
		// --> loading dialog ended (because of modality)

		// if the element is null, the canceled-button was pressed
		if (templateId == null) {
			return;
		}

		new Thread () {
			public void run() 
			{
				// is an unsaved template already opened
				if (askToLoadIfModified && view.isModified()) 
				{
					// ask to close it or to cancel
					int buttonOption = GECAMedBaseDialogImpl.showMessageDialog(view,
							Translatrix.getTranslationString("formeditor.open_and_reset_title"),
							Translatrix.getTranslationString("formeditor.open_and_reset_message"),
							GECAMedBaseDialogImpl.OK_CANCEL_BUTTON_MODE);
		
					if (buttonOption != GECAMedBaseDialog.OK_OPTION) 
						return;
				}
				
				view.showMenuPanel(false);
				
				// change the cursor to wait-cursor, while loading
		//		Cursor defaultCursor = this.view.getCursor();
				try {
					// show that GECAMed is loading
					view.mainFrame.setWaitCursor(true);
					view.mainFrame.statusBar.setProgress(
							Translatrix.getTranslationString("formeditor.loadingTemplate"),
							StatusBar.PROGRESS_INDETERMINATE);
					
					view.clearMapAndPanel();
					
					// else an element was chosen and the OK-button was pressed
					// load the FormModel by the id
					if (formModel != null)
						formModel.close();
					formModel = new FormModel(FormModel.TYPE_EDITOR);
					formModel.loadTemplate((int) templateId, FormEditorModel.this);
					
					// load the information of the FormModel into this module
					loadTemplate(formModel);
					
					setBackground(formModel.getPanel().getBackgroundImageBytes());
					
					view.setEditPanelBackgroundSettings();
					
					view.rearrangeEditPanel();
					
					view.getTabbedPane().setSelectedIndex(FormEditorModule.EDITOR_TAB);
					
					view.setModified(false);
				} 
				finally 
				{
					// loading ended
					view.showMenuPanel(true);
					view.mainFrame.setWaitCursor(false);
					view.mainFrame.statusBar.setProgress("", 0);
				}
			}
		}.start();
		/* ======================================== */
	}

	/**
	 * creates a FormModel of the data of the current FormEditorModule and
	 * assigns a name to it, if it hasn't got one yet
	 */
	public void saveTemplate() 
	{
		updateFormModel();
		formModel.saveTemplate(formModel.getId());
		view.updateTitle(formModel.getName());
		view.setModified(false);
		updateTemplateListModel();
	}

	public void importTemplate(FormTemplate template) 
	{
		/* ********** IMPORT ********** */
		// import an xml file, make it a FormModel and load it into the current
		// Formeditor
		FormModel formModel;
		JFileChooser fileChooser = getFileChooser();
		
		File file;
		
		if (template != null) {
			// take the name of the template as default file name
			String path = fileChooser.getCurrentDirectory().getPath()
					+ FormWidgets.FOLDER_SEPARATOR
					+ template.getName() + ".xml";
			
			file = new File(path);
			
			fileChooser.setSelectedFile(file);
		} else {
			fileChooser.setSelectedFile(null);
		}
		
		if (fileChooser.showOpenDialog(view) != JFileChooser.APPROVE_OPTION) {
			// import canceled while choosing a file to import
			return;
		}
		
		file = fileChooser.getSelectedFile();
		
		if (!file.exists()) {
			// import canceled, because the chosen file doesn't exist
			GECAMedBaseDialogImpl.showMessageDialog(view, 
					Translatrix.getTranslationString("formeditor.fileDoesntExist_title"), 
					Translatrix.getTranslationString("formeditor.fileDoesntExist_msg"), 
					GECAMedBaseDialog.OK_BUTTON_MODE);
			
			return;
		}

		// view.clearMapAndPanel();
		
		// try to import the XML-file as template
		try {
			formModel	= new FormModel(FormModel.TYPE_EDITOR);
			if (!formModel.importTemplate(file, this))
				return;
		}
		catch (Exception e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
			// import canceled, because an error occurred while trying to create a FormModel
			return;
		}

		boolean reloadOpenedTemplate = false;
		if (template != null) {
			/* ---------------------------------------- */
			// advice the user that old template will be lost
			int buttonMode = GECAMedBaseDialogImpl.showMessageDialog(view, 
					Translatrix.getTranslationString("formeditor.overwriteWithImports_title"), 
					Translatrix.getTranslationString("formeditor.overwriteWithImports_msg"), 
					GECAMedBaseDialogImpl.OK_CANCEL_BUTTON_MODE);
			
			if (buttonMode == GECAMedBaseDialog.CANCEL_OPTION) {
				// import canceled
				return;
			}
			
			if (this.formModel != null
					&& this.formModel.getTemplate() != null
					&& this.formModel.getTemplate().getId().equals(template.getId())) {
				
				if (view.isModified()) {
					// only ask, if the model is modified, otherwise it is not necessary
					
					// The template to overwrite is opened. Ask to reload or cancel it.
					buttonMode = GECAMedBaseDialogImpl.showMessageDialog(view, 
							Translatrix.getTranslationString("formeditor.reloadCurrentTemplate_title"), 
							Translatrix.getTranslationString("formeditor.reloadCurrentTemplate_msg"), 
							GECAMedBaseDialogImpl.OK_CANCEL_BUTTON_MODE);
					
					if (buttonMode == GECAMedBaseDialog.CANCEL_OPTION) {
						// import canceled
						return;
					}
				}
				
				// reload the opened template
				reloadOpenedTemplate = true;
			}
			
			formModel.setTemplate(template);
			formModel.setName(template.getName());
			formModel.setVersion(template.getVersion());
			formModel.setDescription(template.getDescription());
			/* ---------------------------------------- */
		}

		// check name
		// if (!isValidFormTemplateName(formModel.getName())) {
		/* ---------------------------------------- */
		formModel.setName(getValidFormTemplateName(formModel.getName(),
				template != null ? template.getName() : null));

		if (formModel.getName() == null) {
			// import canceled while entering a valid name
			return;
		}
		/* ---------------------------------------- */
		// }
		
		try {
//			formModel.saveTemplate(formModel.getId());
			formModel.saveTemplate(template == null ? null : template.getId());
			
			MainFrame.getInstance().showMessage(
					Translatrix.getTranslationString("formeditor.import_successful"));
			
			if (reloadOpenedTemplate) {
				this.chooseTemplate(formModel.getTemplate().getId(), false);
			}
		} catch (Exception e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		/* ---------------------------------------- */
		/* **************************************** */
	}

	public void exportTemplate(FormTemplate template) {
		/* ********** EXPORT ********** */
		JFileChooser fileChooser = getFileChooser();
		
		String path = fileChooser.getCurrentDirectory().getPath() 
				+ FormWidgets.FOLDER_SEPARATOR 
				+ template.getName() + ".xml";
		
		fileChooser.setSelectedFile(new File(path));
		
		if (fileChooser.showSaveDialog(view) == JFileChooser.APPROVE_OPTION) {
			File file = fileChooser.getSelectedFile();

			FormModel formModel = new FormModel(FormModel.TYPE_EDITOR);
			formModel.loadTemplate(template, this, false);
			formModel.exportTemplate(file);
			
			MainFrame.getInstance().showMessage(
					Translatrix.getTranslationString("formeditor.export_successful"));
		}
		/* **************************************** */
	}

	/***************************************************
	 ****************** HELP METHODS: ******************
	 **************************************************/

	/**
	 * creates and returns a border element at the specified coordinates (col
	 * and row)
	 */
	public EditableComponent createBorderBarElement(int col, int row) {
		EditableComponent border = null;
		border = new BorderComponent();
		border.init(new CellConstraints(col, row, CellConstraints.FILL,
				CellConstraints.FILL), this, FormModel.TYPE_EDITOR);
		return border;
	}

	/**
	 * creates and returns a dummy component at the specified coordinates (col
	 * and row)
	 */
	public EditableComponent createDummy(int col, int row) 
	{
		EditableLabel dummy = new DummyComponent();
		dummy.init(new CellConstraints(col, row, CellConstraints.FILL,
				CellConstraints.FILL), this, FormModel.TYPE_EDITOR);
		return dummy;
	}

	private static JFileChooser fileChooser;

	public static JFileChooser getFileChooser() {
		if (fileChooser == null) {
			/* ---------------------------------------- */
			fileChooser = new JFileChooser();

			/*
			 * defines which file are shown in the JFileChooser
			 */
			FileFilter filter = new FileFilter() {
				@Override
				public String getDescription() {
					return ".xml";
				}

				@Override
				public boolean accept(File f) {
					return f.getName().endsWith(".xml") || f.isDirectory();
				}
			};
			fileChooser.setFileFilter(filter);
			/* ---------------------------------------- */
		}

		return fileChooser;
	}

	/****************************************************
	 **************** GETTER AND SETTER: ****************
	 ***************************************************/

	/**
	 * Set and mark a clicked component as the currently selected component. The
	 * component will be marked in a color and the settings panel will show the
	 * possible settings
	 */
	public void setCurrentlySelectedComponent(EditableComponent selectedComponent) {
		if (this.curSelComp != null) {
			// if the component referenced to another component, set this
			// component as not selected anymore
			this.curSelComp.setSelected(false);
		}
		
		this.curSelComp = selectedComponent;
		
		if (this.curSelComp != null) {
			// if the component references another component, set this component
			// as currently selected
			this.curSelComp.setSelected(true);
		}
		
		view.rearrangeSettingsPanel();
	}

	/**
	 * returns the currently selected component
	 */
	public EditableComponent getCurrentlySelectedComponent() {
		return curSelComp;
	}

	public EditorPanelController getEditorPanelController() {
		return editorCtrl;
	}

	public Map<String, EditableComponent> getEditableComponents() {
		return editableComponents;
	}

	public void setInitScript(String initScript) {
		this.initScript = initScript;
	}

	public String getInitScript() {
		return initScript;
	}

	public void setScriptFunctions(HashMap<String, String> scriptFunctions) {
		this.scriptFunctions = scriptFunctions;
	}

	public Map<String, String> getScriptFunctions() {
		return scriptFunctions;
	}

	public void updateFormModel() 
	{
		if (formModel == null) {
			formModel = new FormModel(FormModel.TYPE_EDITOR);
		} 
		formModel.changeModel(
				view.createEditPanelColSpecs(view.getEditorColumnCount()),
				view.createEditPanelRowSpecs(view.getEditorRowCount()),
				view.getEditPanel().getBackgroundImageBytes(),
				editableComponents, initScript, scriptFunctions);
	}
	

	public FormModel getFormModel() {
		return formModel;
	}

	public void setBackground(byte[] img) {
		/* ======================================== */
		view.getEditPanel().setBackgroundImage(img);
		view.repaintEditPanel();

		view.setEditPanelBackgroundSettings();
		
		if (formModel != null) {
			formModel.getPanel().setBackgroundImage(img);
		}
		
		view.enableRemoveBackgroundButton(img != null);
		/* ======================================== */
	}

	public void destructFormModel() {
		if (formModel != null)
		{
			formModel.close();
			formModel = null;
		}
	}

	public void destructScriptEditor() {
		initScript = "";
		scriptFunctions.clear();
		if (scriptEditorModel == null) {
			return;
		}
		scriptEditorModel.getView().closeWithoutSaving();
		scriptEditorModel = null;
	}

	public FormEditorModule getView() {
		return view;
	}

	public void openScriptEditor() {
		if (scriptEditorModel != null) {
			scriptEditorModel.openEditor();
		} else {
			scriptEditorModel = new ScriptEditorModel(this);
		}
	}

	/**
	 * Takes a Set of strings and orders
	 */
	public static ArrayList<String> orderKeys(Set<String> keys) {
		ArrayList<String> orderedKeys = new ArrayList<String>();
		for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
			String key = iter.next();
			boolean entered = false;
			for (int index = 0; !entered && index < orderedKeys.size(); index++) {
				if (key.toUpperCase().compareTo(
						orderedKeys.get(index).toUpperCase()) < 0) {
					entered = true;
					orderedKeys.add(index, key);
				}
			}
			if (!entered) {
				orderedKeys.add(key);
			}
		}
		return orderedKeys;
	}

	public boolean deleteSelectedFormTemplate() 
	{
		FormTemplate template = (FormTemplate) view.getTemplateList()
				.getSelectedValue();

		FormManager manager = (FormManager) ManagerFactory
				.getRemote(FormManagerImpl.class);

		// delete the related forms with its IncidentEntries
		List<Patient> patients = manager
				.getPatientsWithFormsOfTemplate(template.getId());

		if (patients.isEmpty()) 
		{
			int buttonPressed = GECAMedBaseDialogImpl.showMessageDialog(
					view,
					Translatrix.getTranslationString("formeditor.form.delete_template_title"),
					Translatrix.getTranslationString("formeditor.form.delete_template"),
					GECAMedBaseDialogImpl.YES_NO_BUTTON_MODE);

			if (buttonPressed == GECAMedBaseDialog.YES_OPTION) 
			{
				manager.deleteTemplate(template.getId());
				updateTemplateListModel();
				closeEditor();
				return true;
			}
		} 
		else 
		{
			StringBuffer patientList = new StringBuffer();

			patientList.append(Translatrix.getTranslationString(
					"formeditor.cannot_delete_template_msg"));
			patientList.append("<br/><br/>");

			for (Patient p : patients) 
			{
				patientList.append("    -  ");
				patientList.append(p.getSurName());
				patientList.append(", ");
				patientList.append(p.getFirstName());
				patientList.append("<br/>");
			}

			GECAMedBaseDialogImpl.showMessageDialog(
					view, 
					Translatrix.getTranslationString("formeditor.cannot_delete_template_title"),
					patientList.toString(), 
					GECAMedBaseDialogImpl.OK_BUTTON_MODE);
		}

		return false;
	}

	public void updateTemplateListModel() 
	{
		FormTemplate selectedTemplate = (FormTemplate) view.getTemplateList().getSelectedValue();
		
		FormManager manager = (FormManager) ManagerFactory
				.getRemote(FormManagerImpl.class);
		Collection<FormTemplate> templates = manager.getAllTemplates();

		DefaultListModel listModel = getTemplateListModel();
		listModel.clear();
		for (FormTemplate template : templates)
			getTemplateListModel().addElement(template);
		
		view.getTemplateList().setSelectedValue(selectedTemplate, true);
		view.validate();
	}

	public DefaultListModel getTemplateListModel() 
	{
		if (templateListModel == null) 
		{
			templateListModel = new DefaultListModel();
//			updateTemplateListModel();
		}
		
		return templateListModel;
	}

	/**
	 * Opens a Dialog to ask the user, to enter a name for the template. This
	 * name is checked, whether it's valid and returned, if it's valid or ask to
	 * enter another name or cancel, if it's not valid.
	 * 
	 * @param name
	 *            the name given at start
	 * @return a valid name for a template or <code>null</code>, if the user
	 *         canceled the dialog.
	 */
	public String getValidFormTemplateName(String name) {
		return getValidFormTemplateName(name, null);
	}

	/**
	 * Opens a Dialog to ask the user, to enter a name for the template. This
	 * name is checked, whether it's valid and returned, if it's valid or ask to
	 * enter another name or cancel, if it's not valid.
	 * 
	 * @param newName
	 *            the name given at start
	 * @param oldName
	 *            the former name of the template, if existing, else
	 *            <code>null</code>
	 * @return a valid name for a template or <code>null</code>, if the user
	 *         canceled the dialog.
	 */
	public String getValidFormTemplateName(String newName, String oldName) {
		/* ======================================== */
		boolean valid = isValidFormTemplateName(newName, oldName);
		while (!valid) {
			/* ---------------------------------------- */
			newName = GECAMedBaseDialogImpl.showInputMessageDialog(
					view,
					Translatrix.getTranslationString("formeditor.enter_template_name_title"),
					Translatrix.getTranslationString("formeditor.enter_template_name"),
					newName);

			if (newName == null) {
				return null;
			}

			valid = isValidFormTemplateName(newName, oldName);

			if (!valid) {
				GECAMedBaseDialogImpl.showMessageDialog(view,
						Translatrix.getTranslationString("formeditor.invalid_template_name_title"),
						Translatrix.getTranslationString("formeditor.invalid_template_name_msg"),
						GECAMedBaseDialogImpl.OK_BUTTON_MODE);
			}
			/* ---------------------------------------- */
		}

		return newName;
		/* ======================================== */
	}

	public boolean isValidFormTemplateName(String name) {
		return isValidFormTemplateName(name, null);
	}

	public boolean isValidFormTemplateName(String newName, String oldName) {
		/* ======================================== */
		if (newName == null || newName.equals("")) {
			/* ---------------------------------------- */
			return false;
			/* ---------------------------------------- */
		}

		if (oldName != null && oldName.equals(newName)) {
			return true;
		}

		DefaultListModel model = this.getTemplateListModel();
		FormTemplate template;
		for (int i = 0; i < model.getSize(); i++) 
		{
			template = (FormTemplate) model.getElementAt(i);
			if (template.getName().toUpperCase().equals(newName.toUpperCase())) {
				return false;
			}
		}

		return true;
		/* ======================================== */
	}

	public void closeEditor() 
	{
		if (formModel != null)
			formModel.close();
		formModel = null;
		view.getEditPanel().removeAll();
		view.updateTitle("");
		view.setModified(false);
	}
	
	
	public SelectFormDialog getSelectFormDialog ()
	{
		if (selectFormDialog == null)
			selectFormDialog = new SelectFormDialog(this);
		return selectFormDialog;
	}
}
