import {
	WorkStepType,
} from "qrc:/js/lib/generated/enum";
import {
	checkConstraints,
} from "./constraints";
import {
	computeManufacturingStateImpl,
	ConstraintLevelMap,
	isExportReadyImpl,
	ManufacturingState,
	ReplyStateLevelMap,
	WstConstraintMap,
} from "./manufacturing_state";
import {
	minConstraintLevelMap,
	minReplyStateLevelMap,
} from "./min_manufacturing_state";
import {
	isEqual,
} from "./utils";

/**
 * Convenience function to compute manufacturing state for a vertex and its underlying `WorkStepType`
 */
export function computeActualManufacturingState(vertex: Readonly<Vertex>, replyStateLevelMap: ReplyStateLevelMap, constraintLevelMap: ConstraintLevelMap): ManufacturingState {
	const wst = wsi4.node.workStepType(vertex);
	const wstConstraintMap = (() => {
		const m: WstConstraintMap = {};
		m[wst] = checkConstraints(vertex);
		return m;
	})();

	return computeManufacturingStateImpl(
		wsi4.node.camReplyStateIndicators(vertex),
		wstConstraintMap,
		replyStateLevelMap,
		constraintLevelMap,
		[ wst ],
	);
}

export interface ManufacturingStateCache {
	entries: [Vertex, ManufacturingState][];
}

export function createManufacturingStateCache(): ManufacturingStateCache {
	return {
		entries: [],
	};
}

export function computeActualManufacturingStateCached(vertex: Vertex, replyStateLevelMap: ReplyStateLevelMap, constraintLevelMap: ConstraintLevelMap, cache: ManufacturingStateCache): ManufacturingState {
	const entry = cache.entries.find(entry => isEqual(entry[0], vertex));
	if (entry === undefined) {
		const result = computeActualManufacturingState(vertex, replyStateLevelMap, constraintLevelMap);
		cache.entries.push([
			vertex,
			result,
		]);
		return result;
	} else {
		return entry[1];
	}
}

/**
 * Convenience function to compute manufacturing state for all `WorkStepType`s besides `vertex`'s actual `WorkStepType`
 */
export function computeVirtualManufacturingState(vertex: Readonly<Vertex>, replyStateLevelMap: ReplyStateLevelMap, constraintLevelMap: ConstraintLevelMap): ManufacturingState {
	// Currently constraints are checked for the actual wst only.
	const wstConstraintMap: WstConstraintMap = {};

	const actualWst = wsi4.node.workStepType(vertex);
	const targetWsts = Array.from(WorkStepType)
		.filter(wst => wst !== actualWst);

	return computeManufacturingStateImpl(
		wsi4.node.camReplyStateIndicators(vertex),
		wstConstraintMap,
		replyStateLevelMap,
		constraintLevelMap,
		targetWsts,
	);
}

/**
 * @returns true if a certain minimal requirements are fulfilled.
 *
 * This function can be used e.g. in contexts where certain minimal requirements need
 * to be asserted regardless of actual context (e.g. gui vs. shop).
 */
export function isMinExportReady(vertex: Vertex): boolean {
	const manufacturingState = computeActualManufacturingState(vertex, minReplyStateLevelMap, minConstraintLevelMap);
	return isExportReadyImpl(manufacturingState);
}

export function isExportReady(vertex: Vertex, replyStateLevelMap: ReplyStateLevelMap, constraintLevelMap: ConstraintLevelMap): boolean {
	const manufacturingState = computeActualManufacturingState(vertex, replyStateLevelMap, constraintLevelMap);
	return isExportReadyImpl(manufacturingState);
}
