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

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.TransferHandler;
import javax.swing.plaf.basic.BasicButtonListener;

import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialog;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.Form;
import lu.tudor.santec.gecamed.formeditor.ejb.entity.beans.FormTemplate;
import lu.tudor.santec.gecamed.formeditor.ejb.session.beans.FormManagerImpl;
import lu.tudor.santec.gecamed.formeditor.ejb.session.interfaces.FormManager;
import lu.tudor.santec.gecamed.formeditor.gui.FormWidgets;
import lu.tudor.santec.gecamed.formeditor.gui.controller.EditorPanelController;
import lu.tudor.santec.gecamed.formeditor.gui.controller.FormController;
import lu.tudor.santec.gecamed.formeditor.gui.controller.converter.FormDataConverter;
import lu.tudor.santec.gecamed.formeditor.gui.exception.InvalidSubFormIdException;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormEditorModel;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormModel;
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.jdom.Element;

import com.jgoodies.forms.layout.CellConstraints;

public class EditableSubForm extends EditableComponent
{
	/* ======================================== */
	// 		FINAL MEMBERS
	/* ======================================== */
	
	private static final long serialVersionUID 	= 1L;
	
	private static final String HEADLINE_TAG 	= "headline";
	private static final String ISSELECTED_TAG	= "isSelected";
	private static final String FORMID_TAG		= "formId";
	
	public static final int LISTENER_METHOD_SIZE 	= 3;
	public static final int ACTION_PERFORMED 		= SUPER_LISTENER_METHOD_SIZE;
	public static final int STATE_CHANGED 			= SUPER_LISTENER_METHOD_SIZE + 1;
	public static final int ITEM_STATE_CHANGED 		= SUPER_LISTENER_METHOD_SIZE + 2;
	
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	private JCheckBox 	 headlineBox;
	
	private Container	 content;
	
	private Container	 tabbedPaneParent;
	
//	private FormTemplate formTemplate;
	
	private FormModel	 formModel;
	
	private FormModel 	 parentFormModel;
	
//	private Integer 	 formId;
	
	private JCheckBox	 startValueBox;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTOR
	/* ======================================== */
	
	public EditableSubForm()
	{
		super();
//		((JXTaskPane) component).setTitle(getTypeTranslation());
//		((JPanel)((JXTaskPane) component).getContentPane()).setOpaque(false);
//		headlineBox.setText(getTypeTranslation());
		
		component 	= new JPanel(new BorderLayout());
		component.setOpaque(false);
		headlineBox = new JCheckBox()
		{
			private static final long serialVersionUID = 1L;

			public void setSelected(boolean b) 
			{
				if (b != super.isSelected())
				{
					super.setSelected(b);
					selectionChanged();
				}
			}
		};
		headlineBox.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				selectionChanged();
			}
		});
		headlineBox.setOpaque(false);
	}
	
	
	@Override
	public void init(CellConstraints constraints, FormEditorModel model, byte formModelType)
	{
		super.init(constraints, model, formModelType);
		
		component.add(headlineBox, BorderLayout.NORTH);
	}
	
	@Override
	protected void initType()
	{
		startValueBox = new JCheckBox();
		startValueBox.setOpaque(false);
		// if the startValueBox is (un-)checked, check the component as well
		startValueBox.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
//				((JXTaskPane)component).setExpanded(startValueBox.isCBSelected());
				setCBSelected(startValueBox.isSelected());
			}
		});
		
		TransferHandler handler = new TransferHandler("name"); 
		headlineBox.setName(this.getName());
		headlineBox.setTransferHandler(handler);
		headlineBox.setDropTarget(null);
		headlineBox.addMouseListener(new EditorPanelController(model));
		
		/* remove the basic MouseListener - the component shouldn't be (un-)checked
		 * every time it is clicked, but only through the menu.
		 */
		for (MouseListener l : headlineBox.getMouseListeners()) 
		{
			if (l instanceof BasicButtonListener) 
			{
				headlineBox.removeMouseListener(l);
				break;
			}
		}
		
		super.initType();
	}
	
	
	/* ======================================== */
	// 		GETTER & SETTER
	/* ======================================== */
	
	@Override
	public void setName(String name)
	{
		super.setName(name);
		headlineBox.setName(name);
	}
	
	
	@Override
	public void setText(String text)
	{
		try
		{
			Properties structure = new Properties();
			structure.load(new StringReader(text));
			
			String 	headlineText = structure.getProperty(HEADLINE_TAG);
			Boolean isSelected 	 = FormWidgets.parseBool(structure.getProperty(ISSELECTED_TAG), null);
			Integer formId 		 = FormWidgets.parseInt (structure.getProperty(FORMID_TAG), 	null);
			
			if (headlineText != null)
//				((JXTaskPane)component).setTitle(headlineText);
				headlineBox.setText(headlineText);
			if (isSelected != null)
//				((JXTaskPane)component).setExpanded(isSelected);
				setCBSelected(isSelected);
			
			// TODO: read all component values of the sub form out of the structure and set them on their component
			Form form;
			if (formId != null)
			{
				FormManager manager = (FormManager) ManagerFactory.getRemote(FormManagerImpl.class);
				form = manager.getForm(formId);
				if (form != null)
				{
					formModel.load(form, model != null);
					changeContent(formModel.getView());
				}
			}
		} 
		catch (IOException e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
	}
	
	
	@Override
	public String getText()
	{
		Properties structure = new Properties();
		
		Integer formId;
		if (formModel != null
				&& formModel.getForm() != null)
			formId = formModel.getForm().getId();
		else 
			formId = null;
		
		structure.setProperty(ISSELECTED_TAG, 	String.valueOf(isCBSelected()));
		String title = headlineBox.getText();
		structure.setProperty(HEADLINE_TAG, 	title == null ? "" : title);
		structure.setProperty(FORMID_TAG, 		String.valueOf(formId));
		
		// TODO: get all components and its values of the sub form and write them into the structure
		
		StringWriter writer = new StringWriter();
		try
		{
			structure.store(writer, null);
		}
		catch (IOException e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		
		return writer.toString();
	}
	
	
	@Override
	public Element getDataAsXML()
	{
		Element data 		= new Element(getKey());
		Element components 	= new Element("components");
		Element context 	= new Element("formContext");
		Element contextData;
		Object 	value;
		Form 	form 		= getFormModel().getForm();
		
		Collection<EditableComponent> componentList = formModel.getEditableComponents().values();
		for (EditableComponent ec : componentList)
		{
			components.addContent(ec.getDataAsXML());
		}
		data.addContent(components);
		
		HashMap<Object, Object> formContext = formModel.getFormContext();
		for (Object key : formContext.keySet())
		{
			value = formContext.get(key);
			if (value != null)
			{
				contextData = new Element("contextItem");
				contextData.setAttribute("name", key.toString());
				contextData.addContent(value.toString());
				context.addContent(contextData);
			}
		}
		data.addContent(context);
		
		data.addContent(FormDataConverter.getDateXMLData("creationDate", form.getDateCreated()));
		data.addContent(FormDataConverter.getDateXMLData("lastChangedDate", form.getDateLastChanged()));
		
		data.setAttribute("selected", String.valueOf(headlineBox.isSelected()));
		data.setAttribute("headline", headlineBox.getText());
		
		addDefaultProperties(data);
		return data;
	} 
	
	
	@Override
	public void setStructure(String text)
	{
		try 
		{
			Integer formTemplateId = Integer.valueOf(text);
			loadSubForm(formTemplateId, super.type);
		}
		catch (NumberFormatException e)
		{
//			formTemplate = null;
		}
	}
	
	
	@Override
	public String getStructure()
	{
		Integer templateId;
		if (formModel != null
				&& formModel.getTemplate() != null)
			 templateId = formModel.getTemplate().getId();
		else templateId = null;
		
		return String.valueOf(templateId);
	}
	
	
	@Override
	public String getHistoryText()
	{
		String description = formModel.getIncidentDescription();
		String descriptionHead = description.substring(0, description.indexOf("</head>") + 7);
		description = description.replaceFirst(descriptionHead, "</i>");
		description = description.replaceFirst(formModel.getTemplate().getName(), getHeadline());
		
//		System.out.println(description.replace("<br>", "\n"));
		return description;
//		return super.getHistoryText();
	}
	
	
	public FormModel getFormModel()
	{
		return formModel;
	}
	
	
	public void loadSubForm(Integer templateId, byte type)
	{
		FormManager manager = (FormManager) ManagerFactory.getRemote(FormManagerImpl.class);
		FormTemplate template = manager.getTemplate(templateId);
		if (template == null)
			throw new InvalidSubFormIdException(templateId);
		
		loadSubForm(template, type);
	}
	
	public void loadSubForm (FormTemplate template, byte type)
	{
		if (formModel == null)
		{
			formModel = new FormModel(type == FormModel.TYPE_EDITOR ? FormModel.TYPE_EDITOR_SUBFORM : type);
			formModel.setSubForm(this);
			formModel.loadTemplate(template, super.model, false);
		}
		else if (!formModel.getTemplate().getId().equals(template.getId()))
		{
			formModel.loadTemplate(template, super.model, false);
		}
		
		Form form;
		if (formModel.getForm() == null)
			 form = new Form();
		else form = formModel.getForm();
		
		Patient patient 	= GECAMedModule.getCurrentPatient();
		Integer templateId 	= template != null ? template.getId() : null;
		Integer patientId 	= patient  != null ? patient.getId()  : null;
		
		form.setPatientId(patientId);
		form.setTemplateId(templateId);
		formModel.setForm(form);
		
		changeContent(formModel.getView());
	}
	
	
	public void changeContent (Container newContent)
	{
		if (content != null)
			component.remove(content);
		content = newContent;
		if (content != null)
		{
			component.add(content, BorderLayout.CENTER);
			content.setVisible(isCBSelected());
		}
	}
	
	
	public void executeInitMethod ()
	{
		formModel.executeInitMethod();
	}
	
	
	public void selectionChanged ()
	{
		if (content != null)
		{
			
			showAllComponents(content);
			content.setVisible(isCBSelected());
			
			if (tabbedPaneParent == null)
			{
				tabbedPaneParent = headlineBox.getParent();
				while (!(tabbedPaneParent instanceof JTabbedPane))
					if (tabbedPaneParent != null)
						tabbedPaneParent = tabbedPaneParent.getParent();
					else
						return;
			}
			tabbedPaneParent.validate();
		}	
	}
	
	public static void showAllComponents (Container c)
	{
		c.setVisible(true);
		Component[] components = c.getComponents();
		for (Component component : components)
		{
			if (component != null)
			{
				if (component instanceof Container)
					 showAllComponents((Container) component);
				else component.setVisible(true);
			}
		}
	}
	
	
	public Map<String, JComponent> getSubComponentMap(String superKey)
	{
		Map<String, JComponent> subComponents 	= new HashMap<String, JComponent>();
		Map<String, JComponent> modelComponents;
		
		if (formModel == null)
		{
			logger.log(Level.WARN, "FormModel of SubForm "+getKey()+" is null!");
			return subComponents;
		}
		else modelComponents = formModel.getGlobalComponents();
		
		String 		subKey;
		JComponent 	component;
		for (String key : modelComponents.keySet())
		{
			subKey = superKey + "." + key;
			component = modelComponents.get(key);
			subComponents.put(subKey, component);
		}
		
		return subComponents;
	}
	
	
	@Override
	public String getTypeTranslation()
	{
		return Translatrix.getTranslationString("formeditor.add_subform");
	}
	
	
	@Override
	public String getComponentType()
	{
		return "subform";
	}
	
	
	@Override
	public JComponent copyComponent(FormModel model)
	{
		EditableSubForm subForm = this;

		FormController controller = new FormController(model);
		
		if (getScriptAt(ACTION_PERFORMED).length() > 0)
		{
			subForm.headlineBox.addActionListener(controller);
			controller.setCodeAt(FormController.ACTION_PERFORMED, 	getScriptAt(ACTION_PERFORMED));
		}
		
//		if (getFormulaAt(ITEM_STATE_CHANGED).length() > 0)
//		{
			subForm.headlineBox.addItemListener(controller);
			controller.setCodeAt(FormController.ITEM_STATE_CHANGED, getScriptAt(ITEM_STATE_CHANGED));
//		}
		
		if (getScriptAt(STATE_CHANGED).length() > 0)
		{
			subForm.headlineBox.addChangeListener(controller);
			controller.setCodeAt(FormController.STATE_CHANGED, 		getScriptAt(STATE_CHANGED));
		}
		
		addDefaultListener(subForm.headlineBox, controller);
		
		return subForm.getComponent();
	}
	

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

	@Override
	protected void addListenerMethodNames()
	{
		listenerMethodNames[ACTION_PERFORMED] 	= "actionPerformed";
		listenerMethodNames[STATE_CHANGED] 		= "stateChanged";
		listenerMethodNames[ITEM_STATE_CHANGED] = "itemStateChanged";
	}
	
	
	@Override
	public DefaultOptionPanel createOptionPanel()
	{
		DefaultOptionPanel p 	= super.createOptionPanel();
		
		// start value
		JPanel innerPanel 		= new JPanel (new BorderLayout());
		innerPanel.setOpaque(false);
		
		startValueBox.setSelected(this.isCBSelected());
		JTextField captionText = new JTextField(getHeadline());
		
		innerPanel.add(startValueBox, 	BorderLayout.WEST);
		innerPanel.add(captionText, 	BorderLayout.CENTER);
		
		p.getCaptionTextComponent().setComponent(innerPanel);
		
		captionText.addKeyListener(new KeyListener()
		{
			public void keyTyped(KeyEvent e) {}
			public void keyPressed(KeyEvent e) {}
			
			public void keyReleased(KeyEvent e) 
			{
				setHeadline(((JTextField)e.getSource()).getText());
			}
		});
		
		final JButton selectFormIdButton = new JButton(new AbstractAction(
				Translatrix.getTranslationString("formeditor.templateListHeadline"))
		{
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e)
			{
				SelectFormDialog dialog = model.getSelectFormDialog();
				dialog.setVisible(true);
				
				FormTemplate selectedFormTemplate = dialog.getSelectedForm();
				if (dialog.getButtonOption() == GECAMedBaseDialog.OK_OPTION
						&& selectedFormTemplate != null)
				{
					loadSubForm(selectedFormTemplate, FormModel.TYPE_EDITOR);
					
					((JButton)e.getSource()).setToolTipText(
							Translatrix.getTranslationString("formeditor.chosenForm").
									replace("$FORMNAME", formModel.getTemplate().getName()));
				}
			}
		});
		selectFormIdButton.setToolTipText(
				formModel == null || formModel.getTemplate() == null 
				? Translatrix.getTranslationString("formeditor.noFormChosen")
				: Translatrix.getTranslationString("formeditor.chosenForm").
						replace("$FORMNAME", formModel.getTemplate().getName()));
		
		p.appendRow(selectFormIdButton);
		
		return p;
	}
	
	@Override
	public JComponent getComponentForProperty()
	{
		return headlineBox;
	}
	
	@Override
	public JPanel getAddablePanel(JComponent c)
	{
		if (this.getDescriptionPosition().equals(FormModel.NONE) 
				&& c instanceof JPanel)
			 return (JPanel) c;
		else return super.getAddablePanel(c);
	}

	@Override
	public Boolean isComponentTypeStoredInDBByDefault () {
		return true;
	}
	
	@Override
	public void dispose()
	{
		if (formModel != null)
			formModel.close();
		super.dispose();
		formModel 		= null;
		parentFormModel = null;
	}
	
	public boolean isCBSelected ()
	{
		return headlineBox.isSelected();
	}
	
	public void setCBSelected (boolean selected)
	{
		headlineBox.setSelected(selected);
	}
	
	public String getHeadline ()
	{
		return headlineBox.getText();
	}
	
	public void setHeadline (String text)
	{
		headlineBox.setText(text);
	}
	
	public FormModel getParentFormModel ()
	{
		return parentFormModel;
	}
	
	public void setParentFormModel (FormModel model)
	{
		this.parentFormModel = model;
	}
}
