import {
	TwoDimImportResultType,
} from "qrc:/js/lib/generated/enum";
import {
	isTwoDimImportResultEngravingInvalid,
	isTwoDimImportResultPartInvalid,
	isTwoDimImportResultSuccess,
} from "qrc:/js/lib/generated/typeguard";
import {
	assert,
	bbDimensionX,
	bbDimensionY,
} from "./utils";

export function twoDimRepFromLayered(layered: Layered, selection: DefaultLayeredSelection, scaleFactor: number): TwoDimRepresentation | undefined {
	const tolerances = [
		0.001,
		0.002,
		0.005,
		0.01,
		0.02,
		0.05,
		0.1,
	];

	for (const tol of tolerances) {
		const result = wsi4.cam.util.twoDimRepFromLayered(
			layered,
			selection.cuttingLds,
			selection.engravingLds,
			tol,
			scaleFactor,
		);
		if (result.type === TwoDimImportResultType.success) {
			const resultContent = result.content;
			assert(isTwoDimImportResultSuccess(resultContent));
			return resultContent.twoDimRep;
		} else {
			const resultContent = result.content;
			assert(isTwoDimImportResultPartInvalid(resultContent) || isTwoDimImportResultEngravingInvalid(resultContent));
		}
	}
	return undefined;
}

interface DefaultLayeredSelection {
	cuttingLds: number[];
	engravingLds: number[];
}

export function getDefaultLayeredSelection(layered: Layered, scaleFactor: number): DefaultLayeredSelection {

	const layers = wsi4.geo.util.layers(layered);
	const biggestIopLayer = (() => {
		let biggestIop: InnerOuterPolygon | undefined = undefined;
		let biggestVolume = 0;
		let des = -1;
		for (const layer of layers) {
			// can't use flatMap
			const segments = wsi4.geo.util.layerPaths(layered, layer.descriptor).reduce((s: Segment[], path) => [
				...s,
				...path,
			], []);
			const iop = wsi4.geo.util.createIop(segments);
			if (iop === undefined) {
				continue;
			}
			const b = wsi4.geo.util.boundingBox2d(iop);
			const v = bbDimensionX(b) * bbDimensionY(b);
			if (v !== 0 && v > biggestVolume) {
				assert(!wsi4.geo.util.isLayerEmpty(layered, layer.descriptor), "Empty layer the biggest layer iop");
				biggestIop = iop;
				biggestVolume = v;
				des = layer.descriptor;
			}
		}
		if (biggestIop === undefined) {
			return undefined;
		}
		return {
			iop: biggestIop,
			des: des,
		};

	})();

	if (biggestIopLayer === undefined) {
		return {
			cuttingLds: [],
			engravingLds: [],
		};
	}

	const engravingNumber = 2; // 2 means yellow layer

	const engravingLds = layers.filter(l => l.number === engravingNumber).map(l => l.descriptor);

	const remainingLds = layers.filter(l => l.number !== engravingNumber).map(l => l.descriptor);

	const twoDimRep = twoDimRepFromLayered(layered, {
		cuttingLds: remainingLds,
		engravingLds: engravingLds,
	}, scaleFactor);

	if (twoDimRep === undefined) {
		// couldn't generate layered with all layers
		return {
			cuttingLds: [ biggestIopLayer.des ],
			engravingLds: engravingLds,
		};
	} else {
		return {
			cuttingLds: remainingLds,
			engravingLds: engravingLds,
		};
	}
}
