const fs = require("fs");
const path = require("path");
const gm = require("./gm");
const mime = require("./mimetype");

function shuffle(array) {
	for (let i = array.length - 1; i > 0; i--) {
		let j = Math.floor(Math.random() * (i + 1));
		[array[i], array[j]] = [array[j], array[i]];
	}
}

function readFixedBytes(pathToFile, size = 16) {
	const fd = fs.openSync(pathToFile, "r");
	try {
		const buffer = Buffer.alloc(size);
		fs.readSync(fd, buffer, 0, size, 0);
		return buffer;
	} catch (err) {
		console.error(err);
	} finally {
		fs.closeSync(fd);
	}
}

async function isImage (pathToFile, requiredSize=null) {
	const contentType = mime.byPath(pathToFile).contentType;
	const isImageMime = /^image\//m.test(contentType);
	if (
		!isImageMime || !requiredSize ||
		!Array.isArray(requiredSize)
	) return isImageMime;
	
	const [width, height] = requiredSize;
	const { width:ow, height:oh } = await gm.getSize(
		fs.readFileSync(pathToFile)
	);
	
	return width === ow && height === oh;
}

async function getImageFromFolderGeneral (imagesFolder) {
	// Get list of images from folder
	const entries = fs.readdirSync(imagesFolder, { withFileTypes: true });
	const images = [];
	for (let entry of entries) {
		if (!entry.isDirectory() && !!(await isImage(path.join(imagesFolder, entry.name), [150, 150]))) {
			images.push(path.join(imagesFolder, entry.name));
		}
	}
	
	if (images.length === 0)
		throw new Error(imagesFolder, "has not required images (150x150 px)");
	
	shuffle(images);
	//console.debug(images);
	return images[0];
}

function genHashGeneral(length = 3) {
	return Buffer.from(
		[...new Array(length)].map((x) => Math.round(Math.random() * 255)),
	).toString("hex");
}

async function generateTask (imagesFolder, options = {}) {
	if (!path.isAbsolute(imagesFolder))
		imagesFolder = path.join(process.cwd(), imagesFolder);
	
	// Define tools
	if (!options.genHash) options.genHash = genHashGeneral;
	if (!options.getImageFromFolder)
		options.getImageFromFolder = getImageFromFolderGeneral;
	
	const choosenImage = fs.readFileSync(await getImageFromFolderGeneral(imagesFolder));
	// Split the image by 9 shards
	const fragments = new Object();
	let answer = "";
	const addOps = [];
	for (let row = 0; row < 3; row++)
		for (let col = 0; col < 3; col++) {
			let key;
			do {
				key = options.genHash();
			} while (!!fragments[key]);
			answer += key;
			const shardBytes = await gm.repixelise(
				await gm.cropImage(choosenImage, 50, 50, col * 50, row * 50),
			);
			addOps.push(
				() =>
					(fragments[key] =
						`data:image/png;base64,${shardBytes.toString("base64")}`),
			);
		}
	shuffle(addOps);
	addOps.forEach((f) => f());
	const successCode = options.genHash(8);
	
	return {
		openPuzzleCaptchaAPI: {
			getTask: {
				isError: false,
				mode: "3x3",
				fragments
			},
			taskSolved: {
				isError: false,
				isSuccess: true,
				successCode
			},
		},
		answer,
	};
}

module.exports = { generateTask };
