package de.lcag.common;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Table holds information for actions to take when integrating changes done in
 * Client Table into a Master Table
 */
public class TableComparisonResult {
	private Table masterTable;
	private Table clientTable;

	private Collection<String> commonColumns;
	private Collection<String> columnsToAddToMaster;
	private Collection<String> columnsToDeleteFromMaster;
	private Collection<Integer> rowsToAddFromClient = new ArrayList<>();
	private Collection<Integer> rowsToDeleteFromMaster = new ArrayList<>();

	public int getTotalNumberOfComparedCells() {
		return totalNumberOfComparedCells;
	}

	public void setTotalNumberOfComparedCells(int totalNumberOfComparedCells) {
		this.totalNumberOfComparedCells = totalNumberOfComparedCells;
	}

	public int getTotalNumberOfDifferentCells() {
		return totalNumberOfDifferentCells;
	}

	public double getPercentEqual() {
		double result = 0;

		if (totalNumberOfComparedCells > 0) {
			result = 1 - totalNumberOfDifferentCells * 1.0 / totalNumberOfComparedCells;
		}

		return result;
	}

	public void setTotalNumberOfDifferentCells(int totalNumberOfDifferentCells) {
		this.totalNumberOfDifferentCells = totalNumberOfDifferentCells;
	}

	private int totalNumberOfComparedCells = 0;
	private int totalNumberOfDifferentCells = 0;

	public class CellPair {
		public String key;
		public int clientRow;
		public String masterColName, clientColName;

		public CellPair(String pKey, String pMasterColName, int pClientRowNo, String pClientColName) {
			this.key = pKey;
			this.clientRow = pClientRowNo;
			this.masterColName = pMasterColName;
			this.clientColName = pClientColName;
		}
	}

	private LinkedHashMap<String, List<CellPair>> cellsToChangeInMasterPerRow = new LinkedHashMap<>();

	public TableComparisonResult(Table pMasterTable, Table pClientTable) {
		this.masterTable = pMasterTable;
		this.clientTable = pClientTable;
	}

	public Table getMasterTable() {
		return masterTable;
	}

	public Table getClientTable() {
		return clientTable;
	}

	public Collection<String> getColumnsToAddToMaster() {
		return columnsToAddToMaster;
	}

	public Collection<String> getColumnsToDeleteFromMaster() {
		return columnsToDeleteFromMaster;
	}

	public Collection<Integer> getRowsToAddFromClient() {
		return rowsToAddFromClient;
	}

	public Collection<Integer> getRowsToDeleteFromMaster() {
		return rowsToDeleteFromMaster;
	}

	public Map<String, List<CellPair>> getCellsToChangeInMaster() {
		return cellsToChangeInMasterPerRow;
	}

	public void addCellDifference(String pKey, int pMasterRowNo, String pMasterColName, int pCmpRowNo,
			String pClientColName) {
		CellPair cellPair = new CellPair(pKey, pMasterColName, pCmpRowNo, pClientColName);
		List<CellPair> changesInRow = this.cellsToChangeInMasterPerRow.getOrDefault(pKey, new ArrayList<>());

		changesInRow.add(cellPair);

		if (changesInRow.size() == 1)
			this.cellsToChangeInMasterPerRow.put(pKey, changesInRow);
	}

	public Collection<String> getCommonColumns() {
		return this.commonColumns;
	}

	public void setCommonColumns(Collection<String> pCommonColNames) {
		this.commonColumns = pCommonColNames;
	}

	public void setColumnsToAdd(Collection<String> pClientsOnlyColNames) {
		this.columnsToAddToMaster = pClientsOnlyColNames;
	}

	public void setColumnsToDelete(Collection<String> pMastersOnlyColNames) {
		this.columnsToDeleteFromMaster = pMastersOnlyColNames;
	}

	public void addRowToAdd(int pFromClientRowNo) {
		this.rowsToAddFromClient.add(pFromClientRowNo);
	}

	public void addRowToDelete(int pFromMasterRowNo) {
		this.rowsToDeleteFromMaster.add(pFromMasterRowNo);
	}

	public void generateMergedExcelFile(String pFilePath) throws IOException {
		ExcelWorkbook resultTable = new ExcelWorkbook(pFilePath);

		resultTable.saveAs(pFilePath);
	}

	boolean tablesAreInconsistent() {
		boolean result = !cellsToChangeInMasterPerRow.isEmpty();

		return result;
	}

	boolean masterIncludesClientTable() {
		boolean result = columnsToAddToMaster.isEmpty() && rowsToAddFromClient.isEmpty()
				&& cellsToChangeInMasterPerRow.isEmpty();

		return result;
	}

	boolean tablesAreEqual() {
		boolean result = masterIncludesClientTable() && columnsToDeleteFromMaster.isEmpty()
				&& rowsToDeleteFromMaster.isEmpty();

		return result;
	}

	public Table integrateDifferencesIntoMaster() throws IOException {
		masterTable.integrateDiff(this);

		return masterTable;
	}

	public HashTable getSummary() {
		HashTable result = new HashTable("Compare Statistic Subject", "Count");

		result.addRow("Rows only in master table", this.rowsToDeleteFromMaster.size());
		result.addRow("Rows only in client table", this.rowsToAddFromClient.size());

		List<Integer> numberOfRowsWithNumberOfChanges = new ArrayList<>();
		for (int i = 1; i < masterTable.getNoColumns(); i++) {
			numberOfRowsWithNumberOfChanges.add(0);
		}

		for (List<CellPair> changeList : this.cellsToChangeInMasterPerRow.values()) {
			int n = changeList.size();
			int currentCount = numberOfRowsWithNumberOfChanges.get(n) + 1;

			numberOfRowsWithNumberOfChanges.set(n, currentCount);
		}

		int noChanges = 0;
		for (int n = 0; n < numberOfRowsWithNumberOfChanges.size(); n++) {
			Integer noChangesInRow = numberOfRowsWithNumberOfChanges.get(n);

			noChanges += noChangesInRow;
			if (noChangesInRow > 0)
				result.addRow(String.format("Rows with %d differences", n), noChangesInRow);
		}

		result.addRow("Total number of compared cells", this.totalNumberOfComparedCells);
		result.addRow("Total number of different cells", this.totalNumberOfDifferentCells);

		double percentEqual = this.getPercentEqual();
		result.addRow("Percent equal", String.format("%.1f%%", percentEqual * 100.0));

		return result;
	}

}
