import {
	ProcessType,
} from "qrc:/js/lib/generated/enum";
import {
	isStringIndexedInterface,
} from "qrc:/js/lib/generated/typeguard";
import {
	appendNodeWithProcessId,
} from "./graph_manipulator";
import {
	findActiveProcess,
} from "./process";
import {
	getSharedDataEntry,
} from "./shared_data";
import {
	assert,
	getKeysOfObject,
	isBoolean,
} from "./utils";

export interface AutomaticProcessConfig {
	automaticDeburringEnabled: boolean;
	manualDeburringEnabled: boolean;
}

export function isAutomaticProcessConfig(arg: unknown): arg is AutomaticProcessConfig {
	const map : {[index in keyof AutomaticProcessConfig]: (arg: unknown) => arg is AutomaticProcessConfig[index]} = {
		automaticDeburringEnabled: isBoolean,
		manualDeburringEnabled: isBoolean,
	};
	return isStringIndexedInterface(arg) && getKeysOfObject(map)
		.every(key => map[key](arg[key]));
}

function articleHasDeburringNode(article: readonly Vertex[]): boolean {
	return article.some(vertex => {
		const processType = wsi4.node.processType(vertex);
		return processType === ProcessType.automaticMechanicalDeburring
			|| processType === ProcessType.manualMechanicalDeburring;
	});
}

function insertDeburringNodes(processType: ProcessType, terminatingRootIds: readonly GraphNodeRootId[]) {
	const process = findActiveProcess(processType);
	if (process === undefined) {
		wsi4.util.warn("Cannot insert deburring node(s).  No active process found for process type " + processType);
	} else {
		terminatingRootIds.map(rootId => {
			const vertex = wsi4.node.vertexFromRootId(rootId);
			assert(vertex !== undefined, "Expecting valid vertex for rootId " + wsi4.util.toKey(rootId));
			return vertex;
		})
			.reduce((acc: Vertex[], vertex) => ([
				...acc,
				...wsi4.graph.reaching(vertex),
				vertex,
			]), [])
			.filter(vertex => wsi4.node.processType(vertex) === ProcessType.laserSheetCutting)
			.filter(vertex => !articleHasDeburringNode(wsi4.graph.article(vertex)))
			.map(vertex => wsi4.node.rootId(vertex))
			.forEach(rootId => {
				const sourceVertex = wsi4.node.vertexFromRootId(rootId);
				assert(sourceVertex !== undefined, "Expecting valid vertex for RootId " + wsi4.util.toKey(rootId));
				const newVertex = appendNodeWithProcessId(process.identifier, sourceVertex);
				if (!newVertex !== undefined) {
					wsi4.util.warn("Automatic node insertion failed for process type " + processType);
				}
			});
	}
}

function deburrIfRequired(terminatingRootIds: readonly GraphNodeRootId[]) {
	const automaticProcessConfig = getSharedDataEntry("automaticProcessConfig");
	if (automaticProcessConfig.automaticDeburringEnabled) {
		insertDeburringNodes(ProcessType.automaticMechanicalDeburring, terminatingRootIds);
	} else if (automaticProcessConfig.manualDeburringEnabled) {
		insertDeburringNodes(ProcessType.manualMechanicalDeburring, terminatingRootIds);
	}
}

/**
 * Post-process a sub-graph
 *
 * @param terminatingRootIds Root IDs defining the sub-graph
 */
export function postProcessSubGraph(terminatingRootIds: readonly GraphNodeRootId[]): void {
	deburrIfRequired(terminatingRootIds);
}
