import simpleSpawn from "async-spawner";
import tempy from "tempy";

async function perspectiveShift(
	input: string,
	shift: transformCoefs
): Promise<string> {
	const output = tempy.file({ extension: "jpg" });
	const args = [
		"-verbose",
		input,
		"-virtual-pixel",
		"Transparent",
		"-distort",
		"PerspectiveProjection",
		shift.join(","),
		output,
	];
	console.log(args);
	await simpleSpawn("convert", args);
	return output; // the file name;
}

type coordinates = { x: number; y: number };
type alpr_candidate = {
	plate: string;
	confidence: number;
	matches_template: 0 | 1;
};

type alpr_response = {
	version: number;
	data_type: "alpr_results";
	epoch_time: number;
	img_width: number;
	img_height: number;
	processing_time_ms: number;
	regions_of_interest: {
		x: number;
		y: number;
		width: number;
		height: number;
	}[];
	results: {
		plate: string;
		confidence: number;
		matches_template: 0 | 1;
		plate_index: number;
		region: string;
		region_confidence: number;
		processing_time_ms: number;
		requested_topn: number;
		coordinates: [coordinates, coordinates, coordinates, coordinates];
		candidates: alpr_candidate[];
	}[];
};

function empty_alpr_response() {
	return {
		version: 2,
		data_type: <const>"alpr_results",
		epoch_time: Date.now(),
		img_width: 0,
		img_height: 0,
		processing_time_ms: 0,
		regions_of_interest: [],
		results: [],
	};
}

export default async function alpr(path: string): Promise<alpr_response> {
	const result = await simpleSpawn("alpr", [
		"-c",
		"eu",
		"-p",
		"pl",
		"-j",
		path,
	]);
	return JSON.parse(result.stdout);
}

function newpos(
	x: number,
	y: number,
	t: transformCoefs
): { x: number; y: number } {
	return {
		x: (x * t[0] + y * t[1] + t[2]) / (t[6] * x + t[7] * y + 1),
		y: (x * t[3] + y * t[4] + t[5]) / (t[6] * x + t[7] * y + 1),
	};
}

export async function perspectiveAssistedAlpr(
	input: string,
	shift: transformDesc
) {
	const shifted_image = await perspectiveShift(input, shift.forward);
	let result = await alpr(shifted_image);
	for (const i in result.results) {
		result.results[i].coordinates = result.results[i].coordinates.map(
			(c) => {
				// https://legacy.imagemagick.org/Usage/distorts/#perspective_internals
				const ret = newpos(c.x, c.y, shift.backward);
				console.log("new coords!", c, "→", ret);
				return ret;
			}
		) as [coordinates, coordinates, coordinates, coordinates];
	}
	return result;
}

type transformCoefs = [
	number,
	number,
	number,
	number,
	number,
	number,
	number,
	number
];
type transformDesc = { forward: transformCoefs; backward: transformCoefs };

// See perspective.md to learn how coefficients are created for calls below:
const transforms: transformDesc[] = [
	{
		forward: [
			0.418385,
			-0.064912,
			1.11204e-12,
			-1.07285e-16,
			0.407118,
			1.85136e-13,
			-0.000143964,
			-0.0000155811,
		],
		backward: [
			2.39014,
			0.381091,
			-5.07071e-13,
			8.09745e-16,
			2.45629,
			-1.95474e-12,
			0.000344095,
			0.0000931351,
		],
	},
];

export async function alprMultiPersp(input: string): Promise<alpr_response> {
	let resolved = false;
	return new Promise((resolve, reject) => {
		const callback = (arg: alpr_response) => {
			if (resolved) {
				return;
			}
			if (arg.results.length === 0) {
				return;
			}
			resolved = true;
			resolve(arg);
		};
		Promise.all([
			alpr(input).then(callback),
			...transforms.map((transform) =>
				perspectiveAssistedAlpr(input, transform).then(callback)
			),
		])
			.then(() => {
				if (!resolved) {
					resolve(empty_alpr_response());
				}
			})
			.catch(reject);
	});
}

console.log(newpos(4040, 0, transforms[0].forward));
console.log(newpos(4040, 0, transforms[0].backward));
