import {
	DocumentAlignment,
	DocumentImageType,
	DocumentItemType,
	FileType,
	TableType,
	WorkStepType,
} from "qrc:/js/lib/generated/enum";

import {
	computeCameraDirection,
	cross,
	multiply3,
	normalize,
	scalarMult,
	sum,
} from "qrc:/js/lib/geometry_utils";
import {
	boxFromResolution,
	defaultSvgResolution,
} from "qrc:/js/lib/scene_utils";
import {
	getTable,
} from "qrc:/js/lib/table_utils";
import {
	assert,
	bbDimensionX,
	bbDimensionY,
	defaultPngResolution,
} from "qrc:/js/lib/utils";

import {
	createArticleOverviewTable,
	createCommentOnDemand,
	createHeading,
	createImageItem,
	createPageBreak,
	createSeparator,
	createSpacerItem,
	createTitle,
} from "./export_utils";

function createOverviewSection(vertex: Vertex): Array<Array<DocumentItem>> {
	return [
		[
			createArticleOverviewTable(vertex, 6),
			createSpacerItem(3),
			createImageItem(vertex, DocumentImageType.png, 3),
		],
		...createCommentOnDemand(vertex),
	];
}

function radToDeg(angle: number): number {
	return angle * 180 / Math.PI;
}

function computeBendTable(vertex: Vertex, width: number): Array<Array<DocumentItem>> {
	const computeRows = () => {
		const bendLineData = wsi4.node.computeBendLineData(vertex);
		if (bendLineData === undefined) {
			return [];
		}
		const dieChoiceMap = wsi4.node.dieChoiceMap(vertex);
		if (dieChoiceMap === undefined) {
			return [];
		}
		const upperDieGroupTable = getTable(TableType.upperDieGroup);
		const lookUpUpperDieGroup = (bendDescriptor: number) => {
			const entry = dieChoiceMap.find(entry => entry.bendDescriptor === bendDescriptor);
			assert(entry !== undefined, "Expecting valid die choice map entry");
			const id = entry.bendDieChoice.upperDieGroupId;
			if (id === undefined) {
				return "N/A";
			}
			const row = upperDieGroupTable.find(r => r.identifier === id);
			return row === undefined ? "N/A" : row.name;
		};
		const lowerDieGroupTable = getTable(TableType.lowerDieGroup);
		const lookUpLowerDieGroup = (bendDescriptor: number) => {
			const entry = dieChoiceMap.find(entry => entry.bendDescriptor === bendDescriptor);
			assert(entry !== undefined, "Expecting valid die choice map entry");
			const id = entry.bendDieChoice.lowerDieGroupId;
			if (id === undefined) {
				return "N/A";
			}
			const row = lowerDieGroupTable.find(r => r.identifier === id);
			return row === undefined ? "N/A" : row.name;
		};
		return bendLineData
			.sort((lhs, rhs) => lhs.bendDescriptor - rhs.bendDescriptor)
			.map(bendLineData => {
				const index = bendLineData.bendDescriptor;
				const mapEntry = dieChoiceMap.find(entry => entry.bendDescriptor === bendLineData.bendDescriptor);
				assert(mapEntry !== undefined, "Expecting valid die choice map entry");
				return [
					{
						text: Number(index + 1)
							.toString(),
						alignment: DocumentAlignment.right,
					},
					{
						text: Number(radToDeg(bendLineData.bendAngle))
							.toFixed(1),
						alignment: DocumentAlignment.right,
					},
					{
						text: lookUpUpperDieGroup(index),
						alignment: DocumentAlignment.right,
					},
					{
						text: lookUpLowerDieGroup(index),
						alignment: DocumentAlignment.right,
					},
					{
						text: Number(bendLineData.constructedInnerRadius)
							.toFixed(1),
						alignment: DocumentAlignment.right,
					},
					{
						text: Number(mapEntry.bendDieChoice.baseClass.innerRadius)
							.toFixed(1),
						alignment: DocumentAlignment.right,
					},
					{
						text: Number(mapEntry.bendDieChoice.sharpDeduction)
							.toFixed(3),
						alignment: DocumentAlignment.right,
					},
				];
			});
	};
	return [
		[
			{
				type: DocumentItemType.table,
				content: {
					width: width,
					columnWidths: [
						5,
						10,
						25,
						25,
						12.5,
						12.5,
						10,
					],
					columnHeaders: [
						{
							text: "#",
							alignment: DocumentAlignment.right,
						},
						{
							text: "α [º]",
							alignment: DocumentAlignment.right,
						},
						{
							text: "OW",
							alignment: DocumentAlignment.right,
						},
						{
							text: "UW",
							alignment: DocumentAlignment.right,
						},
						{
							text: "r<sub>k</sub> [mm]",
							alignment: DocumentAlignment.right,
						},
						{
							text: "r<sub>r</sub> [mm]",
							alignment: DocumentAlignment.right,
						},
						{
							text: "BD [mm]",
							alignment: DocumentAlignment.right,
						},
					],
					rows: computeRows(),
				},
			},
		],
		[
			{
				type: DocumentItemType.paragraph,
				content: {
					width: width,
					alignment: DocumentAlignment.center,
					text: "<small>" +
						"OW: " + wsi4.util.translate("upper_die_group") + "; " +
						"UW: " + wsi4.util.translate("lower_die_group") + "; " +
						"r<sub>k</sub>: " + wsi4.util.translate("radius_constructed") + "; " +
						"r<sub>r</sub>: " + wsi4.util.translate("radius_resulting") + "; " +
						"BD: " + wsi4.util.translate("bend_deduction") +
						"</small>",
				},
			},
		],
	];
}

function createToleranceTable(): Array<Array<DocumentItem>> {
	return [
		createHeading(wsi4.util.translate("tolerances_in_mm_for_nominal_size_range_in_mm"), 3),
		[
			{
				type: DocumentItemType.table,
				content: {
					width: 12,
					columnWidths: [
						12.5,
						12.5,
						12.5,
						12.5,
						12.5,
						12.5,
						12.5,
						12.5,
					],
					columnHeaders: [
						{
							text: wsi4.util.translate("tolerance_class"),
							alignment: DocumentAlignment.left,
						},
						{
							text: "&gt; 0.5; &lt;= 3",
							alignment: DocumentAlignment.right,
						},
						{
							text: "&gt; 3; &lt;= 6",
							alignment: DocumentAlignment.right,
						},
						{
							text: "&gt; 6; &lt;= 30",
							alignment: DocumentAlignment.right,
						},
						{
							text: "&gt; 120; &lt;= 400",
							alignment: DocumentAlignment.right,
						},
						{
							text: "&gt; 400; &lt;= 1000",
							alignment: DocumentAlignment.right,
						},
						{
							text: "&gt; 1000; &lt;= 2000",
							alignment: DocumentAlignment.right,
						},
						{
							text: "&gt; 2000; &lt;= 4000",
							alignment: DocumentAlignment.right,
						},
					],
					rows: [
						[
							{
								text: wsi4.util.translate("fine"),
								alignment: DocumentAlignment.left,
							},
							{
								text: "±0.05",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.05",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.10",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.20",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.30",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.50",
								alignment: DocumentAlignment.right,
							},
							{
								text: "-",
								alignment: DocumentAlignment.right,
							},
						],
						[
							{
								text: wsi4.util.translate("medium"),
								alignment: DocumentAlignment.left,
							},
							{
								text: "±0.10",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.10",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.20",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.50",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.80",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±1.20",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±2.00",
								alignment: DocumentAlignment.right,
							},
						],
						[
							{
								text: wsi4.util.translate("rough"),
								alignment: DocumentAlignment.left,
							},
							{
								text: "±0.20",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.30",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.50",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±1.20",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±2.00",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±3.00",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±4.00",
								alignment: DocumentAlignment.right,
							},
						],
						[
							{
								text: wsi4.util.translate("very_rough"),
								alignment: DocumentAlignment.left,
							},
							{
								text: "-",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±0.50",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±1.00",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±2.50",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±4.00",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±6.00",
								alignment: DocumentAlignment.right,
							},
							{
								text: "±8.00",
								alignment: DocumentAlignment.right,
							},
						],
					],
				},
			},
		],
	];
}

function createUnfoldView(vertex: Vertex): Array<Array<DocumentItem>> {
	const twoDimRep = wsi4.node.twoDimRep(vertex);
	if (twoDimRep === undefined) {
		wsi4.util.error("export_bend_drawing::createUnfoldView(): Expecting twoDimRep");
		return [];
	}

	const bbox = wsi4.cam.util.boundingBox2(twoDimRep);
	const x = bbDimensionX(bbox);
	const y = bbDimensionY(bbox);
	const z = wsi4.node.sheetThickness(vertex);
	assert(z !== undefined, "Expecting valid sheet thickness");

	return [
		createPageBreak(),
		createHeading(wsi4.util.translate("unfolding"), 3),
		[
			createSpacerItem(2),
			createImageItem(vertex, DocumentImageType.svg, 8, DocumentAlignment.center),
			createSpacerItem(2),
		],
		[
			{
				type: DocumentItemType.paragraph,
				content: {
					width: 12,
					alignment: DocumentAlignment.center,
					text: `<small>${wsi4.util.translate("Dimensions")} [mm]: ${x.toFixed(1)} x ${y.toFixed(1)} x ${z.toFixed(1)}</small>`,
				},
			},
		],
		createSeparator(),
		...computeBendTable(vertex, 12),
	];
}

function createBendMeasurementScene(vertex: Vertex, measurementScene: MeasurementScene, index: number, resolution: Resolution): Array<Array<DocumentItem>> {
	const scene = measurementScene.scene;
	const svgBa = wsi4.geo.util.renderScene(scene, FileType.svg, {
		resolution: resolution,
		viewPort: boxFromResolution(resolution),
	});
	const bendDrawingImageItem = {
		type: DocumentItemType.image,
		content: {
			width: 6,
			alignment: DocumentAlignment.center,
			uuid: "bendMeasurementScene" + index.toString(),
			type: DocumentImageType.svg,
			data: svgBa,
		},
	};

	const camera = measurementScene.camera;
	// Update camera to look at assembly from upper left corner
	{
		const dir = computeCameraDirection(camera);
		const cr = normalize(cross(dir, camera.up));
		const target = normalize(sum(cr, sum(scalarMult(0.1, normalize(dir)), scalarMult(0.4, normalize(camera.up)))));

		const rotationMatrix = wsi4.geo.util.rotationMatrix(cr, target);
		const newDir = multiply3(rotationMatrix, dir);
		camera.eye = sum(camera.center, scalarMult(-1, newDir));
	}

	const pngImageItem = (() => {
		const assembly = wsi4.node.assembly(vertex);
		if (assembly === undefined) {
			return createSpacerItem(4);
		} else {
			return {
				type: DocumentItemType.image,
				content: {
					width: 6,
					alignment: DocumentAlignment.center,
					uuid: wsi4.util.toKey(assembly) + index.toString(),
					type: DocumentImageType.png,
					data: wsi4.geo.assembly.renderIntoPng(assembly, camera, defaultPngResolution()),
				},
			};
		}
	})();

	return [
		createPageBreak(),
		createHeading("#" + index.toString(), 3),
		[
			bendDrawingImageItem,
			pngImageItem,
		],
	];
}

function createBendMeasurementScenes(vertex: Vertex): Array<Array<DocumentItem>> {
	const resolution = defaultSvgResolution();
	return wsi4.node.bendMeasurementScenes(vertex, 48, resolution)
		.reduce((acc: Array<Array<DocumentItem>>, measurementScene, index) => [
			...acc,
			...createBendMeasurementScene(vertex, measurementScene, index, resolution),
		], []);
}

export function createBendDrawing(vertex: Vertex): Array<Array<DocumentItem>> {
	if (wsi4.node.workStepType(vertex) !== WorkStepType.sheetBending) {
		return wsi4.throwError("Expecting vertex for node of type sheetBending");
	}
	return [
		...createTitle(wsi4.util.translate("bend_drawing")),
		[ createSpacerItem(12) ],
		...createOverviewSection(vertex),
		[ createSpacerItem(12) ],
		...createToleranceTable(),
		[ createSpacerItem(12) ],
		...createUnfoldView(vertex),
		...createBendMeasurementScenes(vertex),
	];
}
