package com.baselet.element.elementnew.plot.parser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.baselet.control.Matrix;

public class DataSet {
	private String id;
	private Integer nr;
	private int lineNr;
	private boolean isInverted;

	private List<String> titleRow = null;
	private List<String> titleCol = null;

	private Matrix<Double> valueMatrix;
	private Matrix<String> analyseMatrix;

	public static final Double VALUE_DEFAULT = 0.0; // used for invalid value fields or missing fields

	protected DataSet(String id, int nr, int lineNr) {
		this.id = id;
		this.nr = nr;
		this.lineNr = lineNr;
		analyseMatrix = new Matrix<String>();
	}

	public String getId() {
		return id;
	}

	public Integer getNr() {
		return nr;
	}

	public int getLineNr() {
		return lineNr;
	}

	/**
	 * @return the row size
	 */
	public int rows() {
		return valueMatrix.rows();
	}

	/**
	 * @return the column size
	 */
	public int cols() {
		return valueMatrix.cols();
	}

	public boolean isEmpty() {
		return valueMatrix.isEmpty();
	}

	/**
	 * @param index
	 *            the index of the row
	 * @return a Double[] containing the cells of the row
	 */
	public Double[] row(int index) {
		List<Double> list = valueMatrix.row(index);
		return list.toArray(new Double[list.size()]);
	}

	public Double[][] data() {
		if (valueMatrix.isEmpty()) {
			throw new ParserException("The dataset (line: " + getLineNr() + ") has no values");
		}
		Double[][] returnArray = new Double[rows()][];
		for (int i = 0; i < rows(); i++) {
			returnArray[i] = row(i);
		}
		return returnArray;
	}

	/**
	 * Changed the manual inversion of the dataset. The dataset must only be inverted if the value has changed because
	 * it is only referenced from plots, therefore the last inversion will be dragged to further plot-calls without a problem
	 */
	public void setInvert(boolean shouldBeInverted) {
		if (isInverted == !shouldBeInverted) {
			analyseMatrix.invert();
			separateTitleRowColFromContent();
			isInverted = shouldBeInverted;
		}
	}

	public String[] titleRow() {
		return titleRow.toArray(new String[titleRow.size()]);
	}

	public String[] titleCol() {
		return titleCol.toArray(new String[titleCol.size()]);
	}

	@Override
	public String toString() {
		return "Dataset (" + id + ")\n" + analyseMatrix;
	}

	protected void addLine(String[] line) {
		analyseMatrix.addLine(new ArrayList<String>(Arrays.asList(line)));
	}

	protected void analyseMatrix() {
		separateTitleRowColFromContent();
		// If the valuematrix has more rows than cols the analyseMatrix must be inverted and analysed again
		// if (!valueMatrix.isEmpty() && valueMatrix.hasMoreRowsThanCols()) analyseMatrix.invert();
		// separateTitleRowColFromContent();
	}

	private void separateTitleRowColFromContent() {
		if (analyseMatrix.isEmpty()) {
			throw new ParserException("The dataset (line: " + getLineNr() + ") has no content");
		}
		List<String> firstRow = analyseMatrix.row(0);
		List<String> firstCol = analyseMatrix.col(0);
		boolean hasTitleRow = isTitleLine(firstRow);
		boolean hasTitleCol = isTitleLine(firstCol);

		if (hasTitleRow && hasTitleCol) {
			if (!firstRow.get(0).isEmpty() || !firstCol.get(0).isEmpty()) {
				throw new ParserException("If a dataset has a title row and column, the upper left space must be empty");
			}
			titleRow = firstRow.subList(1, firstRow.size()); // ignore first cell
			titleCol = firstCol.subList(1, firstCol.size()); // ignore first cell
		}
		else if (hasTitleRow && !hasTitleCol) {
			titleRow = firstRow;
			titleCol = createEmptyList(firstCol.size() - 1);

		}
		else if (!hasTitleRow && hasTitleCol) {
			titleRow = createEmptyList(firstRow.size() - 1);
			titleCol = firstCol;
		}
		else /* if (!hasTitleRow && !hasTitleCol) */ {
			titleRow = createEmptyList(firstRow.size());
			titleCol = createEmptyList(firstCol.size());
		}

		valueMatrix = new Matrix<Double>();
		for (int r = hasTitleRow ? 1 : 0; r < analyseMatrix.rows(); r++) {
			List<String> row = analyseMatrix.row(r);
			List<Double> rowDouble = new ArrayList<Double>();
			for (int c = hasTitleCol ? 1 : 0; c < row.size(); c++) {
				String val = row.get(c);
				try {
					if (val == null) {
						throw new NumberFormatException();
					}
					else {
						rowDouble.add(Double.parseDouble(val));
					}
				} catch (NumberFormatException ex) {
					throw new ParserException("The Dataset (line: " + getLineNr() + ") contains invalid values");
				}
			}
			valueMatrix.addLine(rowDouble);
		}
	}

	private boolean isTitleLine(List<String> row) {
		int numbersInRow = 0;
		for (String cell : row) {
			if (cell == null) {
				continue;
			}
			try {
				Double.parseDouble(cell);
				numbersInRow++;
			} catch (NumberFormatException ex) {/* do nothing */}
		}

		return row.size() - numbersInRow > numbersInRow;
	}

	private List<String> createEmptyList(int size) {
		List<String> returnList = new ArrayList<String>();
		for (int i = 0; i < size; i++) {
			returnList.add("");
		}
		return returnList;
	}
}
