package org.bench4q.agent.utils.types;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.bench4q.agent.utils.ParameterConstant;
import org.bench4q.agent.utils.ParameterParser;
import org.bench4q.agent.utils.Type;
import org.bench4q.share.exception.Bench4QRunTimeException;

public class Table extends Type {
	public static class Row {
		private List<String> cells = new ArrayList<String>();
		private List<String> columnLabels = new ArrayList<String>();

		public List<String> getCells() {
			return cells;
		}

		public List<String> getColumnLabels() {
			return this.columnLabels;
		}

		public Row(List<String> cells, List<String> columnLabels) {
			if (cells.size() != columnLabels.size()) {
				throw new RuntimeException(
						"cell size in a row does not equals to column size");
			}
			this.cells = cells;
			this.columnLabels = columnLabels;
		}

		public Row(Map<String, String> keyValues) {
			for (Entry<String, String> entry : keyValues.entrySet()) {
				this.columnLabels.add(entry.getKey());
				this.cells.add(entry.getValue());
			}
		}

		public int cellCount() {
			return this.getCells().size();
		}

		public String getCell(int index) {
			if (index >= cellCount()) {
				return "";
			}
			return this.getCells().get(index);
		}

		public String getCell(String label) {
			for (int i = 0; i < this.columnLabels.size(); i++) {
				if (this.columnLabels.get(i).equalsIgnoreCase(label)) {
					return this.cells.get(i);
				}
			}
			throw new Bench4QRunTimeException("Row doesn't has label : "
					+ label);
		}

		@Override
		public String toString() {
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < getCells().size(); i++) {
				stringBuilder.append(this.columnLabels.get(i));
				stringBuilder.append("=");
				stringBuilder.append(this.getCells().get(i));
				stringBuilder.append(COLUMN_SEPARATOR);
			}
			stringBuilder.append(ROW_SEPARATOR_TAIL);
			return stringBuilder.toString();
		}
	}

	public static final String ROW_SEPARATOR = "|;";
	public static final String COLUMN_SEPARATOR = "|";
	public static final String ROW_SEPARATOR_TAIL = ";";

	public static final String ESCAPE_ROW_SEPARATOR = ParameterConstant.escape
			+ ROW_SEPARATOR;
	public static final String ESCAPE_COLUMN_SEPARATOR = ParameterConstant.escape
			+ COLUMN_SEPARATOR;
	private List<List<String>> realContent;
	private List<String> columnLables;
	private List<Row> rows;

	public List<List<String>> getRealContent() {
		return realContent;
	}

	private void setRealContent(List<List<String>> realContent) {
		this.realContent = realContent;
	}

	private List<String> getColumnLables() {
		return columnLables;
	}

	private void setColumnLables(List<String> columnLables) {
		this.columnLables = columnLables;
	}

	public List<Row> getRows() {
		return rows;
	}

	private void setRows(List<Row> rows) {
		this.rows = rows;
	}

	private Table() {
		this.setRows(new LinkedList<Table.Row>());
		this.setColumnLables(new LinkedList<String>());
	}

	public Row getRow(int index) {
		return this.getRows().get(index);
	}

	public static Table buildTable(String value,
			List<String> columnLablesInSequence) {
		Table result = new Table();
		result.setColumnLables(columnLablesInSequence);
		List<List<String>> realContent = buildTableContent(value);
		result.setRealContent(realContent);
		for (List<String> tableRow : realContent) {
			result.getRows().add(new Row(tableRow, columnLablesInSequence));
		}
		return result;
	}

	public static Table buildWith(Map<String, String> keyValues,
			List<String> columnLabels) {
		if (columnLabels.size() != 2) {
			throw new Bench4QRunTimeException("column label size is not two!");
		}
		StringBuilder builder = new StringBuilder();
		for (Entry<String, String> entry : keyValues.entrySet()) {
			builder.append(columnLabels.get(0) + "=");
			builder.append(entry.getKey());
			builder.append("|" + columnLabels.get(1) + "=");
			builder.append(entry.getValue());
			builder.append("|;");
		}
		return Table.buildTable(builder.toString(), columnLabels);
	}

	/**
	 * This method analyzes the value to be set in the table, and remove all
	 * escape characters
	 * 
	 * @param value
	 *            The value containing the serialization of all values of the
	 *            table
	 * @return The vector containing all the entries, each entries is in a
	 *         vector, which contains the string values
	 */
	private static List<List<String>> buildTableContent(String value) {
		List<String> tempEntries = ParameterParser.getRealTokens(
				Table.ESCAPE_ROW_SEPARATOR, value, false);
		List<List<String>> allValues = new ArrayList<List<String>>();
		for (String tempEntry : tempEntries) {
			if (tempEntry.trim().isEmpty()) {
				continue;
			}
			List<String> values = ParameterParser.getRealTokens(
					Table.ESCAPE_COLUMN_SEPARATOR, tempEntry, true);
			arrangeTableWithRowSeparatorTail(values);
			allValues.add(values);
		}
		// now analyze all values, and get the '=' separator
		return extractValueFromWellFormedTable(allValues);
	}

	private static List<List<String>> extractValueFromWellFormedTable(
			List<List<String>> allValues) {
		List<List<String>> result = new LinkedList<List<String>>();
		for (int i = 0; i < allValues.size(); i++) {
			List<String> resultValues = new ArrayList<String>();
			List<String> tempValues = allValues.get(i);
			for (String v : tempValues) {
				// List<String> temp = ParameterParser
				// .getRealTokens("=", v, false);
				int index = v.indexOf('=');
				if (index < 0) {
					continue;
				}
				resultValues.add(v.substring(index + 1));
			}
			result.add(resultValues);
		}
		return result;
	}

	/**
	 * This method check if the table_separator after escape is split by the
	 * table_entry_separator
	 * 
	 * @param values
	 */
	private static void arrangeTableWithRowSeparatorTail(List<String> values) {
		for (int i = 0; i < values.size(); i++) {
			String currentToken = values.get(i);
			if (currentToken.equals(Table.ROW_SEPARATOR_TAIL) && i > 0) {
				values.set(i - 1, values.get(i - 1) + Table.ROW_SEPARATOR_TAIL);
			}
		}

	}

	public int getColumnCount() {
		return this.getColumnLables().size();
	}

	@Override
	public String toString() {
		StringBuilder stringBuilder = new StringBuilder();
		for (Row row : this.getRows()) {
			stringBuilder.append(row.toString());
		}
		return stringBuilder.toString();
	}

}
