package lu.tudor.santec.gecamed.reporting.gui.creator;

import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.IconFetcher;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.utils.GECAMedGuiUtils;
import lu.tudor.santec.gecamed.core.gui.utils.LineColorListRenderer;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.formeditor.gui.component.NumericField;
import lu.tudor.santec.gecamed.formeditor.gui.model.ComboBoxElement;
import lu.tudor.santec.gecamed.reporting.ejb.entity.beans.ParameterQuery;
import lu.tudor.santec.gecamed.reporting.ejb.entity.beans.Report;
import lu.tudor.santec.gecamed.reporting.ejb.entity.beans.ReportParameter;
import lu.tudor.santec.gecamed.reporting.ejb.entity.beans.ReportParameter.EmptyParameterException;
import lu.tudor.santec.gecamed.reporting.ejb.session.beans.ReportManagerBean;
import lu.tudor.santec.gecamed.reporting.gui.ReportModule;
import lu.tudor.santec.gecamed.reporting.gui.designer.ParameterPanel;
import lu.tudor.santec.i18n.Translatrix;

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

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;
import com.jgoodies.forms.layout.Sizes;
import com.toedter.calendar.JDateChooser;

public class ReportCreatorDialog extends GECAMedBaseDialogImpl
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	private static final long		serialVersionUID	= 1L;
	
//	private static final int		COLUMN_USE			= 2;
	private static final int		COLUMN_NAME			= 4;
	private static final int		COLUMN_VALUE		= 6;
//	private static final int		COLUMN_COMMENT		= 8;
	
	private static final RowSpec	DEFAULT_ROWSPEC		= new RowSpec(RowSpec.CENTER, Sizes.PREFERRED, RowSpec.NO_GROW);
	
	private static final RowSpec	SPACER_ROWSPEC		= new RowSpec(Sizes.pixel(5));
	
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(ReportCreatorDialog.class.getName());
	
	private static CellConstraints	CC				= new CellConstraints();
	
	private static DateFormat	dateFormatter		= new SimpleDateFormat("yyyy-MM-dd");
	
//	private static DateFormat	dateTimeFormatter	= 
	
	private static ReportCreatorDialog	instance;
	
	
	private Report				report;
	
	private JLabel				reportNameLabel;
	
	private JLabel				reportDescriptionLabel;
	
	private JLabel				reportDescriptionHeadlineLabel;
	
	private List<ParameterRow>	rows;
	
	private JPanel				parameterPanel;
	
//	private JButton				showQueryButton;
	
	private JScrollPane			parameterScroller;
	
	private FormLayout			parameterPanelLayout;
	
//	private JButton				iReportButton;
	
	private ReportCreator		reportCreator;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTORS
	/* ======================================== */
	
	public ReportCreatorDialog ()
	{
		super(MainFrame.getInstance(), 
				Translatrix.getTranslationString("ReportModule.CreatorDialog.Title"), 
				GECAMedBaseDialogImpl.CLOSE_BUTTON_MODE);
		
		instance	= this;
		
		initComponents();
	}
	
	
	
	/* ======================================== */
	// 		CLASS BODY
	/* ======================================== */
	
	public void setReport (Report report)
	{
		boolean	hasParameter;
		
		
		this.report	= (Report) report.clone();
		
		clearParameter();
		
		this.reportNameLabel.setText(this.report.getName());
		
		if (this.report.getDescription() != null 
				&& this.report.getDescription().trim().length() > 0)
		{
			try
			{
//				this.reportDescriptionLabel.setText(this.report.getDescription().matches(
//						"(\\w|\\W)*\\<(?i)html(?-i)[^\\>]*\\>(\\w|\\W)*") 
//						? this.report.getDescription().replaceFirst(
//								"(\\w|\\W)*\\<(?i)html(?-i)[^\\>]*\\>", "<html>")
//						: "<html>"+this.report.getDescription().replaceAll("\n", "<br>"));
				this.reportDescriptionLabel.setText(this.report.getDescription().contains("<html>")
						? this.report.getDescription() 
						: "<html>"+this.report.getDescription().replaceAll("\n", "<br>"));
				this.reportDescriptionLabel.setVisible(true);
				this.reportDescriptionHeadlineLabel.setVisible(true);
			}
			catch (Throwable e)
			{
				logger.error("Error while trying to replace HTML tags.", e);
			}
		}
		else
		{
			this.reportDescriptionLabel.setText("");
			this.reportDescriptionLabel.setVisible(false);
			this.reportDescriptionHeadlineLabel.setVisible(false);
		}
		
		for (ReportParameter parameter : this.report.getParameter())
			new ParameterRow(parameter).appendMe();
		
		hasParameter	= !this.report.getParameter().isEmpty();
		this.parameterScroller.setVisible(hasParameter);
		
//		this.iReportButton.setEnabled(
//				this.report.getJrxml() == null 
//				&& this.report.getJasper() == null);
		
		
		// show the dialog
//		this.setMinimumSize(new Dimension(450, hasParameter ? 340 : 0));
//		this.setMaximumSize(new Dimension(450, 600));
		
		this.pack();
		this.setLocationRelativeTo(getOwner());
		this.setVisible(true);
	}
	
	
	public static ReportCreatorDialog getInstance ()
	{
		return instance;
	}
	
	
	
	/* ======================================== */
	// 		HELP METHODS
	/* ======================================== */
	
	private void initComponents ()
	{
		JLabel		label;
		JButton		button;
		int			row;
		
		
		this.reportCreator			= new ReportCreator();
		
		/* **************************************** */
		//	define the guidance & report description
		/* **************************************** */
		this.mainPanel.setLayout(new FormLayout(
				"5px,f:p,5px,f:d:g,5px", 
				"5px,f:p," +	// guidance
				"20px,t:p," +	// report name
				"5px,t:p," +	// report description
				"5px,f:min(p;150px)," +	// parameter panel
				"5px,f:p," +	// report query
				"5px"));
		this.mainPanel.setOpaque(false);
		row	= 0;
		
		// guidance
		label						= new JLabel(
				Translatrix.getTranslationString("ReportModule.CreatorDialog.guidance"));
		label.setOpaque(false);
//		label.setSize(label.getPreferredSize());
		this.mainPanel.add(label,				CC.xyw(2, row+=2, 3));
		
		// report name
		label						= new JLabel(
				Translatrix.getTranslationString("ReportModule.CreatorDialog.reportName")); 
		label.setOpaque(false);
		this.mainPanel.add(label, 				CC.xy(2, row+=2));
		
		this.reportNameLabel		= new JLabel();
		this.reportNameLabel.setOpaque(false);
		this.mainPanel.add(this.reportNameLabel,	CC.xy(4, row));
		
		this.reportDescriptionHeadlineLabel	= new JLabel(
				Translatrix.getTranslationString("ReportModule.CreatorDialog.reportDescription")); 
		this.reportDescriptionHeadlineLabel.setOpaque(false);
		this.mainPanel.add(this.reportDescriptionHeadlineLabel,	CC.xy(2, row+=2));
		
		this.reportDescriptionLabel	= new JLabel();
		this.reportDescriptionLabel.setOpaque(false);
//		ComponentUtilities.setFont(this.reportDescriptionLabel, Font.SANS_SERIF);
		this.mainPanel.add(this.reportDescriptionLabel,			CC.xy(4, row));
		
		
		/* **************************************** */
		// 	define the parameter panel
		/* **************************************** */
		this.rows					= new ArrayList<ParameterRow>();
		this.parameterPanelLayout	= new FormLayout(
				"5px,f:p," +		// use parameter
				"5px,f:p," +		// parameter name
				"5px,f:150px," +	// select value
//				"5px,f:p," +		// comment
				"5px");
		this.parameterPanelLayout.appendRow(SPACER_ROWSPEC);
		this.parameterPanel			= new JPanel(this.parameterPanelLayout);
		this.parameterPanel.setOpaque(false);
		this.parameterScroller		= new JScrollPane(this.parameterPanel);
		this.parameterScroller.setOpaque(false);
		this.parameterScroller.getViewport().setOpaque(false);
		this.parameterScroller.setBorder(BorderFactory.createEmptyBorder());
		this.parameterScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		this.mainPanel.add(parameterScroller, CC.xyw(2, row+=2, 3, 
				CellConstraints.FILL, CellConstraints.TOP));
		
		
		button			= new JButton(new AbstractAction(
				Translatrix.getTranslationString("ReportModule.CreatorDialog.showQuery"), 
				IconFetcher.getSmallIcon(ReportModule.class, "db_query.png")
				)
		{
			private static final long	serialVersionUID	= 1L;

			public void actionPerformed(ActionEvent e)
			{
				ReportParameter[]	parameters	= new ReportParameter[rows.size()];
				int					index		= 0;
				String				query;
				
				for (ParameterRow row : rows)
					parameters[index++]	= row.getParameter();
//				report.setParameters(parameters);
				
				try
				{
					query	= report.createFilledQuery();
					new FilledQueryDialog(ReportCreatorDialog.this, 
							query).setVisible(true);
				}
				catch (EmptyParameterException epe)
				{
					GECAMedBaseDialogImpl.showMessageDialog(ReportCreatorDialog.this, 
							Translatrix.getTranslationString("ReportModule.CreatorDialog.emptyParameterTitle"), 
							Translatrix.getTranslationString("ReportModule.CreatorDialog.emptyParameterMessage",
									new String[] { report.getMendatoryParameterNames() }), 
							OK_BUTTON_MODE, 
							GECAMedModule.getBigIcon(GECAMedIconNames.WARNING));
				}
			}
		});
		this.mainPanel.add(button, CC.xy(2, row+=2));
		
		button			= new JButton(new AbstractAction(null,
				ReportModule.getButtonIcon("table.png"))
		{
			private static final long	serialVersionUID	= 1L;

			public void actionPerformed(ActionEvent e)
			{
				// show table
				export(ReportCreator.OPTION_SHOW_TABLE);
			}
		});
		button.setToolTipText(
				Translatrix.getTranslationString("ReportModule.CreatorDialog.showTable"));
//		panel.add(button, CC.xy(2, 2));
		this.addButton(button);
		
		button			= new JButton(new AbstractAction(null,
				ReportModule.getButtonIcon("export_csv.png"))
		{
			private static final long	serialVersionUID	= 1L;

			public void actionPerformed(ActionEvent e)
			{
				// export as CSV
				export(ReportCreator.OPTION_EXPORT_CSV);
			}
		});
		button.setToolTipText(
				Translatrix.getTranslationString("ReportModule.CreatorDialog.exportAsCSV"));
//		panel.add(button, CC.xy(4, 2));
		this.addButton(button);
		
//		iReportButton	= new JButton(new AbstractAction(null,
//				ReportModule.getButtonIcon("iReport.png"))
//		{
//			private static final long	serialVersionUID	= 1L;
//
//			public void actionPerformed(ActionEvent e)
//			{
//				// create an iReport
//				export(ReportCreator.OPTION_CREATE_IREPORT);
//			}
//		});
//		iReportButton.setToolTipText(
//				Translatrix.getTranslationString("ReportModule.CreatorDialog.createIReport"));
//		panel.add(iReportButton, CC.xy(6, 2));
//		this.addButton(iReportButton);
		
//		super.mainPanel.add(panel, BorderLayout.SOUTH);
	}
	
	
	private void clearParameter ()
	{
		while (!this.rows.isEmpty())
			this.rows.get(0).removeMe();
		
		for (int i = this.parameterPanelLayout.getRowCount(); i > 1; i--)
			this.parameterPanelLayout.removeRow(i);
	}
	
	
	private void export (int option)
	{
		this.report.setData(null);
		this.report.setModifiedQuery(null);
		this.reportCreator.createReport(this.report, option);
	}
	
	
	
	/* ======================================== */
	// 		CLASS: ParameterRow
	/* ======================================== */
	
	private class ParameterRow
	{
		/* ======================================== */
		// 		MEMBERS
		/* ======================================== */
		
		private ReportParameter	parameter;
		
//		private JCheckBox		useBox;
		
		private JLabel			nameLabel;
		
		private JComponent		valueComponent;
		
		
		
		/* ======================================== */
		// 		CONSTRUCTORS
		/* ======================================== */
		
		public ParameterRow (ReportParameter parameter)
		{
			String	comment;
			String	label;
			
			this.parameter		= parameter;
			
//			this.useBox			= new JCheckBox(
//					Translatrix.getTranslationString("ReportModule.CreatorDialog.use"));
//			this.useBox.setOpaque(false);
//			this.useBox.setSelected(true);
//			this.useBox.setEnabled(this.parameter.isOptional());
			
			label				= this.parameter.getLabel();
			if (label == null || label.trim().length() <= 0)
				label			= this.parameter.getName();
			if (this.parameter.getMendatory())
				label = "<html><i>" + label + "*";
			
			this.nameLabel		= new JLabel(label + ":");
			this.nameLabel.setOpaque(false);
			
			this.valueComponent	= getValueComponent(
					this.parameter.getType(), 
					this.parameter.getDefaultValue());
			
			// create tool tip 
			comment	= new StringBuilder("<html><b>")
					.append(this.parameter.getName())
					.append("</b><br><i>")
					.append(this.parameter.getComment())
					.append("</i></html>")
					.toString();
			
			// set the tool tip
			this.nameLabel.setToolTipText(comment);
//			this.useBox.setToolTipText(this.parameter.getComment());
			this.valueComponent.setToolTipText(comment);
		}
		
		
		
		/* ======================================== */
		// 		CLASS BODY
		/* ======================================== */
		
		public void appendMe ()
		{
			int row;
			rows.add(this);
			
			parameterPanelLayout.appendRow(DEFAULT_ROWSPEC);
			row	= parameterPanelLayout.getRowCount();
			parameterPanelLayout.appendRow(SPACER_ROWSPEC);
			
//			parameterPanel.add(this.useBox,			CC.xy(COLUMN_USE, row));
			parameterPanel.add(this.nameLabel,		CC.xy(COLUMN_NAME, row));
			parameterPanel.add(this.valueComponent,	CC.xy(COLUMN_VALUE, row));
			
			ReportCreatorDialog.this.validate();
		}
		
		
		public void removeMe ()
		{
			rows.remove(this);
			
			/* no need to remove the row of the layout,
			 * as all rows will be removed or none.
			 */
			
//			parameterPanel.remove(this.useBox);
			parameterPanel.remove(this.nameLabel);
			parameterPanel.remove(this.valueComponent);
			
			ReportCreatorDialog.this.validate();
		}
		
		
		public ReportParameter getParameter ()
		{
			return this.parameter;
		}
		
		
		
		/* ======================================== */
		// 		HELP METHODS
		/* ======================================== */
		
		private JComponent getValueComponent (String type, String defaultValue)
		{
			JComponent	c;
			Integer		id;
			
			
			if (type != null)
			{
				try
				{
					id	= Integer.parseInt(type);
					
					// if this doesn't throws a NumberFormatException, this is the ID of a ParameterQuery
					// TODO: 
					c	= createParameterQueryValueComponent(id, defaultValue);
				}
				catch (NumberFormatException nfe)
				{
					// it's not a ParameterQuery
					if (type.equals(ReportParameter.TYPE_BOOLEAN))
					{
						// BOOLEAN - CHECK BOX
						boolean		selected;
						JCheckBox	box	= new JCheckBox(new AbstractAction()
						{
							private static final long	serialVersionUID	= 1L;
	
							public void actionPerformed(ActionEvent e)
							{
								JCheckBox	box	= (JCheckBox) e.getSource();
								parameter.setValue(box.isSelected() ? Boolean.TRUE : Boolean.FALSE);
							}
						});
						box.setOpaque(false);
						c			= box;
						selected	= defaultValue != null && defaultValue.trim().toUpperCase().equals("TRUE");
						box.setSelected(selected);
						parameter.setValue(selected ? Boolean.TRUE : Boolean.FALSE);
					}
					else if (type.equals(ReportParameter.TYPE_DATE))
					{
						// DATE - DATE CHOOSER
						JDateChooser	chooser	= GECAMedGuiUtils.getDateChooser(false);
						chooser.addPropertyChangeListener("date", new PropertyChangeListener()
						{
							public void propertyChange(PropertyChangeEvent evt)
							{
								JDateChooser	chooser	= (JDateChooser) evt.getSource();
								Calendar		cal		= chooser.getCalendar();
	//							Date			date	= new Date(cal.getTimeInMillis());
								parameter.setValue(cal);
							}
						});
						try
						{
							chooser.setDate(dateFormatter.parse(defaultValue));
						}
						catch (ParseException e)
						{
							logger.log(Level.WARN, "Couldn't parse the default value to a date.");
						}
						parameter.setValue(chooser.getCalendar());
						c	= chooser;
					}
	//				else if (type.equals(ReportParameter.TYPE_DATE_TIME))
	//				{
	//					
	//				}
					else if (type.equals(ReportParameter.TYPE_DECIMAL))
					{
						// DOUBLE - NUMERIC TEXT FIELD
						NumericField	field		= new NumericField(defaultValue, true);
	//					field.setText(defaultValue);
						parameter.setValue(field.getDecimal());
						field.addCaretListener(new CaretListener()
						{
							public void caretUpdate(CaretEvent e)
							{
								NumericField	field	= (NumericField) e.getSource();
								parameter.setValue(field.getDecimal());
							}
						});
						c	= field;
					}
					else if (type.equals(ReportParameter.TYPE_INTEGER))
					{
						// INTEGER - NUMERIC TEXT FIELD
						NumericField	field	= new NumericField(defaultValue, false);
	//					field.setText(defaultValue);
						try
						{
							parameter.setValue(field.getLong());
						}
						catch (NumberFormatException e)
						{
							parameter.setValue(null);
						}
						field.addCaretListener(new CaretListener()
						{
							public void caretUpdate(CaretEvent e)
							{
								NumericField	field	= (NumericField) e.getSource();
								try
								{
									parameter.setValue(field.getLong());
								} 
								catch (NumberFormatException nfe) {}
							}
						});
						c	= field;
					}
					else if (type.equals(ReportParameter.TYPE_STRING))
					{
						// STRING - TEXT FIELD
						JTextField	field	= new JTextField();
						field.setText(defaultValue);
						parameter.setValue(field.getText());
						field.addCaretListener(new CaretListener()
						{
							public void caretUpdate(CaretEvent e)
							{
								JTextField	field	= (JTextField) e.getSource();
								parameter.setValue(field.getText());
							}
						});
						c	= field;
					}
					else c	= null;
				}
			}
			else c	= null;
			
			return c;
		}



		private JComboBox createParameterQueryValueComponent (Integer id, String defaultValue)
		{
			ComboBoxElement<?>		item;
			ComboBoxElement<?>[]	items;
			String			label;
			Object			value;
			JComboBox		box;
			ParameterQuery	parameterQuery;
			List<?>			options;
			Object[]		option;
			int				index;
			int				selectedIndex	= -1;
			
			
			parameterQuery	= ReportManagerBean.getInstance().getParameterQuery(id);
			options			= ReportManagerBean.getInstance().getParameterOptions(parameterQuery.getQuery());
			
			if (options == null)
				return new JComboBox();
			
			items			= new ComboBoxElement[options.size()];
			index			= 0;
			
			for (Object o : options)
			{
				if (o == null)
					continue;
				
				if (o.getClass().isArray())
				{
					option	= (Object[]) o;
					value	= option[0];
					label	= String.valueOf(option[option.length > 1 ? 1 : 0]);
				}
				else
				{
					value	= o;
					label	= String.valueOf(o);
				}
				
				item	= new ComboBoxElement<Object>(label, value, 
						ParameterPanel.queryParameterStringValue(value));
				items[index]	= item;
				if (defaultValue != null && defaultValue.equals(item.getValue(1)))
					selectedIndex	= index;
				
				index++;
			}
			
			box	= new JComboBox(items);
			box.addItemListener(new ItemListener()
			{
				public void itemStateChanged(ItemEvent e)
				{
					if (e.getStateChange() == ItemEvent.DESELECTED)
						return;
					
					JComboBox			box		= (JComboBox) e.getSource();
					ComboBoxElement<?>	item	= (ComboBoxElement<?>) box.getSelectedItem();
					if (item == null)
					{
						parameter.setValue(null);
					}
					else
					{
						parameter.setValue(item.getValue());
						if (parameter.getValue() == null)
							parameter.setMendatory(Boolean.FALSE);
					}
				}
			});
			box.setRenderer(new LineColorListRenderer());
			
			if (selectedIndex < box.getItemCount())
				box.setSelectedIndex(selectedIndex);
			else 
				box.setSelectedItem(null);
			
			item	= (ComboBoxElement<?>) box.getSelectedItem();
			if (item == null)
				parameter.setValue(null);
			else
				parameter.setValue(item.getValue());
			
			return box;
		}
	}
}
