package de.lcag.common;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;

import de.lcag.common.TableComparisonResult.CellPair;

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

	private Collection<String> commonTables;
	private Collection<String> tablesToAddToMaster;
	private Collection<String> tablesToDeleteFromMaster;
	private Map<String, TableComparisonResult> differencesPerTable = new LinkedHashMap<>();

	public WorkbookComparisonResult(ExcelWorkbook pMasterTable, ExcelWorkbook pClientTable) {
		this.masterTable = pMasterTable;
		this.clientTable = pClientTable;
	}

	public ExcelWorkbook getMasterWorkbook() {
		return masterTable;
	}

	public ExcelWorkbook getClientWorkbook() {
		return clientTable;
	}

	public Collection<String> getTablesToAddToMaster() {
		return tablesToAddToMaster;
	}

	public Collection<String> getTablesToDeleteFromMaster() {
		return tablesToDeleteFromMaster;
	}

	public Map<String, TableComparisonResult> getDifferencesPerTable() {
		return differencesPerTable;
	}

	public TableComparisonResult getFirstTableDifferences() {
		return differencesPerTable.values().iterator().next();
	}

	public void addTableDifference(String pTableName, TableComparisonResult pResult) {
		this.differencesPerTable.put(pTableName, pResult);
	}

	public Collection<String> getCommonTables() {
		return this.commonTables;
	}

	public void setCommonTables(Collection<String> pCommonTables) {
		this.commonTables = pCommonTables;
	}

	public void setTablesToAdd(Collection<String> pClientsOnlyTables) {
		this.tablesToAddToMaster = pClientsOnlyTables;
	}

	public void setTablesToDelete(Collection<String> pMastersOnlyTables) {
		this.tablesToDeleteFromMaster = pMastersOnlyTables;
	}

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

		return result;
	}

	boolean masterIncludesClientTables() {
		boolean result = tablesToAddToMaster.isEmpty() && differencesPerTable.isEmpty();

		return result;
	}

	public boolean tablesAreEquals() {
		boolean result = masterIncludesClientTables() && tablesToDeleteFromMaster.isEmpty();

		return result;
	}

	public ExcelWorkbook integrateDifferencesIntoMaster() throws IOException {
		masterTable.integrateDifferences(this);

		return masterTable;
	}

	public void save(Path pFilePath) throws IOException, InvalidFormatException {
		ExcelWorkbook wb = new ExcelWorkbook(pFilePath.toString(), "Files", "Instruction", "Object", "Name", "Key",
				"OldValue", "NewValue");

		int rowNo = wb.newRow();

		wb.newRow();

		wb.setCellValue(rowNo, "Files", masterTable.getFilePath().toString());
		wb.setCellValue(rowNo + 1, "Files", clientTable.getFilePath().toString());

		int n = rowNo;
		for (String s : tablesToAddToMaster) {
			wb.setCellValue(n, "Instruction", "add");
			wb.setCellValue(n, "Object", "Table");
			wb.setCellValue(n, "Name", s);

			n = (n < 2) ? n + 1 : wb.newRow();
		}

		for (String s : tablesToDeleteFromMaster) {
			wb.setCellValue(n, "Instruction", "delete");
			wb.setCellValue(n, "Object", "Table");
			wb.setCellValue(n, "Name", s);

			n = (n < 2) ? n + 1 : wb.newRow();
		}

		for (String tableName : differencesPerTable.keySet()) {
			TableComparisonResult tableComparisonResult = differencesPerTable.get(tableName);

			for (String s : tableComparisonResult.getColumnsToAddToMaster()) {
				wb.setCellValue(n, "Instruction", "add");
				wb.setCellValue(n, "Object", "Column");
				wb.setCellValue(n, "Name", s);

				n = (n < 2) ? n + 1 : wb.newRow();
			}

			for (String s : tableComparisonResult.getColumnsToDeleteFromMaster()) {
				wb.setCellValue(n, "Instruction", "delete");
				wb.setCellValue(n, "Object", "Column");
				wb.setCellValue(n, "Name", s);

				n = (n < 2) ? n + 1 : wb.newRow();
			}

			for (Integer s : tableComparisonResult.getRowsToAddFromClient()) {
				wb.setCellValue(n, "Instruction", "add");
				wb.setCellValue(n, "Object", "Row");
				wb.setCellValue(n, "Name", s);

				n = (n < 2) ? n + 1 : wb.newRow();
			}

			for (Integer s : tableComparisonResult.getRowsToDeleteFromMaster()) {
				wb.setCellValue(n, "Instruction", "delete");
				wb.setCellValue(n, "Object", "Row");
				wb.setCellValue(n, "Name", s);

				n = (n < 2) ? n + 1 : wb.newRow();
			}

			Map<String, List<CellPair>> cellsToChangeInMaster = tableComparisonResult.getCellsToChangeInMaster();
			Table masterTable2 = tableComparisonResult.getMasterTable();
			Table clientTable2 = tableComparisonResult.getClientTable();

			for (String s : cellsToChangeInMaster.keySet()) {
				List<CellPair> diffList = cellsToChangeInMaster.get(s);

				for (CellPair diff : diffList) {
					wb.setCellValue(n, "Instruction", "update");
					wb.setCellValue(n, "Object", "Cell");
					wb.setCellValue(n, "Name", diff.masterColName);
					wb.setCellValue(n, "Key", diff.key);

					Integer masterRowNo = masterTable2.getRowByKey(diff.key);
					Integer clientRowNo = clientTable2.getRowByKey(diff.key);

					Object oldValue = masterTable2.getCellValue(masterRowNo, diff.masterColName);
					Object newValue = clientTable2.getCellValue(clientRowNo, diff.clientColName);

					wb.setCellValue(n, "OldValue", oldValue);
					wb.setCellValue(n, "NewValue", newValue);
					n = (n < 2) ? n + 1 : wb.newRow();
				}
			}

		}

		wb.save();
	}
}
