import {
	TableType,
} from "qrc:/js/lib/generated/enum";
import {
	getTable,
} from "qrc:/js/lib/table_utils";
import {
	visitMaximalPaths,
} from "qrc:/js/lib/graph_utils";
import {
	getSettingOrDefault,
} from "qrc:/js/lib/settings_table";
import {
	computeNodeTimes,
	getFixedMultiplicity,
} from "./export_calc_times";
import {
	CalcCache,
} from "./export_calc_cache";

/**
 * The resulting time is defined by the most time-consuming maximal path through the graph.
 */
function computeMaximalPathCompletionTime(calcCache?: CalcCache): number {
	const table = getTable(TableType.processIdlePeriod);
	let maxTimeInS = 0;
	visitMaximalPaths((path: Vertex[]) => {
		const idleTimeInS = path.reduce((acc, vertex) => {
			const processId = wsi4.node.processId(vertex);
			const entry = table.find(row => row.processId === processId);
			// [h] -> [s]
			return entry === undefined ? acc : acc + 3600 * entry.time;
		}, 0);
		const manufacturingTimeInS = path.reduce((acc, vertex) => {
			const times = computeNodeTimes(vertex, getFixedMultiplicity(vertex), calcCache);
			if (times === undefined) {
				return acc;
			}
			return acc + times.setup + times.unit;
		}, 0);
		maxTimeInS = Math.max(maxTimeInS, idleTimeInS + manufacturingTimeInS);
	});
	return maxTimeInS;
}

/**
 * The resulting time is defined by the most time-consuming node in the graph.
 */
function computeMaximalNodeCompletionTime(inclManufacturingTimes: boolean, calcCache?: CalcCache): number {
	const table = getTable(TableType.processIdlePeriod);
	return wsi4.graph.vertices().reduce((acc, vertex) => {
		const processId = wsi4.node.processId(vertex);
		const entry = table.find(row => row.processId === processId);
		// [h] -> [s]
		const idleTime = entry === undefined ? 0. : 3600 * entry.time;
		const mfgTimes = inclManufacturingTimes ? computeNodeTimes(vertex, getFixedMultiplicity(vertex), calcCache) : undefined;
		return Math.max(acc, idleTime + (mfgTimes?.setup ?? 0) + (mfgTimes?.unit ?? 0));
	}, 0);
}

/**
 * Best case time to manufacture all components of the graph.
 *
 * The time consists of both manufacturing times and the idle periods.
 *
 * "Best case": This time can be reached if all possible processes are handled in parallel.
 */
export function computeMinCompletionTime(calcCache?: CalcCache): number {
	const mode = getSettingOrDefault("completionTimeMode");
	switch (mode) {
		case "maximalPath": return computeMaximalPathCompletionTime(calcCache);
		case "maximalNodeInclManufacturingTime": return computeMaximalNodeCompletionTime(true, calcCache);
		case "maximalNodeExclManufacturingTime": return computeMaximalNodeCompletionTime(false, calcCache);
	}
}
