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

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.CaretEvent;
import javax.swing.event.ChangeEvent;

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.formeditor.gui.FormEditorModule;
import lu.tudor.santec.gecamed.formeditor.gui.component.EditableComponent;
import lu.tudor.santec.gecamed.formeditor.gui.component.ScriptLink;
import lu.tudor.santec.gecamed.formeditor.gui.controller.ComponentSettingsController;
import lu.tudor.santec.gecamed.formeditor.gui.controller.FormController;
import lu.tudor.santec.gecamed.formeditor.gui.controller.ScriptEditorController;
import lu.tudor.santec.gecamed.formeditor.gui.view.ScriptEditorView;
import lu.tudor.santec.i18n.Translatrix;
import lu.tudor.santec.org.fife.ui.autocomplete.DefaultCompletionProvider;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

@SuppressWarnings("unchecked")
public class ScriptEditorModel {
	
	/**
	 * @author Jens Ferring
	 * 
	 * This model contains the data and important methods for the script-editor.
	 */
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(ScriptEditorModel.class.getName());
	
	
	private static String TEMPLATE_FUNCTION 	= "function";
	private static String TEMPLATE_FOR 			= "for";
	private static String TEMPLATE_FOREACH 		= "foreach";
	private static String TEMPLATE_WHILE 		= "while";
	private static String TEMPLATE_IF 			= "if";
	private static String TEMPLATE_INSTANCEOF 	= "instanceof";
	private static String TEMPLATE_TRY_CATCH 	= "try";
	private static String TEMPLATE_PRINT 		= "println";

//	private static String TEMPLATE_ = "";
//	private static String TEMPLATE_ = "";
//	private static String TEMPLATE_ = "";
//	private static String TEMPLATE_ = "";
	private static String[] SCRIPT_TEMPLATES = {TEMPLATE_FOR, TEMPLATE_FOREACH, TEMPLATE_FUNCTION, 
												TEMPLATE_IF, TEMPLATE_INSTANCEOF, TEMPLATE_PRINT, 
												TEMPLATE_TRY_CATCH, TEMPLATE_WHILE};
	
	private static final Properties scriptTemplates = loadScriptTemplates();
	
	/*
	 * These are the sizes and types (= the position in the array) of the ArrayLists
	 * containing the ScriptLinks and components
	 */
	public static final int SCRIPT_SIZE 		= 5;
	public static final int SCRIPT_SIZE_WEST 	= 3;
	public static final int INIT_SCRIPT 		= 0;
	public static final int LISTENER_METHOD 	= 1;
	public static final int FUNCTION 			= 2;
	public static final int COMPONENT_KEYS 		= 3;
	public static final int FUNCTION_SHORTCUT 	= 4;
	
	private static final Icon AC_ICON_COMPONENT = IconFetcher.getMiniIcon(FormEditorModule.class, "key.png");
	private static final Icon AC_ICON_CONSTANT 	= IconFetcher.getMiniIcon(FormEditorModule.class, "constant.png");
	private static final Icon AC_ICON_FUNCTION 	= IconFetcher.getMiniIcon(FormEditorModule.class, "function.png");
	private static final Icon AC_ICON_TEMPLATE 	= IconFetcher.getMiniIcon(FormEditorModule.class, "new.png");
	
	// contains all components in the west and east  
	private ArrayList<JComponent>[] scripts = new ArrayList[SCRIPT_SIZE];
	
	// the currently selected link
	private ScriptLink curSelLink;
	
	// the from the global model currently selected link
	private EditableComponent curSelComp;
	
	// this is the form-editor-model, where the data is saved
	private FormEditorModel globalModel;
	
	// this is the view component
	private ScriptEditorView view;
	
	// the controller of the script-editor, listens to all buttons except the method-links 
	private ScriptEditorController controller;
	
	// checks whether the view component is initialized or not
	private boolean editorIsInitilized = false;
	
	/* If a script-link was clicked, but the old hasn't been saved, a dialog
	 * asks the user, whether he likes to save or doesn't or wants to cancel
	 * and go back to the old script. If the user wants to cancel, this value
	 * is set to true (so the update won't be performed again) 
	 */
	private boolean reselectLastComponent = false;
	
	/*
	 * If something must be changed in the comboBox, but the comboBox shouldn't call an update, yet, this 
	 * semaphore is increased. Everytime an update should be called, the semaphore is decreased. If the 
	 * semaphore cannot be decreased, because it would be less than 0, an update can be done, otherwise not.
	 */
//	private int dontUpdateCounter = 0;
	
//	private JPopupMenu rightClickMenu;
	
	private DefaultCompletionProvider provider;
	
	public ScriptEditorModel (FormEditorModel model) {
		/* ======================================== */
		globalModel = model;
		for (int i = 0; i < scripts.length; i++) {
			scripts[i] = new ArrayList<JComponent>();
		}
		
		if (model.getCurrentlySelectedComponent() != null
				&& model.getCurrentlySelectedComponent().isRealComponent()) {
			curSelComp = model.getCurrentlySelectedComponent();
		} else {
			// the selected Component isn't a "real"-component, but a dummy- or border-component -> select any component
			Iterator<EditableComponent> iter = model.getEditableComponents().values().iterator();
			if (iter.hasNext()) {
				curSelComp = iter.next();
			}
		}
		
		provider = new DefaultCompletionProvider();
		provider.setListCellRenderer(IconCompletion.getRenderer());
		
		view = new ScriptEditorView(MainFrame.getInstance(), this);
		controller = new ScriptEditorController(this);
		view.init();
		editorIsInitilized = true;
		view.selectInComponentBox(curSelComp);
		view.setVisible(true);
		/* ======================================== */
	}
	
	/**
	 * Change or update the list of scripts for the specified type
	 */
	public void changeScripts (int type) {	
		scripts[type].clear();
		if (type == INIT_SCRIPT) {
			/*
			 * add the INIT-SCRIPT
			 */
			ScriptLink l = new ScriptLink(Translatrix.getTranslationString("formeditor.init_method"), globalModel.getInitScript(), type, provider);
			l.addActionListener(controller);
			scripts[type].add(l);
		} else if (type == LISTENER_METHOD) {
			/*
			 * add the LISTENER-METHODS for the selected component
			 */
			EditableComponent c = curSelComp; // globalModel.getCurrentlySelectedComponent();
			if (c != null 
					&& !scripts[COMPONENT_KEYS].isEmpty()
					&& c.isRealComponent()) {
				ArrayList<JComponent> listenerMethods = scripts[type];
				
				for (int index = 0; index < c.getFormulaSize(); index++) {
					ScriptLink l = new ScriptLink(c.getMethodNameOf(index), 
							c.getScriptAt(index), type, provider);
					l.addActionListener(controller);
					listenerMethods.add(l);
//					scripts[type].add(l);
				}
			}
		} else if (type == FUNCTION) {
			/* ---------------------------------------- */
			/*
			 * add the FUNCITONS
			 */
			Map<String, String> functions = globalModel.getScriptFunctions();				
			for (Iterator<String> iter = functions.keySet().iterator(); iter.hasNext();) {
				String key = iter.next();
				ScriptLink l = new ScriptLink (key, functions.get(key), type, provider);
				l.addActionListener(controller);
				scripts[type].add(l);
//				l.addMouseListener(controller);
//				l.setTransferHandler(new TransferHandler("text"));
			}
			/* ---------------------------------------- */
		} else if (type == COMPONENT_KEYS) {
			/* ---------------------------------------- */
			/*
			 * the COMPONENT-KEYS in the east-panel
			 */
			
			// the editableComponent-keys are entered into the panel
//			if (!view.isComponentBoxEmpty()) {
//				dontUpdate();
//			}
			
			EditableComponent oldCurSelComp = curSelComp;
			
			view.removeAllFromComponentBox();
			EditableComponent[] components = (EditableComponent[]) getComponents().toArray(new EditableComponent[0]);
			for (EditableComponent ec : components) { //int i = 0; i < components.length; i++) {
//				EditableComponent ec = (EditableComponent)components[i];
				if (ec != null) {
					// this is the text, that will be entered if the label is dropped into the editor
					String keyText = ec.getKey();
					JLabel l = new JLabel(keyText);
//					l.addMouseListener(controller);
//					l.setTransferHandler(new TransferHandler("text"));
//					/* 
//					 * a new panel is created, where the key and a description of the element, the key belongs 
//					 * to, are add. This panel will be add to the available components panel, which will be add
//					 * to the east panel later on. 
//					 */
////					JPanel p = new JPanel (new FormLayout("left:p:grow, right:p", "center:p"));
////					p.add(l, new CellConstraints(1, 1));
//					l.setToolTipText(ec.getComponentType());
//	//					p.add(new JLabel("("+ec.getComponentType()+")"), new CellConstraints(2, 1));
					scripts[type].add(l);
					// flag for preventing an update by the comboBox
//					if (view.isComponentBoxEmpty()) {
//						dontUpdate();
//					}
					view.addToComponentBox(keyText);
				}
			}
			
			if (oldCurSelComp != null) {
				view.selectInComponentBox(oldCurSelComp);
			}
			
//			buildRightClickMenu();
			fillCompletionProvider();
			/* ---------------------------------------- */
		} else if (type == FUNCTION_SHORTCUT) {
			for (Iterator<String> iter = globalModel.getScriptFunctions().keySet().iterator(); iter.hasNext(); ) {
				JLabel l = new JLabel(iter.next());
//				l.setName(l.getText()+" ()");
//				l.addMouseListener(controller);
//				l.setTransferHandler(new TransferHandler("name"));
				scripts[type].add(l);
			}
//			buildRightClickMenu();
			fillCompletionProvider();
		}
		// add the components to the panel, specified by the type
		view.setScriptLinks(type);		
	}
	
	/**
	 * Called, after a link was clicked. The method deselects the former selected and 
	 * selects the consigned ScriptLink.
	 */
	public void select (ScriptLink link) {
		/* ======================================== */
		if (curSelLink != null) {
//			// if a former ScriptLink was saved, it is unselected
			curSelLink.setText(curSelLink.getKey());
//			curSelLink.setScript(editor.getScript());
		}
		curSelLink = link;
		view.setFunctionButtonsEnabled(curSelLink.getType() == FUNCTION);
		view.setEditor(curSelLink.getEditor());
		view.enableButtons(!isSaved(curSelLink));
		curSelLink.setText(">> "+curSelLink.getKey()+" <<");		
		/* ======================================== */
	}
	
	/**
	 * this method adds a new function to the ArrayLists and makes it be add to the panel
	 */
	public void addFunction () {
		String name = "";
		while (true) {
			String newName = null;
			// open a dialog, to enter the name of the function
			newName = GECAMedBaseDialogImpl.showInputMessageDialog(view, 
					Translatrix.getTranslationString("formeditor.function_name"), 
					Translatrix.getTranslationString("formeditor.enter_function_name"), 
					name);
			if (newName == null) {
				// if the name is still null, the dialog was abort
				return;
			}
			newName = newName.trim();
			/*
			 * the name must not be the empty-string and not an already existing name. If the name is
			 * not valid or already exists, an error message is shown and the dialog will be opened 
			 * again (until it is abort or the name is valid).
			 */
			name = newName;
			if (!isValidFunctionName(newName)) {
				GECAMedBaseDialogImpl.showMessageDialog(view, 
						Translatrix.getTranslationString("formeditor.function_name"), 
						Translatrix.getTranslationString("formeditor.enter_valid_name"), 
						GECAMedBaseDialogImpl.OK_BUTTON_MODE);
				newName = null;
			} else {
				break;
			}
		}
		String defaultScript = scriptTemplates.getProperty(TEMPLATE_FUNCTION).replace("NAME", name);
		globalModel.getScriptFunctions().put(name, defaultScript);
		ScriptLink l = new ScriptLink(name, defaultScript, FUNCTION, provider);
		l.addActionListener(controller);
		scripts[FUNCTION].add(l);
		view.addScriptLink(FUNCTION, l);
		changeScripts(FUNCTION_SHORTCUT);
		view.validate();
	}
	
	/**
	 * removes the currently selected function, but calls a confirm dialog first
	 */
	public void removeFunction () {
		if (GECAMedBaseDialogImpl.showMessageDialog(view, 
				Translatrix.getTranslationString("formeditor.remove_function"), 
				Translatrix.getTranslationString("formeditor.want_to_remove_function"), 
				GECAMedBaseDialogImpl.OK_CANCEL_BUTTON_MODE) == GECAMedBaseDialog.OK_OPTION) {
			scripts[FUNCTION].remove(curSelLink);
			globalModel.getScriptFunctions().remove(curSelLink.getKey());
			view.setScriptLinks(FUNCTION);
			select((ScriptLink)scripts[INIT_SCRIPT].get(0));
		}
		changeScripts(FUNCTION_SHORTCUT);
		view.validate();
	}
	
	/**
	 * renames the currently selected function. Calls a dialog, where to enter the new name
	 */
	public void renameFunction () {
		String oldName = curSelLink.getKey();
		
		// calls the dialog, where to enter the new name
		String newName = oldName;
		while (true) {
			
			newName = GECAMedBaseDialogImpl.showInputMessageDialog(view, 
					Translatrix.getTranslationString("formeditor.rename_function"), 
					Translatrix.getTranslationString("formeditor.enter_function_name"), 
					newName).trim();
			// if the return value of the dialog is null, the cancel button was pressed
			if (newName == null) {
				return;
				
			} else {				
//				oldName = newName;
				if (!isValidFunctionName(newName)) {
					GECAMedBaseDialogImpl.showMessageDialog(view, 
							Translatrix.getTranslationString("formeditor.function_name"), 
							Translatrix.getTranslationString("formeditor.enter_valid_name"), 
							GECAMedBaseDialogImpl.OK_BUTTON_MODE);
					newName = null;
					
				} else {
					break;
				}
			}	
		}
		/*
		 * the function shell be renamed. For that, the old key and the belonging object must be removed
		 * and has to be put with the new key
		 */
		globalModel.getScriptFunctions().remove(oldName);
		
		curSelLink.setKey(newName);
		curSelLink.setText(newName);
		
		/* rename the function name in its own script -
		 * 			"function <old_name> () {"
		 * becomes: "function <new_name> () {"
		 */		
		globalModel.getScriptFunctions().put(curSelLink.getKey(), curSelLink.getScript());
		curSelLink.setScript(curSelLink.getScript().replace("function "+oldName, "function "+newName));
		
		// search and replace the old function name with the new also in all the other scripts
//		for (int index = 0; index < scripts.length; index++) {
//			if (scripts[index].size() > 0 
//					&& scripts[index].get(0) instanceof ScriptLink) {
//				for (Iterator iter = scripts[index].iterator(); iter.hasNext(); ) {
//					ScriptLink link = (ScriptLink)iter.next();
//					link.setScript(link.getScript().replaceAll(" "+oldName+"\\(", " "+newName+"\\("));
//					link.setScript(link.getScript().replaceAll(" "+oldName+" \\(", " "+newName+" \\("));					
//				}
//			}
//		}
		
		changeScripts(FUNCTION_SHORTCUT);
		view.validate();
	}
		
	/**
	 * tests, if this script saved 
	 */
	public boolean isSaved(int type, int index) {
		if (type < SCRIPT_SIZE_WEST) {
			ScriptLink link = (ScriptLink)scripts[type].get(index);
			return isSaved(link);
		} else {
			return true;
		}
	}
	public boolean isSaved(ScriptLink link) {
		int type = link.getType();
		if (type == INIT_SCRIPT) {
			// the type is the one of the init-script
			return ((ScriptLink)scripts[type].get(0)).getScript().equals(globalModel.getInitScript());
		} else if (type == LISTENER_METHOD) {
			// the type is the one of a listener method
//			EditableComponent c = globalModel.getCurrentlySelectedComponent();
			int scriptIndex = getScriptIndex(link);
			if (scriptIndex < 0)
				return false;
			if (!curSelComp.getScriptAt(scriptIndex).equals(link.getScript())) {
				return false;
			}
		} else if (type == FUNCTION) {
			// the type is the one of a function
			String key = link.getKey();
			if (!link.getScript().equals((globalModel.getScriptFunctions().get(key)))) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Checks, if all scripts of this type is saved
	 */
	public boolean isSaved (int type) {
		for (int index = 0; index < curSelComp.getFormulaSize() 
				&& index < scripts[type].size(); index++) {
			if (!isSaved(type, index)) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Checks, if all scripts are saved
	 */
	public boolean isSaved () {
		for (int type = 0; type < SCRIPT_SIZE_WEST; type++) {
			if (!isSaved(type)) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Counts the number of unsaved scripts
	 * @return
	 */
	public int unsavedScripts () {
		int i = 0;
		
		for (int type = 0; type < SCRIPT_SIZE_WEST; type++) {
			if (!isSaved(type)) {
				i++;
			}
		}
		
		return i;
	}
	
	/********** SAVE METHODS **********
	 * Save the script of the type 'type' at the index 'index'
	 */
	private void saveScript (int type, int index) {
		String s = ((ScriptLink)scripts[type].get(index)).getScript();
		if (type == INIT_SCRIPT) {
			globalModel.setInitScript(s);
		} else if (type == LISTENER_METHOD) {
			globalModel.getView().getComponent(curSelComp.getName()).setFormulaAt(index, s);
		} else if (type == FUNCTION) {
			String key = ((ScriptLink)scripts[type].get(index)).getKey();
			globalModel.getScriptFunctions().put(key, s);
		}
		view.markAsSaved(type, index);
		
		globalModel.getView().setModified(true);
	}
	
	private void saveScript (int type) {
		/*
		 * save all scripts with the selected type 
		 */
		for (int index = 0; index < scripts[type].size(); index++) {
			saveScript(type, index);
		}
	}
	
	/**
	 * saves the currently selected script
	 */
	public void saveSelectedScript() {
//		curSelLink.setScript(editor.getScript());
		saveScript(curSelLink.getType(), getScriptIndex(curSelLink));
		view.enableButtons(false);
	}
	
	/**
	 * saves all scripts
	 */
	public void saveAllScripts () {
		/*
		 * Save all scripts for all types 
		 */
		for (int type = 0; type < SCRIPT_SIZE_WEST; type++) {
			for (int index = 0; index < scripts[type].size(); index++) {
				saveScript(type, index);
			}
		}
	}
	
	/********** RESET METHODS **********
	 * Reset the script of the type 'type' at the index 'index'
	 */
	private void resetScript (int type, int index) {
		if (type == INIT_SCRIPT) {
			((ScriptLink)scripts[type].get(index)).setScript(globalModel.getInitScript());
		} else if (type == LISTENER_METHOD) {
			((ScriptLink)scripts[type].get(index)).setScript(globalModel.getView().getComponent(curSelComp.getName()).getScriptAt(index));
		} else if (type == FUNCTION) {
			String key = ((ScriptLink)scripts[type].get(index)).getKey();
			((ScriptLink)scripts[type].get(index)).setScript(globalModel.getScriptFunctions().get(key));
		}
		view.markAsSaved(type, index);
	}
	
	/**
	 * resets the currently selected script
	 */
	public void resetSelectedScript () {
		resetScript(curSelLink.getType(), getScriptIndex(curSelLink));
//		editor.setScript(curSelLink.getScript());
	}
	
	/**
	 * resets all scripts
	 */
	public void resetAllScripts () {
		for (int type = 0; type < SCRIPT_SIZE_WEST; type++) {
			for (int index = 0; index < scripts[type].size(); index++) {
				resetScript(type, index);
			}
		}
//		editor.setScript(curSelLink.getScript());
	}
	
//	public void selectItemFromComboBox (String compenentKey) {
//		if (editorIsInitilized && !reselectLastComponent) {
//			EditableComponent ec = globalModel.getEditableComponents().get(compenentKey); 
//			if (ec == null) {
//				ec = globalModel.getCurrentlySelectedComponent();
//			}
//			update(ec);
//		} else {
//			reselectLastComponent = false;
//		}
//	}
	
	
//	public void selectItemByMouseClick (EditableComponent component) {
//		if ((component != null
//					&& component.isRealComponent() 
//					&& !component.equals(curSelComp))
//				|| (component == null 
//					&& globalModel.getEditableComponents().size() == 0)) {
//			view.selectInComponentBox(component);
//		} else {
//			globalModel.setCurrentlySelectedComponent(component);
//		}
//	}	
	
	
	
	/**
	 * is called, after an EditableComponent is added or removed or another component is selected
	 */
	public void update (String componentKey) {
		/* ======================================== */
		if (!editorIsInitilized && reselectLastComponent) {
			/* ---------------------------------------- */
			reselectLastComponent = false;
			return;
			/* ---------------------------------------- */
		}
		
		EditableComponent c = globalModel.getEditableComponents().get(componentKey); 
		if (c == null) {
			c = globalModel.getCurrentlySelectedComponent();
		}
		
		/*
		 * If there are no keys, it has to be updated, to update the listener method if a component was removed.
		 * If curSelComp is null, it has to be updated, to update the listener method if the first component was added
		 * If the keys aren't empty and and the current selected component is not null
		 * 	the component was reset to the old selection by the ScriptEditorModel
		 * 	or it is not a real component selected
		 * 	or the selected component is the same as already selected, 
		 * the update can be stopped at this place.
		 */
		if (!scripts[COMPONENT_KEYS].isEmpty()
				&& curSelComp != null
				&& (!c.isRealComponent()
					|| (curSelComp != null 
						&& c.getName().equals(curSelComp.getName())  ))) {
			/*
			 * Not all methods had been saved and in the "want to change"-dialog cancel was pressed.
			 * The old component is reselected and it must not be made an update of the script-editor,
			 * because otherwise the scripts will be reset and the changes are lost  
			 */
			globalModel.setCurrentlySelectedComponent(globalModel.getView().getComponent(c.getName()));
			view.validate();
			return;
		} 
		
		/* 
		 * If there is a unsaved script, the component is changed and if the component with unsaved 
		 * script was not removed call a dialog to ask whether the script shell be saved, not saved 
		 * or the last component shell be selected again.
		 */
		if (c != null
				&& c.isRealComponent()
				&& curSelComp != null 
				&& globalModel.getEditableComponents().containsKey(curSelComp.getKey())
				&& !isSaved(LISTENER_METHOD)) {
			/* ---------------------------------------- */
			int yesNoCancel = GECAMedBaseDialog.NO_OPTION;
			
			yesNoCancel = GECAMedBaseDialogImpl.showMessageDialog(view, 
					Translatrix.getTranslationString("formeditor.save_changes_title"), 
					Translatrix.getTranslationString("formeditor.save_changes"), 
					GECAMedBaseDialogImpl.YES_NO_CANCEL_BUTTON_MODE);
			
			if (yesNoCancel == GECAMedBaseDialog.YES_OPTION) {
				this.saveScript(LISTENER_METHOD);
			} else if (yesNoCancel == GECAMedBaseDialog.CANCEL_OPTION) {
				/*
				 * JOptionPane was canceled. The old component will be selected again.
				 */
				reselectLastComponent = true;
				
//				view.selectInComponentBox(curSelComp);
//				globalModel.setCurrentlySelectedComponent(curSelComp);
				globalModel.update(curSelComp, false);
				return;
			}
			
			// if the "no"-button was pressed, proceed
			/* ---------------------------------------- */
		}
		globalModel.setCurrentlySelectedComponent(c);
		/*
		 * If the component was reset, don't set component c to currently selected component
		 * and don't change the selected method.
		 */
		if (scripts[COMPONENT_KEYS].isEmpty()) {
			curSelComp = null;
		} else if (c.isRealComponent()) {
			curSelComp = c;
		}
		changeScripts(LISTENER_METHOD);
		select((ScriptLink)scripts[INIT_SCRIPT].get(0));
		view.validate();
	}
	
	/**
	 * Makes the editor be shown again, after it was hidden
	 */
	public void openEditor () {
		curSelComp = globalModel.getCurrentlySelectedComponent();
		view.setVisible(true);
		view.selectInComponentBox(curSelComp);
//		globalModel.update(curSelComp, false);
		changeScripts(LISTENER_METHOD);
		changeScripts(COMPONENT_KEYS);
		globalModel.getView().repaint();
	}
	
	
	/*************************************************
	 *************** GETTER AND SETTER ***************
	 ************************************************/
	
//	public void dontUpdate () {
//		dontUpdateCounter++;
//	}
//	public boolean canUpdate () {
//		if (dontUpdateCounter > 0) {
//			dontUpdateCounter--;
//			return false;
//		} else {
//			return true;
//		}
//	}
	
	public JComponent getScriptAt (int type, int index) {
		return scripts[type].get(index);
	}
	public ScriptLink getCurrentlySelectedLink () {
		return curSelLink;
	}
	public ArrayList<JComponent> getScriptAt (int type) {
		return scripts[type];
	}	
	public Collection<EditableComponent> getComponents () {
		return globalModel.getEditableComponents().values();
	}
	public int getScriptIndex (ScriptLink l) {
		return scripts[l.getType()].indexOf(l);
	}
	public ScriptEditorView getView () {
		return view;
	}

	public void setExtendedModus(boolean extendedModus) {
		view.setScriptLinks(LISTENER_METHOD);
		view.validate();
	}

	public boolean isExtendedModus() {
		return view.isExtendedModus();
	}
	
	public Collection<String> getFunctionNames () {
		/* ---------------------------------------- */
		return globalModel.getScriptFunctions().keySet();
		/* ---------------------------------------- */
	}
	
	public FormEditorModel getGlobalModel () {
		return globalModel;
	}
	
	/**********************************************
	 **************** HELP-METHODS ****************
	 *********************************************/
	
	private boolean isValidFunctionName (String name) {
		String vaildName = ComponentSettingsController.makeObjectName(name);
		if (vaildName.equals("")
				|| !vaildName.equals(name)
				|| globalModel.getScriptFunctions().get(name) != null) {
//				|| this.getScriptAt(INIT_SCRIPT, 0).equals(name)
//				|| EditableComponent.getAllPossibleListenerMethodNames().contains(name)) {
			return false;
		} else {
			return true;
		}
	}
	
	
//	public void buildRightClickMenu () {
//		/* ======================================== */
//		if (view.getEditor() == null) {
//			return;
//		}
//		
//		// create a new menu for this text area
//		rightClickMenu = new JPopupMenu();
//		rightClickMenu.setBackground(GECAMedColors.c_GECAMedBackground);
//		
//		/* **********  add the components  ********** */
////		JLabel componentsHeadline = new JLabel(
////				Translatrix.getTranslationString("formeditor.components") + ":");
////		rightClickMenu.add(componentsHeadline);
//		
//		Icon icon = IconFetcher.getMiniIcon(FormEditorModule.class, "object_key.png");
//		for (EditableComponent component : this.getComponents()) {
//			/* ---------------------------------------- */
//			if (component == null) {
//				continue;
//			}
//			
//			this.addRightClickMenuItem(component.getKey(), icon);
//			/* ---------------------------------------- */
//		}
//		/* **************************************** */
//		
////		rightClickMenu.addSeparator();
//		
//		/* **********  add the fix components  ********** */
//		icon = IconFetcher.getMiniIcon(FormEditorModule.class, "fix_object_key.png");
//		for (String fixKey : FormController.FIX_KEYS) {
//			/* ---------------------------------------- */
//			addRightClickMenuItem(fixKey, icon);
//			/* ---------------------------------------- */
//		}
//		/* **************************************** */
//		
////		rightClickMenu.addSeparator();
//		
//		/* **********  add the functions  ********** */
////		JLabel functionsHeadline = new JLabel(
////				Translatrix.getTranslationString("formeditor.functions") + ":");
////		rightClickMenu.add(functionsHeadline);
//		
//		icon = IconFetcher.getMiniIcon(FormEditorModule.class, "function.png");
//		for (String function : this.getFunctionNames()) {
//			/* ---------------------------------------- */
//			this.addRightClickMenuItem(function + "()", icon);
//			/* ---------------------------------------- */
//		}
//		/* **************************************** */
//		
//		view.getEditor().setPopupMenu(rightClickMenu);
//		/* ======================================== */
//	}
	
//	private void addRightClickMenuItem(String key, Icon icon) {
//		/* ======================================== */
//		JMenuItem item = new JMenuItem();
//		Map<TextAttribute, Object> attributes = (Map<TextAttribute, Object>) 
//				item.getFont().getAttributes();
////		attributes.put(TextAttribute.FOREGROUND, Color.DARK_GRAY);
//		attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
//		attributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
//		item.setFont(new Font(attributes));
//		
//		item.setBackground(GECAMedColors.c_GECAMedBackground);
//		
//		item.setActionCommand(key);
//		item.setAction(new AbstractAction(key, icon) {
//			private static final long serialVersionUID = 1L;
//
//			public void actionPerformed(ActionEvent e) {
//				/* ---------------------------------------- */
//				String key = ((JMenuItem)e.getSource()).getActionCommand();
//				view.getEditor().insert(key, view.getEditor().getCaretPosition());
//				
//				if (key.endsWith("()")) {
//					// a function was added -> put the caret between the brackets
//					view.getEditor().setCaretPosition(view.getEditor().getCaretPosition() - 1);
//				}
//				/* ---------------------------------------- */
//			}
//		});
//		
//		rightClickMenu.add(item);
//		/* ======================================== */
//	}
	
	
//	public JPopupMenu getRightClickMenu () {
//		/* ======================================== */
//		if (rightClickMenu == null) {
//			buildRightClickMenu();
//		}
//		return rightClickMenu;
//		/* ======================================== */
//	}
	
	
	private void fillCompletionProvider ()
	{
		provider.clear();
		
		for (EditableComponent ec : this.getComponents())
		{
			if (ec != null)
				provider.addCompletion(new IconCompletion(provider, ec.getKey(), 
						ec.getComponentSimpleName(), AC_ICON_COMPONENT));
		}
		
		for (String[] constant : FormController.FIX_KEYS)
		{
			provider.addCompletion(new IconCompletion(provider, 
					constant[0], constant[1], AC_ICON_CONSTANT));
		}
		
		addFunctionCompletions(provider);
		
		for (String key : SCRIPT_TEMPLATES)
		{
			provider.addCompletion(new IconCompletion(provider, 
					key, scriptTemplates.getProperty(key), null, AC_ICON_TEMPLATE));
		}
	}
	
	
	private void addFunctionCompletions (DefaultCompletionProvider provider)
	{
		Map<String, String> functions 	= globalModel.getScriptFunctions();
		IconCompletion completion;
		String script;
		String replacementText;
		StringBuffer description;
		int startIndex;
		int endIndex;
		
		for (String functionName : functions.keySet())
		{
			try {
				// get the whole script
				script 			= functions.get(functionName);
				
				// get the replacement text
				startIndex 		= script.indexOf("function")+8;
				endIndex 		= script.indexOf(')', startIndex)+1;
				replacementText = script.substring(startIndex, endIndex).trim().replace(" (", "(").concat(";");
				
				// get the description: <<parameter 1>>, <<parameter 2>>, ... , <<parameter n>> : <<return value>>
				startIndex 		= script.indexOf('(', startIndex)+1;
				endIndex--;
				description 	= new StringBuffer(script.substring(startIndex, endIndex).trim());
				
				// create the completion and add it to the provider
				completion 		= new IconCompletion(provider, functionName, replacementText, description.toString(), AC_ICON_FUNCTION);
				provider.addCompletion(completion);
			} 
			catch (Exception e)
			{
				logger.log(Level.WARN, "Function "+functionName+" themes not to be correct.");
			}
		}
	}
	
	
	public void checkScript () {
		/* ======================================== */
		// initialize engine to notice about errors
		HashMap<String, JComponent> engineComponents = new HashMap<String, JComponent>();
		JComponent component;
		ArrayList<JComponent> componentList = new ArrayList<JComponent>();
		
		for (EditableComponent ec : this.getComponents()) {
			if (ec != null) {
				component = ec.getComponent();
				engineComponents.put(ec.getKey(), component);
				componentList.add(component);
			}
		}
		
		ScriptEngine engine = FormController.getEngine(
				engineComponents, new JPanel(), new HashMap<Object, Object>(), new HashMap<Object, Object>());
		
		
		/*
		 * get the event, depending on the current method
		 */
		EventObject event = null;
		String key = this.getCurrentlySelectedLink().getKey();
		Component source = this.getCurrentlySelectedComponent();
		if (key.contains("mouse")) {
			if (key.contains("wheel"))
				event = new MouseWheelEvent(source, -1, 0l, -1, 0, 0, 1, false, 0, 1, 1);
			else
				event = new MouseEvent(source, 0, 0l, -1, 0, 0, 1, false);
		} else if (key.contains("focus")) {
			event = new FocusEvent(source, -1);
		} else if (key.contains("key")) {
			event = new KeyEvent(source, -1, 0l, -1, 0, 'a');
		} else if (key.contains("caret")) {
			event = new CaretEvent(source)
			{
				private static final long serialVersionUID = 1L;
				public int getMark() { return 0; }
				public int getDot() { return 0;}
			};
		} else if (key.contains("action")) {
			event = new ActionEvent (source, -1, "");
		} else if (key.contains("state")) {
			if (key.contains("item"))
				event = new ItemEvent(null, -1, null, 0);
			else 
				event = new ChangeEvent(source);
		} else if (key.equals("contentReloaded")) {
			event = new EventObject(source);
		}
			
		if (event != null)
			engine.put(FormController.FIX_KEY_EVENT, event);
		
		for (String name : this.getFunctionNames()) {
			try {
				engine.eval("function "+name+"() {}");
			} catch (ScriptException e1) {}
		}
		
		// try to evaluate
		try {
			engine.eval(view.getEditor().getText());
			view.setErrorMsg(Translatrix.getTranslationString("formeditor.no_error"), false);
		} catch (ScriptException error) {
			/* ---------------------------------------- */
			view.setErrorMsg(error.getMessage(), true);
			/* ---------------------------------------- */
		}
		/* ======================================== */
	}

	private Component getCurrentlySelectedComponent()
	{
		EditableComponent curSelComp = globalModel.getCurrentlySelectedComponent();
		
		if (curSelComp == null)
		{
			String key = view.getSelectedKey();
			curSelComp = globalModel.getEditableComponents().get(key);
		}
		
		return curSelComp;
	}
	
	
	private static Properties loadScriptTemplates ()
	{
		InputStreamReader 	reader 		= null;
		InputStream 		istream 	= null;
		BufferedInputStream bistream 	= null;
		Properties 			p 			= new Properties();
		
		try
		{
			istream 	= FormEditorModule.class.getResourceAsStream("resources/scriptTemplates.properties");
			bistream 	= new BufferedInputStream(istream);
			reader 		= new InputStreamReader(bistream);
			
//			URL 	url 	= FormEditorModule.class.getResource("resources/scriptTemplates.properties");
//			URI 	uri 	= url.toURI();
//			File 	file 	= new File(uri);
//			reader 	= new FileReader(file);
			
			p.load(reader);
			reader.close();
		} 
		catch (Exception e)
		{
			logger.log(Level.ERROR, "Could not load the script templates", e);
		}
		finally 
		{
			try 
			{
				if (reader != null)
					reader.close();
				else if (bistream != null)
					bistream.close();
				else if (istream != null)
					istream.close();
			} 
			catch (IOException e) 
			{
				logger.log(Level.WARN, "Could not close reader or stream", e);
			}
		}
		
		return p;
	}
}
