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

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.font.TextAttribute;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.EventObject;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;
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.utils.GECAMedGuiUtils;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.core.gui.widgets.NamedComponent;
import lu.tudor.santec.gecamed.formeditor.gui.FormEditorModule;
import lu.tudor.santec.gecamed.formeditor.gui.FormWidgets;
import lu.tudor.santec.gecamed.formeditor.gui.controller.ColorChangeListener;
import lu.tudor.santec.gecamed.formeditor.gui.controller.FormController;
import lu.tudor.santec.gecamed.formeditor.gui.exception.DuplicateEntryException;
import lu.tudor.santec.gecamed.formeditor.gui.model.ComboBoxElement;
import lu.tudor.santec.gecamed.formeditor.gui.model.DatabaseReferences;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormModel;
import lu.tudor.santec.gecamed.formeditor.gui.view.dialog.CSVImportDialog;
import lu.tudor.santec.gecamed.formeditor.gui.view.dialog.StaticDataCSVImportDialog;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.AbstractRenderer;
import org.jfree.chart.renderer.xy.DefaultXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

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

public class EditableChart extends EditableComponent /*implements FormDatabaseChangedListener*/ {
	
	private static final long serialVersionUID = 1L; 

	private final int LISTENER_METHOD_SIZE = 1;
	
	
	public static final String KEY_REF_COMPONENTS = "chart.series.referencingComponents";
	public static final String CREATION_DATE_REF = EditableComponent.CREATION_DATE_REF;
	
	public ComboBoxElement<String> creationDateElement = new ComboBoxElement<String>(
			Translatrix.getTranslationString("formeditor.creation_date"), CREATION_DATE_REF);
	
	/* ======================================== */
	// 			CHART TYPES
	/* ======================================== */
	public static final int TIME_SERIES_CHART = 0;
	public static final int XY_SERIES_CHART = 1;
//	public static final int BAR_CHART = 2;
//	public static final int PIE_CHART = 3;
	public static final int[] CHART_TYPES = {TIME_SERIES_CHART, XY_SERIES_CHART};
	/* ======================================== */
	
	
	/* ======================================== */
	// 			PLOT ORIENTATIONS
	/* ======================================== */
	public static final int VERTICAL_ORIENTATION = 0;
	public static final int HORIZONTAL_ORIENTATION = 1;
	/* ======================================== */
	
	
	/* ======================================== */
	// 			GENERAL OPTIONS
	/* ======================================== */
	
	// reload listener
	private Collection<ReloadListener> reloadListeners = new LinkedList<ReloadListener>();
	
	// series options
	private JButton removeSeriesButton;
	
	
	// Chart options
	private JFreeChart chart;
	private XYPlot plot;
	private Marker marker = new ValueMarker(0.8f);
	private AbstractRenderer renderer;
	
	
	private int chartType = TIME_SERIES_CHART;
	private boolean showLegend;
	private TextTitle title;
//	private boolean antiAlias = true;
//	private Color markerColor = Color.LIGHT_GRAY;
//	private float markerAlpha = 0.8f;
//	private double markerRange = 10.0;
	
	private Vector<DataSeriesInfo> seriesInfos = new Vector<DataSeriesInfo>();
	
//	private String title = "";
//	private String xAxisLabel = "";
//	private String yAxisLabel = "";
//	private int plotOrientation = VERTICAL_ORIENTATION;
	/* ======================================== */
	
	
	private DatabaseReferences database;
	
	
	/* ======================================== */
	// 			OPTION-PANEL COMPONENTS
	/* ======================================== */
	private JComboBox seriesTypeChooser;
	private JComboBox seriesChooser;
	private GECAMedColorChooser seriesPaintChooser;
	private JCheckBox showShapeBox;
	private JTextField seriesNameField;
	private JComboBox xValueRefChooser;
	private JComboBox yValueRefChooser;
	private JDateChooser sinceWhenChooser;
	private JButton clearDateButton;
	private NumericField limitField;
	private JCheckBox isStaticBox;
	private JButton staticDataButton;
	/* ======================================== */
	
	
	
	/* ======================================== */
	// 			CONSTRUCTOR
	/* ======================================== */
	
	public EditableChart() 
	{
		this.paintChart();
	}
	

	/* ======================================== */
	// 			OVERRIDE METHODS
	/* ======================================== */
	
//	@Override
//	public void init (CellConstraints constraints, FormEditorModel model, byte formModelType) {
//		/* ======================================== */
//		super.init(constraints, model, formModelType);
//		/* ======================================== */
//	}

	@Override
	protected void addListenerMethodNames() {
		listenerMethodNames[SUPER_LISTENER_METHOD_SIZE] = "conentReloaded";
	}

	@Override
	public JComponent copyComponent(FormModel model) 
	{
//		EditableChart chart;
//		if (model.isPreview())
//			 chart = new EditableChart(); 
//		else chart = this;
		
		EditableChart chart = this;

		chart.databaseReferenceChanged(model.getDBReferences());
		chart.setStructure(this.getStructure());
		
		FormController controller = new FormController(model);
		
		if (getScriptAt(SUPER_LISTENER_METHOD_SIZE).length() > 0)
		{
			controller.setCodeAt(FormController.CONTENT_RELOADED, 
					getScriptAt(SUPER_LISTENER_METHOD_SIZE));
			chart.addReloadListener(controller);
		}
		addDefaultListener(chart, controller);
		
		return chart;
	}

	@Override
	public JComponent getComponentForOptions ()
	{
		return super.getComponent();
	}

	@Override
	public JComponent getComponentForProperty ()
	{
		return super.getComponent();
	}
	
	@Override
	public JComponent getComponent()
	{
//		return this;
		return component;
	}

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

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

	@Override
	public String getStructure() {
		/* ======================================== */
		Properties structure = new Properties();
		
		/* **********  WRITE THE PROPERTIES  ********** */
		try {
			/* ---------------------------------------- */
			
			// -- CHAR TYPE
			structure.setProperty("chart.type", String.valueOf(this.chartType));
			
			
			/* ---------------------------------------- */
			// get the info of the data series
			/* ---------------------------------------- */
			StringBuffer references = new StringBuffer();
			StringBuffer colors = new StringBuffer();
			StringBuffer shapes = new StringBuffer();
			StringBuffer names = new StringBuffer();
			StringBuffer sinceWhen = new StringBuffer();
			StringBuffer limit = new StringBuffer();
			StringBuffer staticData = new StringBuffer();
			
			for (DataSeriesInfo info : seriesInfos) {
				/* ---------------------------------------- */
				
				if (info.isStatic) {
					info.xReference = null;
					info.yReference = null;
					info.sinceWhen = null;
					info.limit = 0;
				} else {
					info.staticData = null;
				}
				
				// ## X- and Y-References
				if (info.isStatic) {
					references.append("NULL;");
				} else {
					references.append(info.xReference);
					references.append("-");
					references.append(info.yReference);
					references.append(";");
				}
				
				
				// ## series paint
				Color c = info.paint;
				colors.append(c.getRGB());
				colors.append(";");
				
				
				// ## show shape
				shapes.append(info.showShape ? 1 : 0);
				shapes.append(";");
				
				
				// ## series names
				names.append(info.name);
				names.append(";");
				
				
				// ## since when
				Calendar cal = info.sinceWhen;
				Long time;
				if (cal == null) {
					time = null;
				} else {
					time = new Long(cal.getTimeInMillis());
				}
				sinceWhen.append(time);
				sinceWhen.append(";");
				
				
				// ## limit
				limit.append(info.limit);
				limit.append(";");
				
				
				// ## static data
				staticData.append(info.staticData);
				staticData.append("\n#\n");
				/* ---------------------------------------- */
			}
			
			structure.setProperty(KEY_REF_COMPONENTS, references.toString());
			structure.setProperty("chart.series.paint", colors.toString());
			structure.setProperty("chart.series.shape", shapes.toString());
			structure.setProperty("chart.series.names", names.toString());
			structure.setProperty("chart.series.sinceWhen", sinceWhen.toString());
			structure.setProperty("chart.series.limit", limit.toString());
			structure.setProperty("chart.series.staticData", staticData.toString());
			/* ---------------------------------------- */
			/* ---------------------------------------- */
			
			/* ---------------------------------------- */
		} catch (Exception e) {
			logger.log(Level.ERROR, "Error while storing the JFreeChart " + this.getKey(), e);
			return null;
		}
		
		
		// -- CHART
		structure.setProperty("chart.background.color", String.valueOf(
				((Color) chart.getBackgroundPaint()).getRGB()));
		structure.setProperty("chart.border.color", String.valueOf(
				((Color) chart.getBorderPaint()).getRGB()));
		structure.setProperty("chart.visible", String.valueOf(
				chart.getTitle().isVisible()));
		structure.setProperty("chart.title.color", String.valueOf(
				((Color) chart.getTitle().getPaint())));
		
		
		// ANTI ALIAS
		try {
			structure.setProperty("chart.antiAlias", String.valueOf(chart.getAntiAlias()));
		} catch (Exception e) {
			logger.info("chart.antiAlias property is wrong");
		}
		
		
		// TITLE
		try {
			/* ---------------------------------------- */
			structure.setProperty("chart.title.text", chart.getTitle().getText());
			structure.setProperty("chart.title.color", String.valueOf(
					((Color) chart.getTitle().getPaint()).getRGB())); 
			loadFontIntoProperties("chart.title", chart.getTitle().getFont(), structure);
			/* ---------------------------------------- */
		} catch (Exception e) {
			logger.warn("one of the chart.title.* properties is wrong");
		}
		
		
		// -- PLOT
		structure.setProperty("chart.plot.background.color", String.valueOf(
				((Color) ((XYPlot)plot).getBackgroundPaint()).getRGB()));
		structure.setProperty("chart.plot.orientation", String.valueOf(
				getPlotOrientation(((XYPlot)plot).getOrientation())));
		structure.setProperty("chart.plot.outline.color", String.valueOf(
				((Color) ((XYPlot)plot).getOutlinePaint()).getRGB()));
		
		
		// AXIS LABEL
		try {
			/* ---------------------------------------- */
			// x-axis
			ValueAxis xAxis = ((XYPlot)plot).getDomainAxis();
			structure.setProperty("chart.xAxis.label", xAxis.getLabel());
			structure.setProperty("chart.xAxis.color", String.valueOf(
					((Color) xAxis.getLabelPaint()).getRGB()));
			loadFontIntoProperties("chart.xAxis", xAxis.getLabelFont(), structure);
			structure.setProperty("chart.xAxis.color", String.valueOf(
					((Color) xAxis.getLabelPaint()).getRGB()));
			structure.setProperty("chart.xAxis.autoRange", String.valueOf(
					xAxis.isAutoRange()));
			structure.setProperty("chart.xAxis.tickLabelsVisible", String.valueOf(
					xAxis.isTickLabelsVisible()));
			structure.setProperty("chart.xAxis.ticksVisible", String.valueOf(
					xAxis.isTickMarksVisible()));
			/* ---------------------------------------- */
		} catch (Exception e) {
			logger.warn("one of the chart.xAxis.* properties is wrong");
		}
		
		try {
			/* ---------------------------------------- */
			// y-axis
			ValueAxis yAxis = ((XYPlot)plot).getRangeAxis();
			structure.setProperty("chart.yAxis.label", yAxis.getLabel());
			structure.setProperty("chart.yAxis.color", String.valueOf(
					((Color) yAxis.getLabelPaint()).getRGB()));
			loadFontIntoProperties("chart.yAxis", yAxis.getLabelFont(), structure);
			structure.setProperty("chart.yAxis.color", String.valueOf(
					((Color) yAxis.getLabelPaint()).getRGB()));
			structure.setProperty("chart.yAxis.tickLabelsVisible", String.valueOf(
					yAxis.isTickLabelsVisible()));
			structure.setProperty("chart.yAxis.ticksVisible", String.valueOf(
					yAxis.isTickMarksVisible()));
			/* ---------------------------------------- */
		} catch (Exception e) {
			logger.warn("one of the chart.yAxis.* properties is wrong");
		}
		
		
		// -- MARKER COLOR
		Color markerColor = (Color) this.marker.getPaint();
		structure.setProperty("chart.marker.color", String.valueOf(markerColor.getRGB()));
		
		// -- MARKER ALPHA
		structure.setProperty("chart.marker.alpha", String.valueOf(this.marker.getAlpha()));
		
		// -- MARKER RANGE
		structure.setProperty("chart.marker.range", String.valueOf(((ValueMarker)this.marker).getValue()));
		
		// -- SHOW LEGEND
		structure.setProperty("chart.showLegend", String.valueOf(showLegend));
		
		/* **************************************** */
		
		StringWriter writer = new StringWriter();
		try {
			/* ---------------------------------------- */
			structure.store(writer, "");
			/* ---------------------------------------- */
		} catch (IOException e) {
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		
		return writer.toString();
		/* ======================================== */
	}

	@Override
	public void setStructure(String text) {
		/* ======================================== */
		
		Properties structure = new Properties();
		try {
			/* ---------------------------------------- */
			StringReader reader = new StringReader(text);
			structure.load(reader);
			
			/* **********  SET THE PROPERTIES  ********** */
			try {
				/* ---------------------------------------- */
				// -- REFERENCED COMPONENTS
				seriesInfos.clear();
				
				// -- CHAR TYPE
				try {
					this.chartType = Integer.parseInt(structure.getProperty("chart.type"));
				} catch (Exception e) {
					this.chartType = TIME_SERIES_CHART;
				}
				
				
				/* ---------------------------------------- */
				// set the info of the data series
				/* ---------------------------------------- */
				
				String namesProperty = structure.getProperty("chart.series.names");
				String[] names;
				if (namesProperty.contains(";")) {
					names = namesProperty.split(";");
				} else {
					names = new String[0];
				}
				
				
				String[] references = {};
				String[] colors = {};
				String[] shapes = {};
				String[] sinceWhen = {};
				String[] limit = {};
				String[] staticData = {};
				
				
				String keyRefComponents = structure.getProperty(KEY_REF_COMPONENTS);
				String seriesPaint = structure.getProperty("chart.series.paint");
				String seriesShape = structure.getProperty("chart.series.shape");
				String seriesSinceWhen = structure.getProperty("chart.series.sinceWhen");
				String SeriesLimit = structure.getProperty("chart.series.limit");
				String seriesStaticData = structure.getProperty("chart.series.staticData");
				
				if (keyRefComponents != null) {
					references = keyRefComponents.split(";");
				}
				if (seriesPaint != null) {
					colors = seriesPaint.split(";");
				}
				if (seriesShape != null) {
					shapes = seriesShape.split(";");
				}
				if (seriesSinceWhen != null) {
					sinceWhen = seriesSinceWhen.split(";");
				}
				if (SeriesLimit != null) {
					limit = SeriesLimit.split(";");
				}
				if (seriesStaticData != null) {
					staticData = seriesStaticData.split("\n#\n");
				}
				
				
				DataSeriesInfo info;
				for (int index = 0; index < names.length; index++) {
					/* ---------------------------------------- */
					info = new DataSeriesInfo();
					
					// ## series names
					info.name = names[index];
					
					
					if (index < references.length) {
						/* ---------------------------------------- */
						// ## x- and y-references
						if (!references[index].toUpperCase().equals("NULL")) {
							String[] xyValue = references[index].split("-");
							if (xyValue.length >= 2) {
								info.xReference = xyValue[0];
								info.yReference = xyValue[1];
							}
						}
						
						
						// ## series paints
						if (index < colors.length) {
							info.paint = new Color(Integer.parseInt(colors[index]));
						}
						
						
						// ## shapes
						if (index < shapes.length) {
							info.showShape = Integer.parseInt(shapes[index]) > 0;
						}
						
						
						// ## since when
						if (index < sinceWhen.length && !sinceWhen[index].toUpperCase().equals("NULL")) {
							Calendar cal = new GregorianCalendar();
							long millis = Long.parseLong(sinceWhen[index]);
							cal.setTimeInMillis(millis);
							info.sinceWhen  = cal;
						}
						
						// ## limit
						if (index < limit.length) {
							info.limit = Integer.parseInt(limit[index]);
						}
						/* ---------------------------------------- */
					}
					
					
					// ## static data
					String data = staticData[index];
					if (!data.toUpperCase().equals("NULL")) { 
						 info.staticData = data;
						 info.isStatic = true;
					}
					
					
					this.seriesInfos.add(info);
					/* ---------------------------------------- */
				}
				/* ---------------------------------------- */
				/* ---------------------------------------- */
				
				/* ---------------------------------------- */
			} catch (Exception e) {
				logger.log(Level.ERROR, "Error while loading the JFreeChart " + this.getKey(), e);
				
				seriesInfos.clear();
			}
//			this.databaseReferenceChanged(database);
			
			
			// -- CHART
			chart.setBackgroundPaint(new Color(FormWidgets.parseInt(
					structure.getProperty("chart.background.color"), Color.WHITE.getRGB())));
			chart.setBorderPaint(new Color(FormWidgets.parseInt(
					structure.getProperty("chart.border.color"), Color.BLACK.getRGB())));
			
			
			// ANTI ALIAS
			try {
				/* ---------------------------------------- */
				chart.setAntiAlias(Boolean.parseBoolean(structure.getProperty("chart.antiAlias")));
				/* ---------------------------------------- */
			} catch (Exception e) {
				logger.log(Level.WARN, "Incorrect type for antiAlias");
				chart.setAntiAlias(true);
			}
			
			
			// TITLE
			try {
				/* ---------------------------------------- */
				this.title = chart.getTitle();
				chart.getTitle().setVisible(FormWidgets.parseBool(structure.getProperty("chart.title.visible"), true));
				chart.setTitle(structure.getProperty("chart.title.text"));
				chart.getTitle().setPaint(new Color(FormWidgets.parseInt(
						structure.getProperty("chart.title.color"), Color.BLACK.getRGB())));
				chart.getTitle().setFont(loadPropertiesIntoFont("chart.title", 
						chart.getTitle().getFont(), structure));
				/* ---------------------------------------- */
			} catch (Exception e) {
				logger.warn("Incorrect type for title");
				logger.log(Level.WARN, e.getMessage(), e);
				chart.setTitle("");
			}
			
			
			// -- PLOT
			((XYPlot)plot).setBackgroundPaint(new Color(FormWidgets.parseInt(
					structure.getProperty("chart.plot.background.color"), Color.LIGHT_GRAY.getRGB())));
			((XYPlot)plot).setOutlinePaint(new Color(FormWidgets.parseInt(
					structure.getProperty("chart.plot.outline.color"), Color.BLACK.getRGB())));
			
			
			// ORIENTATION
			try {
				/* ---------------------------------------- */
				((XYPlot)plot).setOrientation(getPlotOrientation(
						Integer.parseInt(structure.getProperty("chart.plot.orientation"))));
//				this.plotOrientation = Integer.parseInt(structure.getProperty("plotOrientation"));
				/* ---------------------------------------- */
			} catch (Exception e) {
				logger.warn("Incorrect type for plotOrientation");
				((XYPlot)plot).setOrientation(PlotOrientation.VERTICAL);
//				this.plotOrientation = VERTICAL_ORIENTATION;
			}
			
			
			// AXIS LABEL
			try {
				/* ---------------------------------------- */
				ValueAxis xAxis = ((XYPlot)plot).getDomainAxis();
				xAxis.setLabel(structure.getProperty("chart.xAxis.label"));
				xAxis.setLabelPaint(new Color(FormWidgets.parseInt(
						structure.getProperty("chart.xAxis.color"), Color.black.getRGB())));
				xAxis.setLabelFont(loadPropertiesIntoFont("chart.xAxis", xAxis.getLabelFont(), structure));
				xAxis.setAutoRange(FormWidgets.parseBool(
						structure.getProperty("chart.xAxis.autoRange"), true));
				xAxis.setTickLabelsVisible(FormWidgets.parseBool(
						structure.getProperty("chart.xAxis.tickLabelsVisible"), true));
				xAxis.setTickMarksVisible(FormWidgets.parseBool(
						structure.getProperty("chart.xAxis.ticksVisible"), true));
				
				
				ValueAxis yAxis = ((XYPlot)plot).getRangeAxis();
				yAxis.setLabel(structure.getProperty("chart.yAxis.label"));
				yAxis.setLabelPaint(new Color(FormWidgets.parseInt(
						structure.getProperty("chart.yAxis.color"), Color.black.getRGB())));
				yAxis.setLabelFont(loadPropertiesIntoFont("chart.yAxis", yAxis.getLabelFont(), structure));
				yAxis.setTickLabelsVisible(FormWidgets.parseBool(
						structure.getProperty("chart.yAxis.tickLabelsVisible"), true));
				yAxis.setTickMarksVisible(FormWidgets.parseBool(
						structure.getProperty("chart.yAxis.ticksVisible"), true));
				/* ---------------------------------------- */
			} catch (Exception e) {
				logger.warn("Incorrect type for axis label");
				e.printStackTrace(); // this printStackTrace can stay
				((XYPlot)plot).getDomainAxis().setLabel("");
				((XYPlot)plot).getRangeAxis().setLabel("");
			}
			
			
			// -- MARKER
			
			// COLOR
			try {
				/* ---------------------------------------- */
				this.marker.setPaint(new Color(Integer.parseInt(structure.getProperty("chart.marker.color"))));
				/* ---------------------------------------- */
			} catch (Exception e) {
				logger.warn("Incorrect type for markerColor");
				this.marker.setPaint(Color.LIGHT_GRAY);
			}
			
			// ALPHA
			try {
				this.marker.setAlpha(Float.parseFloat(structure.getProperty("chart.marker.alpha")));			
			} catch (Exception e) {
				logger.warn("Incorrect type for markerAlpha");
				this.marker.setAlpha(0.8f);
			}
			
			// RANGE
			try {
				((ValueMarker)this.marker).setValue(Double.parseDouble(structure.getProperty("chart.marker.range")));			
			} catch (Exception e) {
				logger.warn("Incorrect type for markerRange");
				((ValueMarker)this.marker).setValue(10.0);
			}
			
			// -- SHOW LEGEND
			try {
				this.showLegend = Boolean.parseBoolean(structure.getProperty("chart.showLegend"));
			} catch (Exception e) {
				logger.warn("Incorrect type for show legend");
				this.showLegend = false;
			}
			
			/* **************************************** */
			
			paintChart();
			
			/* ---------------------------------------- */
		} catch (IOException e) {
			logger.log(Level.ERROR, "IOException", e);
		}
		/* ======================================== */
	}
	
	@Override
	public String getText() {
		return "";
	}
	
	@Override
	public void setText(String text) { }
	
	
	@Override
	public JPanel getAddablePanel(JComponent c) {
		/* ======================================== */
		return (JPanel)((EditableComponent)c).getComponent();
		/* ======================================== */
	}

	public void addXYSeries (String name, String xValueRef, String yValueRef, Color color, 
			Calendar sinceWhen, Integer limit, String staticData, boolean isStatic) {
		/* ======================================== */
		seriesInfos.add(new DataSeriesInfo());
		/* ======================================== */
	}
	
	
	
	/* ======================================== */
	// 			GETTER & SETTER
	/* ======================================== */
	
	
	public JFreeChart getChart() {
		return chart;
	}
	
	
	
	/* ======================================== */
	// 			HELP METHODS
	/* ======================================== */
	
	public void paintChart () {
		/* ======================================== */
		/*
		 * as a new chart is going to be created, all made changes need to be
		 * stored and before the creation and loaded afterwards
		 */
		
		Properties properties = null;
		
		// -- CHART
		boolean antiAlias = true;
		Color chartBackgroundColor = Color.LIGHT_GRAY;
		Color chartBorderColor = Color.BLACK;
		
		// title
		String titleLabel = "";
		Color titleColor = Color.BLACK;
		boolean titleVisible = true;
		
		// -- PLOT
		PlotOrientation plotOrientation = PlotOrientation.VERTICAL;
		Color plotBackgroundColor = Color.WHITE;
		Color plotOutlineColor = Color.BLACK;
		
		// x-axis
		String xAxisLabel = "";
		Color xAxisColor = Color.BLACK;
		boolean xAxisAutoRange = true;
		boolean xAxisTickLabelsVisible = true;
		boolean xAxisTicksVisible = true;
		
		// y-axis
		String yAxisLabel = "";
		Color yAxisColor = Color.BLACK;
		boolean yAxisTickLabelsVisible = true;
		boolean yAxisTicksVisible = true;
		
		
		// -- MARKER
//		Color markerColor = Color.DARK_GRAY;
//		float markerAlpha = 0.8f;
//		double markerRange = 10.0; 
		
		/* **********  SAVE THE VALUES  ********** */
		if (chart != null) {
			/* ---------------------------------------- */
			properties = new Properties();
			
			// -- CHART
			antiAlias = chart.getAntiAlias();
			chartBackgroundColor = (Color) chart.getBackgroundPaint();
			chartBorderColor = (Color) chart.getBorderPaint();
			
			// title
			if (chart.getTitle() != null) {
				this.title = chart.getTitle();
			} else {
				titleVisible = false;
			}
			titleVisible = this.title.isVisible();
			titleLabel = this.title.getText();
			titleColor = (Color) this.title.getPaint();
			loadFontIntoProperties("chart.title", this.title.getFont(), properties);
			
			// -- PLOT
			plotOrientation = ((XYPlot)plot).getOrientation();
			plotBackgroundColor = (Color) ((XYPlot)plot).getBackgroundPaint();
			plotOutlineColor = (Color) ((XYPlot)plot).getOutlinePaint();
			
			// x-axis
			ValueAxis xAxis = ((XYPlot)plot).getDomainAxis();
			xAxisLabel = xAxis.getLabel();
			loadFontIntoProperties("chart.xAxis", xAxis.getLabelFont(), properties);
			xAxisColor = (Color) xAxis.getLabelPaint();
			xAxisAutoRange = xAxis.isAutoRange();
			xAxisTickLabelsVisible = xAxis.isTickLabelsVisible();
			xAxisTicksVisible = xAxis.isTickMarksVisible();
			
			// y-axis
			ValueAxis yAxis = ((XYPlot)plot).getRangeAxis();
			yAxisLabel = yAxis.getLabel();
			loadFontIntoProperties("chart.yAxis", yAxis.getLabelFont(), properties);
			yAxisColor = (Color) yAxis.getLabelPaint();
			yAxis.setAutoRange(true);
			yAxisTickLabelsVisible = yAxis.isTickLabelsVisible();
			yAxisTicksVisible = yAxis.isTickMarksVisible();
			
			// -- MARKER
//			markerColor = (Color) this.marker.getPaint();
//			markerAlpha = this.marker.getAlpha();
//			markerRange = ((ValueMarker)this.marker).getValue();
			/* ---------------------------------------- */
		}
		/* **************************************** */
		
		
		/* **********  CREATE THE CHART  ********** */
		XYDataset dataset = null;
		
		boolean chartCreated = false;
		if (chart == null) {
			chartCreated = true;
		}
		
		if (database == null
				|| database.getTemplate() == null) {
			/* ---------------------------------------- */
			dataset = createExampleChart();
			
			if (chartCreated) 
				chart = ChartFactory.createXYLineChart(titleLabel, xAxisLabel, yAxisLabel, dataset, 
						plotOrientation, showLegend, true, false);
			/* ---------------------------------------- */
		} else if (this.chartType == TIME_SERIES_CHART) {
			/* ---------------------------------------- */
			dataset = createTimeSeriesChart();

			if (chartCreated) 
				chart = ChartFactory.createTimeSeriesChart(titleLabel, xAxisLabel, yAxisLabel,
					dataset, showLegend, true, false);
			/* ---------------------------------------- */
		} else if (this.chartType == XY_SERIES_CHART) {
			/* ---------------------------------------- */
			dataset = createXYLineChart();
			
			if (chartCreated) 
				chart = ChartFactory.createXYLineChart(titleLabel, xAxisLabel, yAxisLabel, dataset, 
					plotOrientation, showLegend, true, false);
			/* ---------------------------------------- */
		} else {
			chartCreated = false;
		}
		
		if (chartCreated) {
			// the chart was just created
			
			// create the renderer
			this.renderer = new DefaultXYItemRenderer();
			
			// get the plot
			this.plot = chart.getXYPlot();

			// set the marker
			plot.addRangeMarker(marker);
			
			plot.setRenderer((XYItemRenderer)renderer);
			
		} else {
			// the chart wasn't created, just update it
			plot.setDataset(dataset);
			
//			plot.getDomainAxis().setLabel(yAxisLabel);
//			plot.getRangeAxis().setLabel(xAxisLabel);
//			plot.setOrientation(plotOrientation);
//			chart.setTitle(titleLabel);
		}
		
		// create and set the renderer
		for (int i = 0; i < seriesInfos.size(); i++) {
			renderer.setSeriesShape(i, seriesInfos.get(i).showShape ? 
					new Rectangle(-2, -2, 4, 4) : new Rectangle(0, 0));
			renderer.setSeriesStroke(i, new BasicStroke(1.0f, BasicStroke.CAP_BUTT, 
					BasicStroke.JOIN_ROUND, 1.0f, new float[]{1.0f, 0.0f}, 0.0f));
		}
		
		
		/* **************************************** */
		
		/* ---------------------------------------- */
		if (component == null) {
			/* a new component may only be created if none exist
			 * and only before the initialization of the component.
			 * Otherwise the drag and drop function would be lost.
			 */
			component = new ChartPanel(chart);
//			((ChartPanel)component).setChart(chart);
			component.setOpaque(false);
			
			try {
				this.add(component, BorderLayout.CENTER);
			} catch (IllegalArgumentException e) {
				this.add(component, new CellConstraints(1, 1));
				logger.log(Level.ERROR, e.getMessage(), e);
			}
		} 
//		else {
//			// just set the new chart on the existing component
//			((ChartPanel)component).setChart(chart);
//		}
		/* ---------------------------------------- */
		
		/* **********  LOAD THE VALUES  ********** */
		if (properties != null) {
			/* ---------------------------------------- */
			// -- CHART
			chart.setAntiAlias(antiAlias);
			chart.setBackgroundPaint(chartBackgroundColor);
			chart.setBorderPaint(chartBorderColor);
			chart.getTitle().setFont(loadPropertiesIntoFont("chart.title", 
					chart.getTitle().getFont(), properties));
			chart.getTitle().setPaint(titleColor);
			chart.getTitle().setVisible(titleVisible);
			
			// -- PLOTT
			((XYPlot)plot).setBackgroundPaint(plotBackgroundColor);
			((XYPlot)plot).setOutlinePaint(plotOutlineColor);
			
			ValueAxis xAxis = ((XYPlot)plot).getDomainAxis();
			xAxis.setLabelFont(loadPropertiesIntoFont("chart.xAxis", 
					xAxis.getLabelFont(), properties));
			xAxis.setLabelPaint(xAxisColor);
			xAxis.setAutoRange(xAxisAutoRange);
			xAxis.setTickLabelsVisible(xAxisTickLabelsVisible);
			xAxis.setTickMarksVisible(xAxisTicksVisible);
			
			ValueAxis yAxis = ((XYPlot)plot).getRangeAxis();
			yAxis.setLabelFont(loadPropertiesIntoFont("chart.yAxis",
					yAxis.getLabelFont(), properties));
			yAxis.setLabelPaint(yAxisColor);
			yAxis.setTickLabelsVisible(yAxisTickLabelsVisible);
			yAxis.setTickMarksVisible(yAxisTicksVisible);
			
			// -- MARKER
			
			
			
			// -- RENDERER
			for (int index = 0; index < seriesInfos.size(); index++) {
				renderer.setSeriesPaint(index, seriesInfos.get(index).paint);
			}
			
//			xAxis.configure();
//			yAxis.configure();
			/* ---------------------------------------- */
		}
		/* **************************************** */
		
		/* ======================================== */
	}
	
	
	@Override
	public void saved () {
		this.paintChart();
		
		fireContentReloaded();
	}
	
	
	/**
	 * Creates a dummy chart. This is used, in the form editor to show 
	 * how it could look like later on ...
	 */
	private XYDataset createExampleChart () {
//			String titleLabel, String xAxisLabel, String yAxisLabel,
//			PlotOrientation plotOrientation) {
		/* ---------------------------------------- */
		// initialize dataseries
		XYSeriesCollection dataset = new XYSeriesCollection();
		
		for (int index = 0; index < seriesInfos.size(); index++) {
			/* ---------------------------------------- */
			/* **********  CREATE EXAMPLE DATA  ********** */
			DataSeriesInfo info = seriesInfos.get(index);
			
			TreeMap<Object, Number> map;
			
			if (info.isStatic) {
				map = convertStaticData(info.staticData);
			} else {
				/* ---------------------------------------- */
				map = new TreeMap<Object, Number>();
				int a = index * 10;
				for (double x = -10; x < 10; x += 2) {
	//				cal.add(Calendar.WEEK_OF_YEAR, 1);
					map.put(x, 0.5 * x + a - (index / 2 * 10));
				}
			}
			/* **************************************** */
			
			// initialize series
			XYSeries series = new XYSeries(seriesInfos.get(index).name, true, true);
			
			for (Object xValue : map.keySet()) {
				/* ---------------------------------------- */
				Number yValue = map.get(xValue);
				try {
					series.add((Double)xValue, yValue);
				} catch (Exception e) {
					logger.warn("A time series was added with the same time as an existing one. " +
							"The second time series was left out.");
					logger.log(Level.WARN, e.getMessage(), e);
					continue;
				}
				/* ---------------------------------------- */
			}
			dataset.addSeries(series);
			/* ---------------------------------------- */
		}
		/* **************************************** */
		
//		// CREATE CHART
//		if (chart == null) {
//			chart = ChartFactory.createXYLineChart(titleLabel, xAxisLabel, yAxisLabel, dataset, 
//					plotOrientation, showLegend, true, false);
//		} else {
//			chart.setTitle(titleLabel);
//			
//			XYPlot plot = chart.getXYPlot();
//			plot.setDataset(dataset);
//			plot.getDomainAxis().setLabel(yAxisLabel);
//			plot.getRangeAxis().setLabel(xAxisLabel);
//			plot.setOrientation(plotOrientation);
//		}
		
		return dataset;
		/* ======================================== */
	}
	
	
	/**
	 * Creates a TimeSeriesChart.
	 * 
	 * @param titleLabel
	 * @param xAxisLabel
	 * @param yAxisLabel
	 */
	private XYDataset createTimeSeriesChart () {
//			String titleLabel, String xAxisLabel, String yAxisLabel,
//			PlotOrientation plotOrientation) {
		/* ======================================== */
		TimeSeriesCollection dataset = new TimeSeriesCollection();
		
		for (DataSeriesInfo info : seriesInfos) {
			/* ---------------------------------------- */
			TreeMap<Object, Number> map;
			
			if (info.isStatic) {
				map = convertStaticData(info.staticData);
			} else {
				try {
					// load the data from the db
					map = database.getChartValues(info.sinceWhen, info.limit,
							CREATION_DATE_REF, info.yReference);
				} catch (DuplicateEntryException e) {
					// TODO: show error message to user and proceed as normal
					map = e.getData();
				}
			}
			
			// initialize series
			TimeSeries series = new TimeSeries(info.name, Second.class);
			for (Object xValue : map.keySet()) {
				/* ---------------------------------------- */
				Number yValue = map.get(xValue);
				try {
					series.add(new Second((Date)xValue), yValue);
				} catch (Exception e) {
					logger.warn("A time series was added with the same time as an existing one. " +
							"The second time series was left out.");
					continue;
				}
				/* ---------------------------------------- */
			}
			dataset.addSeries(series);
			/* ---------------------------------------- */
		}
		
//		// CREATE CHART
//		if (chart == null) {
//			chart = ChartFactory.createTimeSeriesChart(titleLabel, xAxisLabel, yAxisLabel,
//					dataset, showLegend, true, false);
//			
//			this.plot = chart.getXYPlot();
//			
//		} else {
//			chart.setTitle(titleLabel);
//			
//			XYPlot plot = chart.getXYPlot();
//			plot.setDataset(dataset);
//			plot.getDomainAxis().setLabel(yAxisLabel);
//			plot.getRangeAxis().setLabel(xAxisLabel);
//			plot.setOrientation(plotOrientation);
//		}
//		
//		((XYPlot)plot).setOrientation(plotOrientation);
		
		return dataset;
		/* ======================================== */
	}
	
	/**
	 * Creates a XYLineChart 
	 * 
	 * @param titleLabel
	 * @param xAxisLabel
	 * @param yAxisLabel
	 * @param plotOrientation
	 */
	private XYDataset createXYLineChart () {
//			String titleLabel, String xAxisLabel, String yAxisLabel,
//			PlotOrientation plotOrientation) {
		/* ======================================== */
		// initialize dataseries
		XYSeriesCollection dataset = new XYSeriesCollection();
		
		for (DataSeriesInfo info : seriesInfos) {
			/* ---------------------------------------- */
			// load the data from the db
			TreeMap<Object, Number> map;
			
			if (info.isStatic) {
				map = convertStaticData(info.staticData);
			} else {
				try {
					map = database.getChartValues(info.sinceWhen, info.limit, 
							info.xReference, info.yReference);
				} catch (DuplicateEntryException e) {
					// TODO: show error message to user and proceed as normal
					map = e.getData();
				}
			}
			
			// initialize series
			XYSeries series = new XYSeries(info.name, true, true);
			for (Object xValue : map.keySet()) {
				/* ---------------------------------------- */
				Number yValue = map.get(xValue);
				try {
					if (xValue instanceof Date) {
						xValue = new Double(((Date)xValue).getTime());
					}
					series.add((Double)xValue, yValue);
				} catch (Exception e) {
					logger.warn("A time series was added with the same time as an existing one. " +
							"The second time series was left out.");
					logger.log(Level.WARN, e.getMessage(), e);
					continue;
				}
				/* ---------------------------------------- */
			}
			dataset.addSeries(series);
			/* ---------------------------------------- */
		}
		
		return dataset;
		/* ======================================== */
	}
	
	
	private TreeMap<Object, Number> convertStaticData (String data) {
		/* ======================================== */
		TreeMap<Object, Number> map = new TreeMap<Object, Number>();
		
		// TODO: convert static data
		String[] rows = data.split("\n");
		for (String row : rows) {
			if (row.contains(";")) {
				/* ---------------------------------------- */
				String[] valuePair = row.split(";");
				
				if (valuePair.length >= 2
						&& !valuePair[0].equals("")
						&& !valuePair[1].equals("")) {
					/* ---------------------------------------- */
					try {
						Double x = Double.parseDouble(valuePair[0].replace(",", "."));
						Double y = Double.parseDouble(valuePair[1].replace(",", "."));
						
						map.put(x, y);
					} catch (NumberFormatException e) {
						continue;
					}
					/* ---------------------------------------- */
				}
				/* ---------------------------------------- */
			}
		}
		
		return map;
		/* ======================================== */
	}
	
	@Override
	public DefaultOptionPanel createOptionPanel () {
		/* ======================================== */
		DefaultOptionPanel optionPanel = super.createOptionPanel();
		
//		// TODO: CREATE OPTION PANEL
//		// --  DEFINE THE PANEL
//		JPanel optionPanel = new JPanel(new FormLayout("f:155px", 
//				"f:p, 2dlu, " + 	// series type
//				"f:p, 2dlu, " + 	// series entry (ComboBox) + add button
//				"f:p, 2dlu, " + 	// name
//				"f:p, 2dlu, " + 	// static data box
//				"f:p, 2dlu, " + 	// color
//				"f:p, 2dlu, " + 	// show marker
//				"f:p, 2dlu, " + 	// x-key
//				"f:p, 2dlu, " + 	// y-key
//				"f:p, 2dlu, " + 	// sinceWhen
//				"f:p, 2dlu, " + 	// limit
//				"f:p, 2dlu, " + 	// static data
//				"f:p, 2dlu, " + 	// show legend
//				"0dlu"));
//		optionPanel.setOpaque(false);
		CellConstraints cc = new CellConstraints();
//		int row = -1;

		boolean modified = model.getView().isModified();
		
		// -- SERIES TYPE CHOOSER
		if (seriesTypeChooser == null) {
			Vector<ComboBoxElement<Integer>> seriesTypes = new Vector<ComboBoxElement<Integer>>();
			
			// add the types
			for (int type : CHART_TYPES) {
				/* ---------------------------------------- */
				seriesTypes.addElement(new ComboBoxElement<Integer>(
						Translatrix.getTranslationString("formeditor.chart_type"+type), 
						type));
				/* ---------------------------------------- */
			}
			
			seriesTypeChooser = new JComboBox(seriesTypes);
			
			seriesTypeChooser.addActionListener(new ActionListener() {
				/* ======================================== */
				public void actionPerformed(ActionEvent e) {
					/* ---------------------------------------- */
					int newType = (Integer)((ComboBoxElement<?>)seriesTypeChooser.
							getSelectedItem()).getValue();
					if (chartType != newType) {
						chartType = newType;
						FormWidgets.setNamedComponentVisibility(xValueRefChooser, chartType == XY_SERIES_CHART);
					}
					/* ---------------------------------------- */
				}
				/* ======================================== */
			});
		}
		
		for (int index = 0; index < seriesTypeChooser.getComponentCount(); index++) {
			/* ---------------------------------------- */
			if (((ComboBoxElement<?>)seriesTypeChooser.getItemAt(index)).getValue().equals(chartType)) {
				seriesTypeChooser.setSelectedIndex(index);
				break;
			}
			/* ---------------------------------------- */
		}
		
		optionPanel.appendRow(new NamedComponent(
				Translatrix.getTranslationString("formeditor.chart_type"), 
				seriesTypeChooser));
		
		
		// -- SERIES CHOOSER
		// ComboBox containing the series (with its referenced component)
		seriesChooser = new JComboBox(seriesInfos);
		seriesChooser.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				selectionChanged();
				/* ---------------------------------------- */
			}
		});
		
//		int selectedIndex = seriesChooser.getSelectedIndex();
//		DataSeriesInfo info = null;
//		if (selectedIndex >= 0) {
//			info = seriesInfos.get(selectedIndex);
//		}

		
		// button to add a new entry
		JButton addButton = new JButton(GECAMedModule.getScaledIcon(GECAMedIconNames.ADD, 20));
		addButton.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
//				addXYSeries("", "", "", Color.RED, null, 0, null, false);
				seriesInfos.add(new DataSeriesInfo());
				seriesChooser.setSelectedIndex(seriesInfos.size()-1);
//				xValueRefChooser.setSelectedIndex(0);
//				yValueRefChooser.setSelectedIndex(0);
//				removeSeriesButton.setEnabled(seriesChooser.getItemCount() > 0);
				selectionChanged();
				/* ---------------------------------------- */
			}
		});
		
		removeSeriesButton = new JButton(GECAMedModule.getScaledIcon(GECAMedIconNames.REMOVE, 20));
		removeSeriesButton.setEnabled(seriesChooser.getItemCount() > 0);
		removeSeriesButton.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				int index = seriesChooser.getSelectedIndex();
				if (index >= 0) {
					seriesInfos.remove(index);
					if (seriesChooser.getItemCount() <= 0) {
						removeSeriesButton.setEnabled(false);
						seriesChooser.setSelectedIndex(-1);
					} else {
						seriesChooser.setSelectedIndex(0);
					}
				}
				/* ---------------------------------------- */
			}
		});
		
		JPanel seriesPanel = new JPanel(new FormLayout("p:g, f:24px, 2px, f:24px", "p, 2px, f:24px"));
		seriesPanel.setOpaque(false);
		
		seriesPanel.add(new NamedComponent(
				Translatrix.getTranslationString("formeditor.seriesEntry"),
				seriesChooser), cc.xywh(1, 1, 4, 1));
		seriesPanel.add(addButton, cc.xy(2, 3));
		seriesPanel.add(removeSeriesButton, cc.xy(4, 3));
		
		optionPanel.appendRow(seriesPanel);
		
//		seriesChooser.setEditable(true);

		// -- SERIES NAME
		// the name of the selected series
		seriesNameField = new JTextField();
		seriesNameField.addKeyListener(new KeyListener() {
			
			public void keyTyped(KeyEvent e) {}
			public void keyPressed(KeyEvent e) {}
			
			public void keyReleased(KeyEvent e) {
				/* ---------------------------------------- */
				int index = seriesChooser.getSelectedIndex();
				seriesInfos.get(index).name = seriesNameField.getText();
				seriesChooser.setSelectedIndex(index);
				/* ---------------------------------------- */
			}
		});
		seriesNameField.addCaretListener(new CaretListener() {
			
			public void caretUpdate(CaretEvent e) {
				/* ---------------------------------------- */
				if (seriesNameField.getText().length() <= 0) {
					seriesNameField.setBackground(FormEditorModule.MARK_WRONG_COLOR);
				} else {
					seriesNameField.setBackground(Color.WHITE);
				}
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(new NamedComponent(
				Translatrix.getTranslationString("formeditor.seriesName"), 
				seriesNameField));
		
		
		// TODO: static box
		// -- IS STATIC / DYNAMIC VLAUE
		isStaticBox = new JCheckBox(
				Translatrix.getTranslationString("formeditor.static"));
		isStaticBox.setOpaque(false);
		isStaticBox.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				int selectionIndex = seriesChooser.getSelectedIndex();
				DataSeriesInfo info = seriesInfos.get(selectionIndex);
				if (info.staticData == null) {
					info.staticData = "";
				}
				
				boolean isStatic = ((JCheckBox)e.getSource()).isSelected();

				info.isStatic = isStatic;
				
				setOptionPanelComponentsVisible(isStatic);
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(isStaticBox);

		
		// -- SERIES PAINT
		seriesPaintChooser = new GECAMedColorChooser(model.getView(), 
				Translatrix.getTranslationString("formeditor.chooseColor"));
//		if (seriesChooser.getSelectedIndex() >= 0) {
//			seriesPaintChooser.setColor(seriesInfos.seriesPaints.get(seriesChooser.getSelectedIndex()));
//		}
		seriesPaintChooser.addColorChangeListener(new ColorChangeListener() {
			
			public void colorChanged(Color c) {}
			
			public void colorChangedByAction (Color c) 
			{
				int index = seriesChooser.getSelectedIndex();
				seriesInfos.get(index).paint = c;
				renderer.setSeriesPaint(index, c);
			}
		});
		
		optionPanel.appendRow(new NamedComponent(
				Translatrix.getTranslationString("formeditor.seriesColor"), 
				seriesPaintChooser));
		
		
		// -- SHOW SHAPE
		showShapeBox = new JCheckBox(
				Translatrix.getTranslationString("formeditor.showMarker"));
		showShapeBox.setOpaque(false);
		showShapeBox.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				JCheckBox box = (JCheckBox) e.getSource();
				DataSeriesInfo info = (DataSeriesInfo) seriesChooser.getSelectedItem();
				
				info.showShape = box.isSelected();
				paintChart();
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(showShapeBox);
		
		
		// -- REFERENCED COMPONENTS
		Vector<?> components = super.getComponentList();
		
		
		// -- REFERENCE CHOOSER
		xValueRefChooser = new JComboBox(components.toArray());
		xValueRefChooser.addItem(CREATION_DATE_REF);
		xValueRefChooser.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				int index = seriesChooser.getSelectedIndex();
				
				if (index >= 0) {
					String component = (String)xValueRefChooser.getSelectedItem();
					seriesInfos.get(index).xReference = String.valueOf(component);
				}
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(new NamedComponent(
				Translatrix.getTranslationString("formeditor.xAxis"), 
				xValueRefChooser));
//		showXValue(chartType == XY_SERIES_CHART);
		
		
		yValueRefChooser = new JComboBox(components);
		yValueRefChooser.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				int index = seriesChooser.getSelectedIndex();
				
				if (index >= 0) {
					String component = (String) yValueRefChooser.getSelectedItem();
					seriesInfos.get(index).yReference = component;
				}
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(new NamedComponent(
				Translatrix.getTranslationString("formeditor.yAxis"), 
				yValueRefChooser));
		
		
		// -- SINCE-WHEN CHOOSER
		sinceWhenChooser = GECAMedGuiUtils.getDateChooser(false);
		
		// disable the date chooser text field
		((JTextField)sinceWhenChooser.getDateEditor().getUiComponent()).setEditable(false);
		
		clearDateButton = new JButton("", GECAMedModule.getScaledIcon(GECAMedIconNames.CANCEL, 16));
		clearDateButton.addActionListener(new ActionListener() {
			/* ======================================== */
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				sinceWhenChooser.setCalendar(null);
				seriesInfos.get(seriesChooser.getSelectedIndex()).sinceWhen = null;
				/* ---------------------------------------- */
			}
			/* ======================================== */
		});
		
		JPanel sinceWhenPanel = new JPanel(new FormLayout("f:p:g, 1px, 20px", "20px"));
		sinceWhenPanel.add(sinceWhenChooser, cc.xy(1, 1));
		sinceWhenPanel.add(clearDateButton, cc.xy(3, 1));
		
		optionPanel.appendRow(new NamedComponent(
				Translatrix.getTranslationString("formeditor.startDate"), 
				sinceWhenPanel));

		
		// -- LIMIT
		// limit of returned values
		limitField = new NumericField("");
		limitField.addKeyListener(new KeyListener() {
			public void keyTyped(KeyEvent e) {}
			public void keyPressed(KeyEvent e) {}
			
			public void keyReleased(KeyEvent e) {
				/* ---------------------------------------- */
				seriesInfos.get(seriesChooser.getSelectedIndex()).limit = limitField.getInt();
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(new NamedComponent(
				Translatrix.getTranslationString("formeditor.lastXForms"), 
				limitField));
		
		
		// -- STATIC DATA BUTTON
		staticDataButton = new JButton(
				Translatrix.getTranslationString("formeditor.loadCSVData"),
				FormEditorModule.getScaledIcon("function.png", 20));
		staticDataButton.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				DataSeriesInfo info = seriesInfos.get(seriesChooser.getSelectedIndex());
				String data = openStaticDataDialog(info.staticData);
				if (data != null) {
					info.staticData = data;
					paintChart();
				}
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(staticDataButton);
		
		
		/* Set the values at startup 
		 * and make the components (not) editable 
		 */
		selectionChanged();
		
		
		// -- SHOW LEGEND
		JCheckBox showLegendBox = new JCheckBox(
				Translatrix.getTranslationString("formeditor.showLegend"));
		showLegendBox.setSelected(showLegend);
		showLegendBox.setOpaque(false);
		showLegendBox.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				/* ---------------------------------------- */
				showLegend = ((JCheckBox)e.getSource()).isSelected();
				paintChart();
				/* ---------------------------------------- */
			}
		});
		
		optionPanel.appendRow(showLegendBox);
		
		/* If the template wasn't modified before clicking the component,
		 * if will won't be modified afterwards.
		 */
		if (!modified) {
			model.getView().setModified(false);
		}
		
		return optionPanel;
		/* ======================================== */
	}
	
	@Override
	public DefaultPropertyPanel createPropertiesPanel () 
	{
		/*
		 * As the properties of the chart can be edited by its own menu
		 * there is no need to build a property menu 
		 */	
		DefaultPropertyPanel panel = super.createPropertiesPanel();
		panel.setVisible(false);
		return panel;
	}
	
	public void databaseReferenceChanged (DatabaseReferences dbRef) {
		/* ======================================== */
		if (dbRef != null && dbRef.getTemplate() != null) {
			database = dbRef;
		}
		/* ======================================== */
	}
	
	private static PlotOrientation getPlotOrientation (int plotOrientation) {
		/* ======================================== */
		switch (plotOrientation) {
		case VERTICAL_ORIENTATION:
			return PlotOrientation.VERTICAL;
		case HORIZONTAL_ORIENTATION:
		default: 
			return PlotOrientation.HORIZONTAL;
		}
		/* ======================================== */
	}
	
	private static int getPlotOrientation (PlotOrientation orientation) {
		/* ======================================== */
		if (orientation.equals(PlotOrientation.HORIZONTAL)) {
			/* ---------------------------------------- */
			return HORIZONTAL_ORIENTATION;
			/* ---------------------------------------- */
		} else /* if (orientation.equals(PlotOrientation.HORIZONTAL)) */ {
			/* ---------------------------------------- */
			return VERTICAL_ORIENTATION;
			/* ---------------------------------------- */
		}
		/* ======================================== */
	}
	
	public static void loadFontIntoProperties (String prefix, Font font, Properties properties) {
		/* ======================================== */
		String key;
		
		for (TextAttribute a : font.getAttributes().keySet()) {
			/* ---------------------------------------- */
			key = prefix + "." + a.toString().
					// replace the Class name and the brackets 
					replace("java.awt.font.TextAttribute", "").	
					replace("(", "").
					replace(")", "");
			
			Object value = font.getAttributes().get(a);
			
			if (value == null) {
				// value is null -> don't add anything null
				continue;
			} else if (value instanceof Color) {
				// value is a color -> get the RGB values
				properties.setProperty(key, String.valueOf(((Color) value).getRGB()));
			} else {
				// value has an own toString method -> make the toString
				properties.setProperty(key, value.toString());
			}
			/* ---------------------------------------- */
		}
		/* ======================================== */
	}
	
	@SuppressWarnings("unchecked")
	public static Font loadPropertiesIntoFont (String prefix, Font font, Properties properties) {
		/* ======================================== */
		Map<TextAttribute, Object> attributes = (Map<TextAttribute, Object>) font.getAttributes();
		String key;
		for (TextAttribute a : attributes.keySet()) {
			/* ---------------------------------------- */
			key = prefix + "." + a.toString().
					// replace the Class name and the brackets 
					replace("java.awt.font.TextAttribute", "").	
					replace("(", "").
					replace(")", "");
			
			String valueString = properties.getProperty(key);
			
			Object value;
			if (valueString == null 
					|| valueString.equals("null")) {
				value = null;
			} else if (valueString.equals("")) {
				value = "";
			} else if (key.equals(prefix + "foreground")
					|| key.equals(prefix + "background")){
				value = new Color(Integer.parseInt(valueString));
			} else {
				try {
					value = Integer.parseInt(valueString);
				} catch (Exception e) { 
					try {
						value = Float.parseFloat(valueString);
					} catch (Exception e1) {
						try {
							value = Boolean.parseBoolean(valueString);
						} catch (Exception e2) {
							value = valueString;
						}
					}
				}
			}
			
			attributes.put((TextAttribute) a, value);
			/* ---------------------------------------- */
		}
		
		return new Font(attributes);
		/* ======================================== */
	}
	
	@Override
	public String getTypeTranslation () {
		/* ======================================== */
		return Translatrix.getTranslationString("formeditor.add_chart");
		/* ======================================== */
	}
	
	
	public String openStaticDataDialog (String oldData) {
		/* ======================================== */
		
		CSVImportDialog dialog = new StaticDataCSVImportDialog(GECAMedModule.getCurrentModule());
		
		Vector<ComboBoxElement<Class<?>>> values = new Vector<ComboBoxElement<Class<?>>>();
		Integer seriesType = (Integer) ((ComboBoxElement<?>)this.seriesTypeChooser.getSelectedItem()).getValue();
		values.add(new ComboBoxElement<Class<?>>("x", 
				seriesType.equals(TIME_SERIES_CHART) ? Date.class : Double.class));
		values.add(new ComboBoxElement<Class<?>>("y", Double.class));
		
		Vector<Vector<ComboBoxElement<Object>>> data = dialog.showDialog(values);
		
		if (data == null) {
			return null;
		}
		
		Iterator<Vector<ComboBoxElement<Object>>> rowIterator = data.iterator();
		
		int xColumn = -1;
		int yColumn = -1;
		
		Vector<ComboBoxElement<Object>> rowData = rowIterator.next();
		for (int index = 0; index < rowData.size(); index++) {

			if ("x".equals(String.valueOf(rowData.get(index)))) {
				xColumn = index;
			} else if ("y".equals(String.valueOf(rowData.get(index)))) {
				yColumn = index;
			}
			
//			if (rowData.get(index).getTranslation().equals("x")) {
//				xColumn = index;
//			} else if (rowData.get(index).getTranslation().equals("y")) {
//				yColumn = index;
//			}
		}
		
		if (xColumn < 0 || yColumn < 0) {
			GECAMedBaseDialogImpl.showMessageDialog(GECAMedModule.getCurrentModule(), 
					Translatrix.getTranslationString("formeditor.error"), 
					Translatrix.getTranslationString("formeditor.mappingError_msg"), 
					GECAMedBaseDialogImpl.OK_BUTTON_MODE);
			
			return null;
		}
		
		StringBuffer dataString = new StringBuffer();
		while (rowIterator.hasNext()) {
			rowData = rowIterator.next();
			
			Object x = rowData.get(xColumn).getValue();
			Object y = rowData.get(yColumn).getValue();
			
			if (seriesType.equals(TIME_SERIES_CHART)) {
				dataString.append(FormWidgets.formatAsInternationalDateAndTimeFormat((Date)x));
			} else {
				dataString.append(x);
			}
			
			dataString.append(";").append(y).append("\n");
		}
		
		return dataString.toString();
		/* ======================================== */
	}
	
	
	// TODO: private methods
	private void setOptionPanelComponentsVisible (boolean isStatic) {
		/* ======================================== */
		FormWidgets.setNamedComponentVisibility(xValueRefChooser, !isStatic && chartType == XY_SERIES_CHART);
		FormWidgets.setNamedComponentVisibility(yValueRefChooser, !isStatic);
		FormWidgets.setNamedComponentVisibility(sinceWhenChooser, !isStatic);
		FormWidgets.setNamedComponentVisibility(limitField, !isStatic);
		staticDataButton.setVisible(isStatic);
		/* ======================================== */
	}
	
	private void enabledOptionPanelComponents (boolean enable) {
		/* ======================================== */
		removeSeriesButton.setEnabled(enable);
		seriesNameField.setEnabled(enable);
		xValueRefChooser.setEnabled(enable);
		yValueRefChooser.setEnabled(enable);
		sinceWhenChooser.getCalendarButton().setEnabled(enable);
		clearDateButton.setEnabled(enable);
		limitField.setEnabled(enable);
		staticDataButton.setEnabled(enable);
		isStaticBox.setEnabled(enable);
		seriesPaintChooser.setEnabled(enable);
		showShapeBox.setEnabled(enable);
		/* ======================================== */
	}
	
	
	private void selectionChanged () {
		/* ======================================== */
		DataSeriesInfo info = (DataSeriesInfo) seriesChooser.getSelectedItem();
		
		enabledOptionPanelComponents(info != null);

		
		if (info != null) {
			/* ---------------------------------------- */
			seriesNameField.setText(info.name);
			xValueRefChooser.setSelectedItem(info.xReference);
			yValueRefChooser.setSelectedItem(info.yReference);
			sinceWhenChooser.setCalendar(info.sinceWhen);
			limitField.setValue(info.limit);
			isStaticBox.setSelected(info.isStatic);
			seriesPaintChooser.setColor(info.paint);
			showShapeBox.setSelected(info.showShape);
			
			setOptionPanelComponentsVisible(info.isStatic);
			/* ---------------------------------------- */
		} else {
			/* ---------------------------------------- */
			seriesNameField.setText("");
			xValueRefChooser.setSelectedItem(null); //Index(-1);
			yValueRefChooser.setSelectedItem(null); //Index(-1);
			clearDateButton.doClick();
			limitField.setText("");
			isStaticBox.setSelected(false);
			showShapeBox.setSelected(false);
			
			setOptionPanelComponentsVisible(true);
			/* ---------------------------------------- */
		}
		/* ======================================== */
	}
	
	
	/**
	 * The reload listener's method contentReloaded() is called when ever the content
	 * of an Component is reloaded and therefore the content has changed.
	 * 
	 * @param listener 
	 * the listener to be added
	 */
	public void addReloadListener (ReloadListener listener) {
		this.reloadListeners.add(listener);
	}
	
	public void removeReloadListener (ReloadListener listener) {
		this.reloadListeners.remove(listener);
	}
	
	public ReloadListener[] getReloadListeners () {
		return this.reloadListeners.toArray(new ReloadListener[0]);
	}
	
	private void fireContentReloaded () {
		final EventObject e = new EventObject(this);
		
		for (final ReloadListener l : this.reloadListeners) {
			new Thread(new Runnable() {
				public void run() {
					l.contentReloaded(e);
				}
			}).run();
		}
	}
	
	
	/**
	 * @author ferring
	 * 
	 * Contains the information for a data series. Instances of this type
	 * are stored in a list in the parent class.
	 */
	public class DataSeriesInfo {
		/* ======================================== */
		private String name 			= "";
		private String xReference 		= null;
		private String yReference		= null;
		private Boolean showShape 	 	= true;
		private Color paint 			= Color.RED;
		private String staticData;	 // = null;
		private Boolean isStatic 		= false;
		
		// TimeSeries options
		private Calendar sinceWhen;	 // = null;
		private Integer limit 			= 0;
		
		public DataSeriesInfo () {}
		
//		public DataSeriesInfo (String name, String xValue, String yValue, Color paint, 
//				String staticData, boolean isStatic, Calendar sinceWhenDate, int limit) {
//			/* ======================================== */
//			this.names = name;
//			this.xReference = xValue;
//			this.yReferences = yValue;
//			this.paints = paint;
//			this.staticData = staticData;
//			this.isStatic = isStatic;
//			this.sinceWhen = sinceWhenDate;
//			this.limit = limit;
//			/* ======================================== */
//		}
		
		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getxReference() {
			return xReference;
		}

		public void setxReference(String xReference) {
			this.xReference = xReference;
		}

		public String getyReference() {
			return yReference;
		}

		public void setyReference(String yReference) {
			this.yReference = yReference;
		}

		public Boolean getShowShape() {
			return showShape;
		}

		public void setShowShape(Boolean showShape) {
			this.showShape = showShape;
		}

		public Color getPaint() {
			return paint;
		}

		public void setPaint(Color paint) {
			this.paint = paint;
		}

		public String getStaticData() {
			return staticData;
		}

		public void setStaticData(String staticData) {
			this.staticData = staticData;
		}

		public Boolean getIsStatic() {
			return isStatic;
		}

		public void setIsStatic(Boolean isStatic) {
			this.isStatic = isStatic;
		}

		public Calendar getSinceWhen() {
			return sinceWhen;
		}

		public void setSinceWhen(Calendar sinceWhen) {
			this.sinceWhen = sinceWhen;
		}

		public Integer getLimit() {
			return limit;
		}

		public void setLimit(Integer limit) {
			this.limit = limit;
		}

		@Override
		public String toString () {
			return this.name;
		}
		/* ======================================== */
	}
	

	@Override
	public Boolean isComponentTypeStoredInDBByDefault () {
		return null;
	}
}