package lpv;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Set;

import org.jfree.data.category.DefaultCategoryDataset;

import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;

public class ErrorStore extends Observable {
	public enum Type {
		aae, are, mae, mre, cae
	}

	private List<String> sources;
	private HashMap<String, List<ErrorMetric>> store;

	public ErrorStore() {
		this.sources = new ArrayList<String>();
		this.store = new HashMap<String, List<ErrorMetric>>();
	}

	public void registerSource(String name) {
		sources.add(name);
		store.put(name, new ArrayList<ErrorMetric>());
	}

	public void add(String name, ErrorMetric error) {
		store.get(name).add(error);
		setChanged();
		notifyObservers();
	}

	public void dump() {
		for (String source : sources) {
			StringBuilder[] sb = new StringBuilder[] {
					new StringBuilder(
							String.format("%20s = [ ", source + "AAE")),
					new StringBuilder(
							String.format("%20s = [ ", source + "ARE")),
					new StringBuilder(
							String.format("%20s = [ ", source + "MAE")),
					new StringBuilder(
							String.format("%20s = [ ", source + "MRE")),
					new StringBuilder(
							String.format("%20s = [ ", source + "CAE")) };

			Iterator<ErrorMetric> it = store.get(source).iterator();
			while (it.hasNext()) {
				ErrorMetric n = it.next();
				sb[0].append(String.format("%12.6f", n.averageAbsoluteError));
				sb[1].append(String.format("%12.6f", n.averageRelativeError));
				sb[2].append(String.format("%12.6f", n.maxAbsoluteError));
				sb[3].append(String.format("%12.6f", n.maxRelativeError));
				sb[4].append(String.format("%12.6f", n.cumulativeAbsoluteError));
				if (it.hasNext()) {
					for (int i = 0; i < sb.length; i++) {
						sb[i].append(", ");
					}
				}
			}

			for (int i = 0; i < sb.length; i++) {
				sb[i].append("];");
				System.out.println(sb[i].toString());
			}
			System.out.println();
		}

	}

	public void getData(DefaultCategoryDataset dataset, String source, Set<Type> types) {
		dataset.clear();
		int i = 1;
		Iterator<ErrorMetric> it = store.get(source).iterator();
		while (it.hasNext()) {
			ErrorMetric n = it.next();
			if (types.contains(Type.aae)) {
				dataset.addValue(n.averageAbsoluteError, "Avg. abs. error",
						(Comparable<Integer>) i);
			}
			if (types.contains(Type.are)) {
				dataset.addValue(n.averageRelativeError, "Avg. rel. error",
						(Comparable<Integer>) i);
			}
			if (types.contains(Type.mae)) {
				dataset.addValue(n.maxAbsoluteError, "Max abs. error",
						(Comparable<Integer>) i);
			}
			if (types.contains(Type.mre)) {
				dataset.addValue(n.maxRelativeError, "Max rel. error",
						(Comparable<Integer>) i);
			}
			if (types.contains(Type.cae)) {
				dataset.addValue(n.cumulativeAbsoluteError, "Cumulative error",
						(Comparable<Integer>) i);
			}
			i++;
		}
	}

	public List<String> getSources() {
		return Collections.unmodifiableList(sources);
	}
	
	public void clear() {
		for(List<ErrorMetric> list : store.values()) {
			list.clear();
		}
		setChanged();
		notifyObservers();
	}
	
	public void load(String name, InputStream in) throws IOException {
		if(!sources.contains(name)) {
			registerSource(name);
		}
		List<ErrorMetric> data = store.get(name);
		data.clear();
		
		CSVReader r = new CSVReader(new InputStreamReader(in));
		String[] line;
		while((line = r.readNext()) != null) {
			ErrorMetric em = new ErrorMetric();
			em.averageAbsoluteError = Float.parseFloat(line[0]);
			em.averageRelativeError = Float.parseFloat(line[1]);
			em.maxAbsoluteError = Float.parseFloat(line[2]);
			em.maxRelativeError = Float.parseFloat(line[3]);
			em.cumulativeAbsoluteError = Float.parseFloat(line[4]);
			data.add(em);
		}
		r.close();
		setChanged();
		notifyObservers();
	}
	
	public void save(String name, OutputStream out) throws IOException {
		List<ErrorMetric> data = store.get(name);
		
		CSVWriter w = new CSVWriter(new OutputStreamWriter(out));
		for(ErrorMetric em : data) {
			String aae = String.valueOf(em.averageAbsoluteError);
			String are = String.valueOf(em.averageRelativeError);
			String mae = String.valueOf(em.maxAbsoluteError);
			String mre = String.valueOf(em.maxRelativeError);
			String cae = String.valueOf(em.cumulativeAbsoluteError);
			w.writeNext(new String[] { aae, are, mae, mre, cae });
		}
		w.close();
	}

}
