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

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Properties;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;

import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.IconFetcher;
import lu.tudor.santec.gecamed.core.gui.widgets.NamedComponent;
import lu.tudor.santec.gecamed.formeditor.gui.controller.FormController;
import lu.tudor.santec.gecamed.formeditor.gui.model.ComboBoxElement;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormModel;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.jdom.Element;

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

public class EditableComboBox extends EditableComponent {
	
	private static final long serialVersionUID = 1L;
	
	private static final int LISTENER_METHOD_SIZE = 2;
	private static final int ITEM_STATE_CHANGED = SUPER_LISTENER_METHOD_SIZE;
	private static final int ACTION_PERFORMED = SUPER_LISTENER_METHOD_SIZE + 1;
	
	private static final String XML_ELEMENT_ITEM 		= "item";
	private static final String XML_ATTRIBUTE_SELECTED 	= "selected";
	private static final String TRUE 					= String.valueOf(true);
	private static final String FALSE 					= String.valueOf(false);
	
	
	private JTextField 		valueField;
	private JTextField 		labelField;
	private JButton 			addItemButton;
	private JButton 			removeItemButton;
	private JComboBox 		itemBox;
	private Vector<Object> 	itemBoxModel;
	
	
	
	public EditableComboBox() {
		
		itemBoxModel 	= new Vector<Object>();
		component 		= new JComboBox(itemBoxModel);
	}
	
	@Override
	protected void initType()
	{
		initMenuComponents();
		super.initType();
	}
	
	

	@Override
	protected void addListenerMethodNames() {
		listenerMethodNames[ITEM_STATE_CHANGED] = "itemStateChanged";
		listenerMethodNames[ACTION_PERFORMED] = "actionPerformed";
	}

	@Override
	public JComponent copyComponent(FormModel model) 
	{
		JComboBox cb = (JComboBox) this.component;
		
		FormController controller = new FormController(model);
		
		cb.addItemListener(controller);
		controller.setCodeAt(FormController.ITEM_STATE_CHANGED, getScriptAt(ITEM_STATE_CHANGED));
		
		if (getScriptAt(ACTION_PERFORMED).length() > 0)
		{
			cb.addActionListener(controller);
			controller.setCodeAt(FormController.ACTION_PERFORMED, getScriptAt(ACTION_PERFORMED));
		}
		
		addDefaultListener(cb, controller);
		
		return cb;
	}

	@Override
	public String getComponentType() {
		return "combobox";
	}

	@Override
	protected int getListenerMethodSize() {
		return LISTENER_METHOD_SIZE;
	}

	@Override
	public String getStructure() 
	{
		Properties 		structure 	= new Properties();
		JComboBox 		cb 			= ((JComboBox)component);
		StringWriter 	out 		= new StringWriter();
		
		for (int index = 0; index < cb.getItemCount(); index++) 
		{
			Object elem = cb.getItemAt(index);
			
			if (elem == null)
				structure.setProperty("", String.valueOf((Object)null));
			if (elem instanceof ComboBoxElement<?>) 
				structure.setProperty(elem.toString(), ((ComboBoxElement<?>)elem).getValue().toString());
			else 
				structure.setProperty(elem.toString(), elem.toString());
		}
		
		try {
			structure.store(out, "");
		} catch (IOException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		
		return out.toString();
	}

	@SuppressWarnings("unchecked")
	@Override
	public void setStructure(String text) 
	{
		TreeSet<ComboBoxElement<String>> set = new TreeSet<ComboBoxElement<String>>(); 
		
		ComboBoxElement<String> elem;
		Enumeration<String> 	propertyNames;
		
		StringReader 	in 			= new StringReader(text);
		Properties 		structure 	= new Properties();
		String 			key;
		
		try {
			structure.load(in);
		} catch (IOException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		itemBoxModel.clear();
		
		propertyNames = (Enumeration<String>) structure.propertyNames();
		while (propertyNames.hasMoreElements()) 
		{
			key 	= propertyNames.nextElement();
			elem 	= new ComboBoxElement<String>(key, structure.getProperty(key));
			set.add(elem);
		}
		
		itemBoxModel.addAll(set);
	}

	@Override
	public String getText() 
	{
		Object elem = ((JComboBox) component).getSelectedItem();
		String text = null;
		
		if (elem != null) 
			text = elem.toString();
		
		if (text == null)
			 return "";
		else return text;
	}

	@Override
	public void setText(String text) 
	{
		Enumeration<Object> elements = itemBoxModel.elements();
		ComboBoxElement<?> 	cbElem;
		
		if (((JComboBox) component).getItemCount() > 0) 
			((JComboBox) component).setSelectedItem(0);
		
		if (text == null || text.equals("")) 
			return;
		
		while (elements.hasMoreElements()) 
		{
			cbElem = (ComboBoxElement<?>)elements.nextElement();
			String value = cbElem.getTranslation();
			
			if (value.equals(text)) 
				((JComboBox) component).setSelectedItem(cbElem);
		}
		
		configTextFields();
	}
	
	@Override
	public Element getDataAsXML()
	{
		ComboBoxModel model 	= getModel();
		Element component 		= new Element(getKey());
		Element text;
		Element data;
		Element xmlCbElement;
		Object 	cbElement;
		Object 	selectedElement = model.getSelectedItem();
		String 	selectedText 	= selectedElement == null ? "" : selectedElement.toString();
		
		text = new Element(XML_ELEMENT_TEXT);
		text.setText(selectedText);
		component.addContent(text);
		
		for (int i = 0; i < model.getSize(); i++)
		{
			cbElement = model.getElementAt(i);
			
			xmlCbElement 	= new Element(XML_ELEMENT_ITEM);
			text 			= new Element(XML_ELEMENT_TEXT);
			data 			= new Element(XML_ELEMENT_DATA);
			
			text.setText(cbElement.toString());
			if (cbElement instanceof ComboBoxElement)
				 data.setText(String.valueOf(((ComboBoxElement<?>) cbElement).getValue()));
			else data.setText(cbElement.toString());
			
			if (cbElement == selectedElement)
				 xmlCbElement.setAttribute(XML_ATTRIBUTE_SELECTED, TRUE);
			else xmlCbElement.setAttribute(XML_ATTRIBUTE_SELECTED, FALSE);
			xmlCbElement.addContent(text);
			xmlCbElement.addContent(data);
			
			component.addContent(xmlCbElement);
		}
		
		addDefaultProperties(component);
		return component;
	}
	
	@Override
	public String getHistoryText () {
		Object element = ((JComboBox)component).getSelectedItem();
		if (element == null) {
			return "";
		} else {
			return element.toString();
		}
	}
	
	public ComboBoxModel getModel () {
		return ((JComboBox)component).getModel();
	}
	
	public void setModel (ComboBoxModel model) {
		((JComboBox)component).setModel(model);
	}

	@Override
	public DefaultOptionPanel createOptionPanel () 
	{
		DefaultOptionPanel optionPanel = super.createOptionPanel();
		optionPanel.getCaptionTextComponent().setVisible(false);
		
		JPanel p = new JPanel(new FormLayout("c:40px, 2dlu, 20dlu:g",
						"5px, " +
						"c:p, 2dlu, " + // 2: ComboBox
						"c:p, 2dlu, " + // 4: Label
						"c:p, 2dlu, " + // 6: Value
						"c:40px")); 	// 8: add- and remove-element buttons
		p.setOpaque(false);
		
		// the ComboBox with its values
		p.add(itemBox, new CellConstraints(1, 2, 3, 1));
		
		p.add(new NamedComponent(
					Translatrix.getTranslationString("formeditor.translation"), 
					labelField), 
				new CellConstraints(1, 4, 3, 1));
		
		// a TextField to enter the value of the selected ComboBox item
		p.add(new NamedComponent(
					Translatrix.getTranslationString("formeditor.element_value"),
					valueField), 
				new CellConstraints(1, 6, 3, 1));
		
		// two buttons to add a new element to the CB, 
		// resp. to remove the selected
		JPanel panel = new JPanel(new FormLayout("c:40px, 2dlu, 40px, p:g", "40px"));
		panel.setOpaque(false);
		p.add(panel, new CellConstraints(3, 8));
		
		// add-button
		panel.add(addItemButton, new CellConstraints(1, 1));
		
		// remove-button
		panel.add(removeItemButton, new CellConstraints(3, 1));		
		
		optionPanel.appendRow(p);
		return optionPanel;
	}
	
	/**
	 * Initializes all components needed for the menu in the form-editor
	 */
	private void initMenuComponents () 
	{
		// ITEM-BOX
		itemBox = new JComboBox(itemBoxModel);
//		ComboBoxElement elem = (ComboBoxElement) itemBox.getSelectedItem();
		
		itemBox.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) 
			{
				ComboBoxElement<?> elem = (ComboBoxElement<?>) 
						((JComboBox) e.getSource()).getSelectedItem();
				((JComboBox)component).setSelectedItem(elem);
				valueField.setText(elem.getValue().toString());
				labelField.setText(elem.getTranslation());
			}
		});

		// ADD ITEM-LISTENER TO COMPONENT
		((JComboBox)component).addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) 
			{
				ComboBoxElement<?> elem = (ComboBoxElement<?>) 
						((JComboBox) e.getSource()).getSelectedItem();
				itemBox.setSelectedItem(elem);
				valueField.setText(elem.getValue().toString());
				labelField.setText(elem.getTranslation());
			}
		});
		
		
		// VALUE-TEXT-FIELD
		valueField = new JTextField("");
		valueField.addKeyListener(new KeyListener() 
		{
			public void keyPressed(KeyEvent e) {}
			public void keyTyped(KeyEvent e) {}
			
			@SuppressWarnings("unchecked")
			public void keyReleased(KeyEvent e) 
			{
				ComboBoxElement<String> elem = (ComboBoxElement<String>) itemBox
						.getModel().getSelectedItem();

				// the text in the value-textfield has changed,
				// update the new value in the selected item
				elem.setValue(valueField.getText());
				
				itemBox.validate();
				component.validate();
			}
		});
		
		// LABEL-TEXT-FIELD
		labelField = new JTextField("");
		labelField.addKeyListener(new KeyListener() {
			
			public void keyPressed(KeyEvent e) {}
			public void keyTyped(KeyEvent e) {}
			
			public void keyReleased(KeyEvent e) 
			{
				ComboBoxElement<?> elem = (ComboBoxElement<?>) itemBox
						.getModel().getSelectedItem();

				// the text in the value-textfield has changed,
				// update the new value in the selected item
				elem.setTranslation(labelField.getText());
			}
		});
		
		configTextFields();
		
		// ADD-ITEM-BUTTON
		addItemButton = new JButton();
		addItemButton.setToolTipText(
				Translatrix.getTranslationString(
						"formeditor.add_new_element"));
		
		addItemButton.setAction(new AbstractAction("",
				IconFetcher.getScaledIcon(
						GECAMedModule.class, "add.png", 24)) 
		{
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) 
			{
				addItem(new ComboBoxElement<String>("", ""));
			}
		});
		
		// REMOVE-ITEM-BUTTON
		removeItemButton = new JButton();
		removeItemButton.setToolTipText(
				Translatrix.getTranslationString(
				"formeditor.remove_elmenet"));
		
		removeItemButton.setAction(new AbstractAction("",
				IconFetcher.getScaledIcon(
						GECAMedModule.class, "remove.png", 24)) 
		{
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) 
			{
				removeSelectedItem ();
			}
		});
	}
	
	public void addItem (ComboBoxElement<String> item) {
		
		valueField.setEnabled(true);
		labelField.setEnabled(true);
		removeItemButton.setEnabled(true);
		
		itemBoxModel.addElement(item);
		itemBox.setSelectedItem(item);
//		((JComboBox)component).setSelectedItem(item);
	}
	
	public void removeSelectedItem () {
		
		itemBox.removeItemAt(itemBox.getSelectedIndex());
		
		if (itemBoxModel.isEmpty()) {
			
			valueField.setText("");
			valueField.setEnabled(false);
			
			labelField.setText("");
			labelField.setEnabled(false);
			
			removeItemButton.setEnabled(false);
		} 
	}
	
	private void configTextFields () 
	{
		if (type == FormModel.TYPE_EDITOR)
		{
			if (itemBoxModel.isEmpty() || itemBox.getSelectedIndex() < 0) 
			{
				// no element in the ComboBox -> disable the TextFields
				labelField.setEnabled(false);
				valueField.setEnabled(false);
			} 
			else 
			{
				ComboBoxElement<?> elem = (ComboBoxElement<?>)itemBox.getSelectedItem();
				
				// there are elements in the ComboBox 
				// -> set the text and the value into the TextFields 
				labelField.setEnabled(true);
				valueField.setEnabled(true);
				
				labelField.setText(elem.getTranslation());
				valueField.setText((String) elem.getValue());
			}
		}
	}
	
	@Override
	public String getTypeTranslation () 
	{
		return Translatrix.getTranslationString("formeditor.add_combobox");
	}
}
