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

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.SpinnerNumberModel;

import lu.tudor.santec.gecamed.formeditor.gui.FormEditorModule;
import lu.tudor.santec.gecamed.formeditor.gui.component.EditableComboBox;
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.FormEditorModel;

public class ComponentSettingsController implements ActionListener, ItemListener {
	/**
	 * @author Jens Ferring
	 * 
	 * This controller listens to the settings panel, if a dummy- or "real"-component is selected. It needs to know on what to do, if an
	 * event is triggered. That does the whatToDo attribute indicate. There are two constructors, because once you need to differ 
	 * between two different spinner models and once you don't. 
	 * This is an ActionListener (to react on a button action to remove or add a component or to open the formula editor), 
	 * ChangeListener (to react on a value changing in one of the spinners, to resize the component) and KeyListener 
	 * (to react everytime a letter is typed into key- or name-textField)
	 */
	
	// what is to do (the meaning of the different whatToDo-values):
	public static int CHANGE_COLUMN_WIDTH 			= 1;
	public static int CHANGE_ROW_HEIGHT 			= 2;
	public static int SET_KEY 						= 5;
//	public static int SET_TEXT_FOR_SELECTED_COMPONENT = 6;
//	public static int SET_TEXT_FOR_SETTINGS_PANEL 	= 7;
	public static int CHECK_COLUMN_WIDTH 			= 9;
	public static int CHECK_ROW_HEIGHT 				= 10;
	public static int SET_CB_VALUE 					= 12;
	public static int SET_CB_TRANSLATION 			= 13;
	public static int UPDATE_SCRIPT_EDITOR 			= 15;
	
	public static String ADD_COMPONENT 				= "3";
	public static String REMOVE_COMPONENT 			= "4";
	public static String OPEN_FORMULA_EDITOR 		= "8";
	public static String CONTROL_EDITABLE_COMBOBOX 	= "11";
	public static String SWITCH_CB_LANUGAGE 		= "14";
	public static String FORMAT_CELLS 				= "format_cells";
	
	private FormEditorModel 	model;
	private FormEditorModule 	view;
	// the spinner models of the spinners, that shows the column- and row-number
	private ListModel 			comboBoxModel;
	private SpinnerNumberModel 	columnSpinnerModel;
	private SpinnerNumberModel 	rowSpinnerModel;
	// tells what to do, for components, which have no action-command
//	private int whatToDo;
	
	
	
	public ComponentSettingsController (FormEditorModel model) 
	{
		this.model = model;
		view = model.getView();
	}
	
//	public ComponentSettingsController (FormEditorModel model, int whatToDo) 
//	{
//		this.model = model;
//		view = model.getView();
//		this.whatToDo = whatToDo;
//	}
	
	// needed to add a component (to specify the column-size, row-size, and which component should be added 
	public ComponentSettingsController (FormEditorModel model, ListModel comboBoxModel,  
			SpinnerNumberModel columnSpinnerModel, SpinnerNumberModel rowSpinnerModel) 
	{
		this.model = model;
		view = model.getView();
		this.comboBoxModel = comboBoxModel;
		this.columnSpinnerModel = columnSpinnerModel;
		this.rowSpinnerModel = rowSpinnerModel;
	}
	
	
	public void setColumnSpinnerModel (SpinnerNumberModel model)
	{
		this.columnSpinnerModel = model;
	}
	
	public void setRowSpinnerModel (SpinnerNumberModel model)
	{
		this.rowSpinnerModel = model;
	}
	
	
	
	/**
	 * This method is called if a button is clicked. The method will add or remove a component from the
	 * editor panel or open the formula editor will be opened, depending on the whatToDo value.
	 */
	public void actionPerformed(ActionEvent e) 
	{		
		String actionCommand = ((JButton)e.getSource()).getActionCommand();
		if (actionCommand.equals(ADD_COMPONENT)) {
			/* ---------------------------------------- */
//			// add a new component to the editor at the position of the selected dummy
//			EditableComponent curComp = model.getCurrentlySelectedComponent();
//			
//			int column			= curComp.getColumn();
//			int row				= curComp.getRow();
//			int width			= ((Integer)columnSpinnerModel.getValue()).intValue();
//			int height			= ((Integer)rowSpinnerModel.getValue()).intValue();
//			int selectedIndex 	= ((JList) e.getSource()).getSelectedIndex();
//			String className 	= (String)((ComboBoxElement<?>)comboBoxModel.getElementAt(selectedIndex)).getValue();
//			
////			if (EditableSubForm.class.getName().equals(className))
////			{
////				column 			= 2;
////				width 			= view.getEditorColumnCount() - 1;
////			}
//			
//			EditableComponent c = model.createComponent(column, row, width, height, className);
//			
////			String defaultText = ((ComboBoxElement)comboBoxModel.getSelectedItem()).getString();
////			int lastDot = defaultText.lastIndexOf('.');
////			if (lastDot != -1) {
////				defaultText = defaultText.substring(lastDot+1);
////			}
////			c.setText(defaultText + " " + c.getKey());
//			c.setText(c.getComponentType());
//			model.addComponent(c);
			/* ---------------------------------------- */
		}
		else if (actionCommand.equals(OPEN_FORMULA_EDITOR)) 
		{
			/* ---------------------------------------- */
			// opens the formula editor
			model.openScriptEditor();
			return;
			/* ---------------------------------------- */
		}
		
		// after an event has been worked out, the settings and edit panel have to be rearranged
		view.rearrangeSettingsPanel();
		view.rearrangeEditPanel();
	}
	
	/**
	 * This method is called everytime an enter is done into the key- or text-textField or into the text-component itself. 
	 */
//	public void keyReleased(KeyEvent e) 
//	{
//		if (whatToDo > 0) 
//		{
//			if (whatToDo == SET_KEY) 
//			{
//				// the key of the currently selected component has changed
//				int startIndex = ((JTextField)e.getSource()).getSelectionStart();
//				int endIndex = ((JTextField)e.getSource()).getSelectionEnd();
//				int caretIndex = ((JTextField)e.getSource()).getCaretPosition(); 
//				
//				String text = ((JTextField)e.getSource()).getText();
//				int i = text.length();
//				text = makeObjectName(text);
//				if (i != text.length()) {
//					i -= text.length();
////					startIndex = startIndex - i <= 0 ? 0 : startIndex - i;
////					endIndex = endIndex - i <= 0 ? 0 : endIndex - i;
//					caretIndex = caretIndex - i <= 0 ? 0 : caretIndex - i;
//					
//					((JTextField)e.getSource()).setText(text);
//					((JTextField)e.getSource()).setCaretPosition(caretIndex);
//				} 
//				else 
//				{
//					((JTextField)e.getSource()).setText(text);
//					((JTextField)e.getSource()).setSelectionStart(startIndex);
//					((JTextField)e.getSource()).setSelectionEnd(endIndex);
//				}
//				
//				if (!model.getCurrentlySelectedComponent().setKey(((JTextField)e.getSource()).getText())) 
//				{
//					// the key is invalid (duplicate entry or empty-string), the textField gets red
//					((JTextField)e.getSource()).setBackground(FormEditorModule.MARK_WRONG_COLOR);
//				} 
//				else 
//				{
//					// the key is valid (no duplicate entries), the textField gets green
//					((JTextField)e.getSource()).setBackground(FormEditorModule.MARK_OK_COLOR);
//				}
//			}
//		}
//		/* ======================================== */
//	}
	
	/**
	 * The key becomes the Object name of the component in the script engine. Object names must only 
	 * consist out of letters, underscore and digits (but digits must not stand at first of the object name).
	 * This method deletes all chars that are not allowed (at there position).
	 * @param 	The name of the object, which should be corrected
	 * @return 	The corrected object name
	 */
	public static String makeObjectName (String name) {		
		String newName = name.replaceAll("[^\\d\\w_]", "");
		if (newName.length() == 0) {
			return newName;
		}
		char firstLetter;
		while (newName.length() != 0) {
			firstLetter = newName.charAt(0);
			if (firstLetter >= (int)'0' && firstLetter <= (int)'9') {
				newName = newName.replaceFirst(String.valueOf(firstLetter), "");
			} else {
				return newName;
			}
		}
		return newName;
	}	
	
	
	public void itemStateChanged(ItemEvent e) {
		/* ======================================== */
		JComboBox comboBox = (JComboBox)e.getSource();
		String actionCommand = comboBox.getActionCommand();
		if (actionCommand.equals(CONTROL_EDITABLE_COMBOBOX)) {
			/* ---------------------------------------- */
			ComboBoxElement<?> elem = (ComboBoxElement<?>)((JComboBox)e.getSource()).getSelectedItem();
			((EditableComboBox)model.getCurrentlySelectedComponent()).getModel().setSelectedItem(elem);
			/* ---------------------------------------- */
//		} else if (actionCommand.equals(SWITCH_CB_LANUGAGE)) {
//			/* ---------------------------------------- */
//			ComboBoxElement<?> languageElem = (ComboBoxElement<?>)((JComboBox)e.getSource()).getSelectedItem();
//			ComboBoxElement<?> componentElem = (ComboBoxElement<?>)((EditableComboBox)model.getCurrentlySelectedComponent()).getModel().getSelectedItem();
//			componentElem.setLanguage(new Locale((String)languageElem.getValue()));
//			view.rearrangeSettingsPanel();
			/* ---------------------------------------- */
		} else if (actionCommand.equals(FORMAT_CELLS)) {
			/* ---------------------------------------- */
			ComboBoxElement<?> value = (ComboBoxElement<?>)comboBox.getModel().getSelectedItem();
			EditableComponent ec = model.getCurrentlySelectedComponent();
			if (ec.getColumn() == 1) {
				// a row is selected
				view.setFormLayoutRowDefinition(ec.getRow(), 
						changeDefinition(
								Integer.parseInt(value.getValue(1).toString()), 
								view.getFormLayoutRowDefinition(ec.getRow()),
								(String)value.getValue()));
			} else if (ec.getRow() == 1) {
				// a column is selected
				view.setFormLayoutColDefinition(ec.getColumn(), 
						changeDefinition(((Integer)value.getValue(1)).intValue(), view.getFormLayoutColDefinition(ec.getColumn()), 
								(String)value.getValue()));
			} else {
				// a wrong component is selected (shouldn't be possible)
				return;
			}
			// the panel must be rearranged, to init the changes
			view.rearrangeEditPanel();
			/* ---------------------------------------- */
		}
		/* ======================================== */
	}
		
//	public void focusLost(FocusEvent e) {
//		if (e.getSource() instanceof JTextField
//				&& (whatToDo == UPDATE_SCRIPT_EDITOR)) {
//			model.updateKeys();
//		}
//	}

	/**
	 * is called when the comboBox it listens to is changed.
	 * It changes the definition of the currently selected column / row to the in the comboBox selected.
	 */
//	public void itemStateChanged(ItemEvent e) {
//		
//	}
	
	/**
	 * changes the value of the definition at the specified index 
	 * 
	 * @param index			Position in the String[] gained by definition.split(":") that has to be changed
	 * @param definition	the string, that has to be modified
	 * @param value			the new value, the definition takes at the specified index
	 * @return Returns the modified definition
	 */
	private String changeDefinition (int index, String definition, String value) {
		String[] defArray = definition.split(":");		
		defArray[index] = value;
		return defArray[0]+":"+defArray[1]+":"+defArray[2];
	}
	
	
	
	public void addComponent (int selectedIndex)
	{
		// add a new component to the editor at the position of the selected dummy
		EditableComponent curComp = model.getCurrentlySelectedComponent();
		
		int column			= curComp.getColumn();
		int row				= curComp.getRow();
		int width			= ((Integer)columnSpinnerModel.getValue()).intValue();
		int height			= ((Integer)rowSpinnerModel.getValue()).intValue();
//		int selectedIndex 	= ((JList) e.getSource()).getSelectedIndex();
		String className 	= (String)((ComboBoxElement<?>)comboBoxModel.getElementAt(selectedIndex)).getValue();
		
//		if (EditableSubForm.class.getName().equals(className))
//		{
//			column 			= 2;
//			width 			= view.getEditorColumnCount() - 1;
//		}
		
		EditableComponent c = model.createComponent(column, row, width, height, className);
		
//		String defaultText = ((ComboBoxElement)comboBoxModel.getSelectedItem()).getString();
//		int lastDot = defaultText.lastIndexOf('.');
//		if (lastDot != -1) {
//			defaultText = defaultText.substring(lastDot+1);
//		}
//		c.setText(defaultText + " " + c.getKey());
		c.setText(c.getComponentType());
		model.addComponent(c);
	}
	
	public void setKey (JTextField keyField)
	{
		// the key of the currently selected component has changed
		int startIndex 	= keyField.getSelectionStart();
		int endIndex 	= keyField.getSelectionEnd();
		int caretIndex 	= keyField.getCaretPosition(); 
		
		String text 	= keyField.getText();
		int i 			= text.length();
		text 			= makeObjectName(text);
		if (i != text.length()) {
			i 		   -= text.length();
			caretIndex 	= caretIndex - i <= 0 ? 0 : caretIndex - i;
			
			keyField.setText(text);
			keyField.setCaretPosition(caretIndex);
		} 
		else 
		{
			keyField.setText(text);
			keyField.setSelectionStart(startIndex);
			keyField.setSelectionEnd(endIndex);
		}
		
		if (!model.getCurrentlySelectedComponent().setKey(keyField.getText())) 
		{
			// the key is invalid (duplicate entry or empty-string), the textField gets red
			keyField.setBackground(FormEditorModule.MARK_WRONG_COLOR);
		} 
		else 
		{
			// the key is valid (no duplicate entries), the textField gets green
			keyField.setBackground(FormEditorModule.MARK_OK_COLOR);
		}
	}
	
	public void removeComponent ()
	{
		model.removeComponent(model.getCurrentlySelectedComponent());
		view.repaint();
		
		view.rearrangeSettingsPanel();
		view.rearrangeEditPanel();
	}
	
	/**
	 * This method is called, if a value of one of the spinnerButtons is changed. The spinners define the column- and 
	 * row-size of the currently selected component. 
	 */
	public void spinnerChanged (JSpinner spinner, int whatToDo) 
	{
		if (whatToDo > 0) {
//			JSpinner spinner = (JSpinner)e.getSource();
			SpinnerNumberModel spinnerModel = (SpinnerNumberModel) spinner.getModel();
			EditableComponent curSel = model.getCurrentlySelectedComponent();
			/*
			 * the current max column / row size is calculated, depending on which was changed (if the column size was changed,
			 * the maximum row size has to be recalculated and the other way round). After that is done, the settignsPanel mustn't
			 * be rearranged, because the former value would be lost.
			 */
			if (whatToDo == CHECK_COLUMN_WIDTH) 
			{
				if (curSel.getColumnSpinnerModel() != null)
					curSel.getColumnSpinnerModel().setMaximum(view.getMaxWidthWithCurrentHeight(curSel));
				return;
			} 
			else if (whatToDo == CHECK_ROW_HEIGHT) 
			{
				if (curSel.getRowSpinnerModel() != null)
					curSel.getRowSpinnerModel().setMaximum(view.getMaxHeightWithCurrentWidth(curSel));
				return;
			}
			
			/*
			 * the component was enlarged. The dummy-components have to be removed and the max column / row value 
			 * has to be calculated again
			 */
			int value = (Integer)spinnerModel.getValue();
			
			/*
			 * to change a component, it has to be removed, the changes have to be done and the changed component
			 * has to be added again 
			 */
			model.removeComponent(curSel, true);
			if (whatToDo == CHANGE_COLUMN_WIDTH) 
			{
				// the row size of the component was enlarged, so the max column value has to be recalculated
				curSel.setColumnWidth(value);
			} 
			else if (whatToDo == CHANGE_ROW_HEIGHT) 
			{
				// the column size of the component was enlarged, so the max row value has to be recalculated
				curSel.setRowHeight(value);
			}
			model.addComponent(curSel);
			
			// after the changes are done, the editPanel and settingsPanel have to be rearranged
			view.rearrangeSettingsPanel();
			view.rearrangeEditPanel();
		}
	}
	
	public void updateKeys ()
	{
		model.updateKeys();
	}
}
