import {
	Color,
	FileType,
	ProcessType,
	TableType,
	TubeProfileGeometryType,
	WorkStepType,
} from "qrc:/js/lib/generated/enum";
import {
	getActiveProcessTypes,
} from "qrc:/js/lib/process_utils";
import {
	defaultSvgResolution,
} from "qrc:/js/lib/scene_utils";
import {
	getSettingOrDefault,
} from "qrc:/js/lib/settings_table";
import {
	getSheetsInStock,
	getTable,
	isTubeInStock,
	parseTubeProfileGeometry,
} from "qrc:/js/lib/table_utils";
import {
	assert,
	computeArrayIntersection,
	exhaustiveStringTuple,
} from "qrc:/js/lib/utils";
import {
	createCircTubeProfilePath,
	createRectTubeProfilePath,
} from "qrc:/js/lib/part_creation";
import {
	isAvailableProcess,
	workStepTypeMap,
} from "qrc:/js/lib/process";

import {
	AdditionalProcesses,
	AllowedAdditionalProcesses,
	ConfigFlags,
	getAdditionalProcesses,
	MinLowerDieOpeningWidth,
	ShopConfig,
	ShopConfigSettings,
	TubeProfileEntry,
} from "./cli_shop_interface";

export function getAllowedAdditionalProcesses(): AllowedAdditionalProcesses {
	const availableTypes = getActiveProcessTypes();
	const ap: AdditionalProcesses = getAdditionalProcesses();
	return exhaustiveStringTuple<keyof AdditionalProcesses>()(
		"mechanicalDeburring",
		"threading",
		"countersinking",
		"slideGrinding",
		"tapping",
	).filter(key => {
		const specificTypes = ap[key];
		return computeArrayIntersection([
			availableTypes,
			specificTypes,
		]).length > 0;
	});
}

function getMinLowerDieOpeningWidths(): Array<MinLowerDieOpeningWidth> {
	const bendDeductions = getTable(TableType.bendDeduction);
	const lowerDieGroups = getTable(TableType.lowerDieGroup);
	const thicknesses = Array.from(new Set(getSheetsInStock().map(sheet => sheet.thickness)));
	return thicknesses.reduce((res: Array<MinLowerDieOpeningWidth>, t) => {
		const deductions = bendDeductions.filter(d => d.thickness === t);
		if (deductions.length === 0) {
			return res;
		}
		const dieGroups = lowerDieGroups.filter(l => deductions.some(d => d.lowerDieGroupId === l.identifier));
		if (dieGroups.length === 0) {
			return res;
		}
		res.push({
			thickness: t,
			openingWidth: Math.min(...dieGroups.map(l => l.openingWidth)),
		});
		return res;
	}, []);
}

function settings(): ShopConfigSettings {
	return {
		fixedRotationsEnabled: getSettingOrDefault("sheetCuttingFixedRotationsEnabled"),
		testReportRequired: getSettingOrDefault("sheetTestReportEnabled"),
	};
}

function createTubeProfileSvg(geometry: TubeProfileGeometry): ArrayBuffer {
	const segments = (() => {
		switch (geometry.type) {
			case TubeProfileGeometryType.circular: {
				return createCircTubeProfilePath(geometry.content);

			}
			case TubeProfileGeometryType.rectangular: {
				return createRectTubeProfilePath(geometry.content);

			}
		}
	})();

	const iop = wsi4.geo.util.createIop(segments);
	assert(iop !== undefined, "Tube profile geometry invalid");

	const scene = (() => {
		const emptyScene = wsi4.geo.util.createScene({
			material: "",
			thickness: 0.,
			identifier: "",
			comment: "",
			globalMaterial: "",
		});

		return wsi4.geo.util.addInnerOuterPolygonsToScene(
			emptyScene,
			[ iop ],
			{
				strokeWidth: 1.,
				strokeColor: Color.black,
			},
		);
	})();

	return wsi4.geo.util.renderScene(scene, FileType.svg, {resolution: defaultSvgResolution()});
}

function computeTubeProfileEntries(): TubeProfileEntry[] {
	return getTable(TableType.tubeProfile)
		.map(row => {
			const geometry = parseTubeProfileGeometry(row.geometryJson);
			return {

				identifier: row.identifier,
				name: row.name,
				description: row.description,
				// Note:  As of now SVGs binary data is converted directly to string in the shop interface
				svg: wsi4.util.arrayBufferToString(createTubeProfileSvg(geometry)),
				geometry: geometry,
			};
		});
}

/**
 * Get the config with the values relevant for the shop
 *
 * Note: Return value must not contain any sensible data such as internal prices or calculation parameters.
 */
export function shopConfiguration(configFlags: Readonly<ConfigFlags>): ShopConfig {
	const materialTable = Array.from(getTable(TableType.sheetMaterial));
	const sheetsInStock = Array.from(getSheetsInStock());
	const screwThreads = getTable(TableType.screwThread);

	const tubeStock = getTable(TableType.tubeStock);
	const tubeProfiles = configFlags.tubesEnabled ? computeTubeProfileEntries() : [];
	const tubes = configFlags.tubesEnabled ? getTable(TableType.tube)
		.filter(tube => tubeProfiles.some(profile => profile.identifier === tube.tubeProfileId)
			&& isTubeInStock(tube.identifier, tubeStock)) : [];
	const tubeMaterials = configFlags.tubesEnabled ? getTable(TableType.tubeMaterial)
		.filter(material => tubes.some(tube => tube.tubeMaterialId === material.identifier)) : [];
	const tubeSpecifications = configFlags.tubesEnabled ? getTable(TableType.tubeSpecification)
		.filter(specification => tubes.some(tube => tube.tubeSpecificationId === specification.identifier)) : [];
	const availableProcessesTubeCutting = configFlags.tubesEnabled ? getTable(TableType.process)
		.filter((row, _, self) => row.type === ProcessType.tubeCutting && isAvailableProcess(row, self)) : [];
	const availableProcessesSheetCutting = getTable(TableType.process)
		.filter((row, _, self) => workStepTypeMap[row.type] === WorkStepType.sheetCutting && isAvailableProcess(row, self));

	return {
		allowedAdditionalProcesses: getAllowedAdditionalProcesses(),
		availableProcessesSheetCutting: availableProcessesSheetCutting,
		availableProcessesTubeCutting: availableProcessesTubeCutting,
		material: materialTable,
		minLowerDieOpeningWidths: getMinLowerDieOpeningWidths(),
		minimalScrewDiameter: screwThreads.length === 0 ? 0. : screwThreads.reduce((d: number, s) => d < s.coreHoleDiameter ? d : s.coreHoleDiameter, screwThreads[0]!.coreHoleDiameter),
		processConstraintsSheetMaterial: Array.from(getTable("processConstraintsSheetMaterial")),
		processConstraintsTubeMaterial: Array.from(getTable("processConstraintsTubeMaterial")),
		settings: settings(),
		sheetCuttingMotionParams: Array.from(getTable("sheetCuttingMotionParameters")),
		sheetCuttingProcessMappings: Array.from(getTable("sheetCuttingProcessMapping")),
		sheetCuttingProcesses: Array.from(getTable("sheetCuttingProcess")),
		sheetCuttingThicknessConstraints: Array.from(getTable("sheetCuttingThicknessConstraints")),
		sheets: sheetsInStock,
		tubeCuttingProcessMappings: Array.from(getTable("tubeCuttingProcessMapping")),
		tubeCuttingProcesses: configFlags.tubesEnabled ? Array.from(getTable("tubeCuttingProcess")) : [],
		tubeMaterials: tubeMaterials,
		tubeProfiles: tubeProfiles,
		tubeSpecifications: tubeSpecifications,
		tubes: tubes,
	};
}
