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

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.TreeMap;
import java.util.Vector;

import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
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.component.EditableChart;
import lu.tudor.santec.gecamed.formeditor.gui.exception.DuplicateEntryException;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;

public class DatabaseReferences {
	
	private DateFormat timeFormatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
	private DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT);

	private FormTemplate template;
	
	public DatabaseReferences (FormTemplate template) {
		/* ======================================== */
		this.template = template;
		/* ======================================== */
	}
	
	public void setTemplate (FormTemplate template) {
		/* ======================================== */
		this.template = template;
		/* ======================================== */
	}
	
	public FormTemplate getTemplate () {
		/* ======================================== */
		return template;
		/* ======================================== */
	}
	
	public Vector<Vector<String>> getTableValues (Calendar sinceWhen, Integer xLatestForms,
			Vector<ComboBoxElement<String>> references) {
		/* ======================================== */
		FormManager manager = (FormManager) ManagerFactory.getRemote(FormManagerImpl.class);
		
		Vector<Vector<String>> data = new Vector<Vector<String>>();
		
		Patient patient = GECAMedModule.getCurrentPatient();
		
		if (patient == null || patient.getId() == null 
				|| template == null || template.getId() == null) {
			/* ---------------------------------------- */
			return data;
			/* ---------------------------------------- */
		}
		
		LinkedList<Integer> creationDateIndexes = new LinkedList<Integer>();
		Vector<String> dbRefs = new Vector<String>();
//		String[] dbRefs = new String[references.size()];
		for (int index = 0; index < references.size(); index++) {
			/* ---------------------------------------- */
//			System.out.println(references.get(index));
			ComboBoxElement<String> elem = references.elementAt(index);
			if (elem.getTranslation().equals(EditableChart.CREATION_DATE_REF)) {
				creationDateIndexes.add(index);
//				references.removeElementAt(index);
			} else {
//				dbRefs.addElement(elem.toString());
				dbRefs.add(elem.toString());
			}
			/* ---------------------------------------- */
		}
		
		Vector<Object[]> values = manager.getValuesOfComponent(sinceWhen, xLatestForms, 
				template.getId(), patient.getId(), dbRefs.toArray(new String[dbRefs.size()]));
		
//		System.out.println("DatabaseReferences.getTableValues() - VALUES:"); 
//		for (Object[] objects : values) {
//			for (Object o : objects) {
//				System.out.print(o + "\t | ");
//			}
//			System.out.println();
//		} 
//		System.out.println("/* ======================================== */\n");
		
		for (Object[] objects : values) {
			/* ---------------------------------------- */
			Vector<Object> row = new Vector<Object>(Arrays.asList(objects));
			
			/* remove the date at the first position and 
			 * add it to to the supposed positions
			 */
			Date creationDate = (Date)row.remove(0);
			for (Integer index : creationDateIndexes) {
				row.add(index, timeFormatter.format(creationDate));
			}
			
			
			Vector<String> rowStrings = new Vector<String>();
			for (Object o : row) {
//				System.out.println(o);
				
				if (o == null) {
					rowStrings.add("#NULL");
				} else if (o instanceof Date) {
					rowStrings.add(dateFormatter.format((Date)o));
				} else if (o instanceof String) {
					String rowContent = String.valueOf(o);
					
					// try if it's a date
					Date date = FormWidgets.parseInternationalDateAndTimeFormat(rowContent);
					if (date != null) {
						rowContent = timeFormatter.format(date);
					} else {
						date = FormWidgets.parseInternationalDateFormat(rowContent);
						
						if (date != null) {
							rowContent = dateFormatter.format(date);
						}
					} // else: just write the string as it is
					
					rowStrings.add(rowContent);
				}
			}
			
			data.add(rowStrings);
			/* ---------------------------------------- */
		}

//		System.out.println("DatabaseReferences.getTableValues() - DATA:"); 
//		for (Vector<Object> objects : data) {
//			for (Object o : objects) {
//				System.out.print(o + "\t | ");
//			}
//			System.out.println();
//		} 
//		System.out.println("/* ======================================== */\n");
		
		return data;
		/* ======================================== */
	}
	
	public TreeMap<Object, Number> getChartValues (Calendar sinceWhen, Integer xLatestForms, 
				String xValueRef, String yValueRef) throws DuplicateEntryException {
		/* ======================================== */
		FormManager manager = (FormManager) ManagerFactory.getRemote(FormManagerImpl.class);
		TreeMap<Object, Number> map = new TreeMap<Object, Number>();
		
		Patient patient = GECAMedModule.getCurrentPatient();
		
		if (patient == null || patient.getId() == null 
				|| template == null || template.getId() == null) {
			/* ---------------------------------------- */
			return map;
			/* ---------------------------------------- */
		}
		
		Vector<Object[]> values;
		if (xValueRef.equals(EditableChart.CREATION_DATE_REF)) {
			/* ---------------------------------------- */
			// DATE-NUMBER-PAIRS
			values = manager.getValuesOfComponent(sinceWhen, xLatestForms,	template.getId(), patient.getId(),
					yValueRef);
			
			// fill the map with the date-number pairs
			for (Object[] v : values) {
				/* ---------------------------------------- */
				if (v[1] == null) {
					continue;
				}
				
				Double value = toDouble(v[1]);
				if (value == null) {
					continue;
				} else {
					map.put(v[0], value);
				}
//				try {
//					if (v[1] instanceof Date) {
//						map.put(v[0], ((Date)v[1]).getTime());
//					} else if (v[1] instanceof String) {
//						String sValue = (String)v[1];
//						if (sValue.startsWith(EditableDateChooser.DATE_PREFIX)) {
//							sValue = sValue.replace(EditableDateChooser.DATE_PREFIX, "");
//							Date date = new Date(Long.parseLong(sValue));
//							map.put(v[0], date.getTime());
//						} else {
//							Double value = Double.parseDouble((String)v[1]);
//							map.put(v[0], value);
//						}
//					}
//				} catch (NumberFormatException nfe) {
//					/* the value couldn't be converted into a String 
//					 * -> leave it and go on with the next value
//					 */
//					continue;
//				}
				/* ---------------------------------------- */
			}
			/* ---------------------------------------- */
		} else {
			/* ---------------------------------------- */
			// NUMBER-NUMBER-PAIRS
			values = manager.getValuesOfComponent(sinceWhen, xLatestForms,	template.getId(), patient.getId(),
					xValueRef, yValueRef);
			LinkedList<Number> duplicateKeys = new LinkedList<Number>();
			// fill the map with the number-number pairs 
			if (values != null) {
				for (Object[] v : values) {
					/* ---------------------------------------- */
					if (v[1] == null
							|| v[2] == null) {
						continue;
					}
					
					Double x = toDouble(v[1]);
					if (x == null) {
						continue;
					}
					
					Double y = toDouble(v[2]);
					if (y == null) {
						continue;
					}
					
					if (map.put(x, y) != null) {
						// if the return value is not null, there is a duplicate entry for an x-value
						duplicateKeys.add(x);
					}
					/* ---------------------------------------- */
				}
			}
			
			if (!duplicateKeys.isEmpty()) {
				/* ---------------------------------------- */
				String message = "";
				for (Number number : duplicateKeys) {
					message += number + ", ";
				}
				message = message.substring(0, message.length()-3);
				
				// the values of map can be get from the exception
				throw new DuplicateEntryException(message, map);
				/* ---------------------------------------- */
			}
			/* ---------------------------------------- */
		}
		
		return map;
		/* ======================================== */
	}
	
	/**
	 * Calls a database query to return all values of the components specified in the parameters.
	 * Every value of the component will be in a list as an FormDatabaseValue where you can access
	 * to <code>creationDate</code> (as Date),  <code>formId</code> (as Integer), 
	 * <code>key</code> (as String) and <code>value</code> (as String).
	 * 
	 * @param componentNames
	 * @return a list of FormDatabaseValues 
	 */
	public ArrayList<FormDatabaseValue> getValues (String ... componentNames) {
		/* ======================================== */
		FormManager manager = (FormManager)ManagerFactory.getRemote(FormManagerImpl.class);
		Vector<Object[]> values = manager.getValuesOfComponent(null, null, this.template.getId(), 
				GECAMedModule.getCurrentPatient().getId(), componentNames);
		
		ArrayList<FormDatabaseValue> list = new ArrayList<FormDatabaseValue>();
		FormDatabaseValue value;
		for (Object[] array : values) {
			value = new FormDatabaseValue();
			value.creationDate = (Date)array[0];
			value.formId = (Integer)array[1];
			value.key = (String)array[2];
			value.value = (String)array[3];
			
			list.add(value);
		}
		
		return list;
		/* ======================================== */
	}
	
	
	private static Double toDouble (Object o) {
		/* ======================================== */
		if (o instanceof Number) {
			return Double.parseDouble(o.toString());
		}
		if (o instanceof Date) {
			return new Double(((Date) o).getTime());
		} else if (o instanceof String) {
			String s = ((String)o).replaceAll(",", ".");
			
			// try to parse directly into double
			Object d = FormWidgets.parseDouble(s);
			if (d != null) {
				return (Double)d;
			}
			
			// try if it's a formated date string
			d = FormWidgets.parseInternationalDateAndTimeFormat(s);
			if (d == null) {
				// try another date format
				d = FormWidgets.parseInternationalDateFormat(s);
			}
			if (d != null) {
				return new Double(((Date)d).getTime());
			}
		}
		
		return null;
		/* ======================================== */
	}
}
