import AbortablePromise from "abortable-promise";
import { deferedSpawn } from "async-spawner";
import tempy from "tempy";
import {
	newpos,
	transformCoefs,
	transformDesc,
	transforms,
} from "./transforms";

function perspectiveShift(
	input: string,
	shift: transformCoefs
): AbortablePromise<string> {
	return new AbortablePromise(async function* (await_or_abort) {
		const output = tempy.file({ extension: "jpg" });
		const args = [
			"-verbose",
			input,
			"-virtual-pixel",
			"Transparent",
			"-distort",
			"PerspectiveProjection",
			shift.join(","),
			output,
		];
		const def = await deferedSpawn("convert", args);
		const next_data_promise = def.waitForNextData();
		yield;
		await await_or_abort(next_data_promise, () => def.kill());
		return output;
	});
}

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

export 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: [],
	};
}

type DeferedSpawn = ReturnType<typeof deferedSpawn>;

export function alpr(path: string): AbortablePromise<alpr_response> {
	return new AbortablePromise(async function* (await_or_abort) {
		console.log("Running alpr abortablepromise");
		const def = await deferedSpawn("alpr", [
			"-c",
			"eu",
			"-p",
			"pl",
			"-j",
			path,
		]);
		console.log("created deferedspawn");
		const answer_promise = def.waitForNextData();
		yield;
		const answer = await await_or_abort(answer_promise, () => def.kill());
		console.log("got answer!");
		return JSON.parse((answer as unknown) as string);
	});
}

export function perspectiveAssistedAlpr(
	input: string,
	shift: transformDesc
): AbortablePromise<alpr_response> {
	console.log("new ap perspectiveAlpr");
	return new AbortablePromise(async function* (await_or_abort) {
		const shifted_image = await await_or_abort(
			perspectiveShift(input, shift.forward)
		);
		yield;
		console.log("after yield1");
		let result = await await_or_abort(alpr(shifted_image));
		yield;
		console.log("after yield2");
		console.log("~~~Got perspective shifted response");
		if (result === null) {
			return null;
		}
		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;
	});
}

// export function alprMultiPersp(input: string): AbortablePromise<alpr_response> {
// 	return new AbortablePromise(async function* (await_or_abort) {
// 		let resolved = false;
// 		return new Promise((resolve, reject): void => {
// 			let promises: Array<AbortablePromise<any> | Promise<any>> = [];
// 			const callback = (arg: alpr_response) => {
// 				if (resolved) {
// 					return;
// 				}
// 				if (arg.results.length === 0) {
// 					return;
// 				}
// 				resolved = true;
// 				for (const promise of promises) {
// 					if (promise instanceof AbortablePromise) {
// 						console.log("aovrting promise!");
// 						promise.abort();
// 					}
// 				}
// 				resolve(arg);
// 			};
// 			promises = [
// 				alpr(input).then(callback),
// 				...transforms.map((transform) =>
// 					perspectiveAssistedAlpr(input, transform).then(callback)
// 				),
// 			];
// 			Promise.all(promises)
// 				.then(() => {
// 					if (!resolved) {
// 						resolve(empty_alpr_response());
// 					}
// 				})
// 				.catch(reject);
// 		});
// 	});
// }
//
export function alprMultiPersp(input: string): AbortablePromise<alpr_response> {
	const ret = AbortablePromise.deadlyRace<alpr_response>(
		[
			alpr(input),
			// ...transforms.map((transform) =>
			// 	perspectiveAssistedAlpr(input, transform)
			// ),
			perspectiveAssistedAlpr(input, transforms[0]),
		],
		async (arg: alpr_response) => {
			return arg.results.length > 0;
		}
	);
	return ret;
	// return new Promise((resolve, reject) => {
	// 	alpr(input).then(resolve, reject);
	// });
	// return new Promise((resolve, reject) => {
	// 	perspectiveAssistedAlpr(input, transforms[0]);
	// });
}
