import Compress from "compress.js";

const FORCE_ORIENTATION = false; // Si se fuerza la orientación o se deja tal cual.
// Cuando empezó este desarrollo canvas no reconocía la orientación.
// Un mes más tarde empezó a fallar todo porque sí la estaba reconociendo.
// Se deja como variable porque no sabemos cuándo puede volver a cambiar ni por qué cambió.

const imageResizeCropAndCompress = async (
	image: File,
	newWidth: number,
	newHeight: number,
	maxSize: number,
	debug: boolean = false,
) => {
	return getOrientation(image)
		.then((orientation) => imageResize(image, newWidth, newHeight, orientation))
		.then((image) => compressImage(image, newWidth, newHeight, maxSize, debug));
};

const getOrientation = (file: File): Promise<number> => {
	return new Promise((resolve, reject) => {
		if (!FORCE_ORIENTATION) resolve(1);
		const reader = new FileReader();

		reader.onload = (e) => {
			const result: any = e.target && e.target.result ? e.target.result : null;
			const view = new DataView(result);

			if (view.getUint16(0, false) !== 0xffd8) {
				resolve(1);
			}

			const length = view.byteLength;
			let offset = 2;

			while (offset < length) {
				if (view.getUint16(offset + 2, false) <= 8) resolve(1);
				const marker = view.getUint16(offset, false);
				offset += 2;
				if (marker === 0xffe1) {
					if (view.getUint32((offset += 2), false) !== 0x45786966) {
						resolve(1);
					}

					const little = view.getUint16((offset += 6), false) === 0x4949;
					offset += view.getUint32(offset + 4, little);
					const tags = view.getUint16(offset, little);
					offset += 2;
					for (let i = 0; i < tags; i++) {
						if (view.getUint16(offset + i * 12, little) === 0x0112) {
							resolve(view.getUint16(offset + i * 12 + 8, little));
						}
					}
				} else if ((marker && 0xff00) !== 0xff00) {
					break;
				} else {
					offset += view.getUint16(offset, false);
				}
			}
			resolve(1);
		};
		reader.readAsArrayBuffer(file);
	});
};

const compressImage = async (
	imageFile: File,
	maxWidth: number,
	maxHeight: number,
	maxSize: number,
	debug: boolean,
): Promise<File> => {
	const compress = new Compress();
	if (debug) console.log(`originalFile size ${imageFile.size / 1024} KB`);
	return compress
		.compress([imageFile], {
			size: maxSize, // the max size in MB
			maxWidth, // the max width of the output image
			maxHeight, // the max height of the output image
			resize: false, // defaults to true, set false if you do not want to resize the image width and height
		})
		.then((result) => {
			// returns an array of compressed images
			const img1 = result[0];
			const base64str = img1.data;
			const imgExt = img1.ext;
			let compressedFile = Compress.convertBase64ToFile(base64str, imgExt);
			compressedFile = new File([compressedFile], imageFile.name, { type: imageFile.type });
			if (debug) console.log(`compressedFile size ${compressedFile.size / 1024} KB`);
			return compressedFile;
		});
};

const dataURLToBlob = (dataURL: string): Blob => {
	const BASE64_MARKER = ";base64,";

	if (dataURL.indexOf(BASE64_MARKER) === -1) {
		const parts = dataURL.split(",");
		const contentType = parts[0].split(":")[1];
		const raw = parts[1];
		return new Blob([raw], { type: contentType });
	}

	const parts = dataURL.split(BASE64_MARKER);
	const contentType = parts[0].split(":")[1];
	const raw = window.atob(parts[1]);
	const rawLength = raw.length;
	const uInt8Array = new Uint8Array(rawLength);

	for (let i = 0; i < rawLength; ++i) {
		uInt8Array[i] = raw.charCodeAt(i);
	}

	return new Blob([uInt8Array], { type: contentType });
};

const imageResize = (picture: File, newWidth: number, newHeight: number, exifOrientation: number = 1): Promise<any> => {
	return new Promise((resolve, reject) => {
		try {
			const reader = new FileReader();
			reader.onload = (readerEvent) => {
				const image = new Image();
				image.onload = () => {
					// Calc exif rotation
					const originalWidth = image.width;
					const originalHeight = image.height;
					const [width, height] =
						exifOrientation >= 5 && exifOrientation <= 8
							? [originalHeight, originalWidth]
							: [originalWidth, originalHeight];
					// Rotate canvas
					const newCanvas = document.createElement("canvas");
					const context = newCanvas.getContext("2d");
					const tWidth = originalWidth;
					const tHeight = originalHeight;
					newCanvas.width = originalWidth;
					newCanvas.height = originalHeight;
					if (context) {
						context.drawImage(image, 0, 0); // Antes estaba detrás del switch
						switch (exifOrientation) {
							case 2:
								context.transform(-1, 0, 0, 1, tWidth, 0);
								break;
							case 3:
								context.transform(-1, 0, 0, -1, tWidth, tHeight);
								break;
							case 4:
								context.transform(1, 0, 0, -1, 0, tHeight);
								break;
							case 5:
								context.transform(0, 1, 1, 0, 0, 0);
								break;
							case 6:
								context.transform(0, 1, -1, 0, tHeight, 0);
								break;
							case 7:
								context.transform(0, -1, -1, 0, tHeight, tWidth);
								break;
							case 8:
								context.transform(0, -1, 1, 0, 0, tWidth);
								break;
							default:
								break;
						}
					}
					// Calc data to crop
					let oX = 0;
					let oWidth = width;
					let dif = width / newWidth;
					let oHeight = newHeight * dif;
					let oY = Math.floor((height - oHeight) / 2);
					if (oY < 0) {
						// resulta que la imagen no tiene el alto suficiente para ajustar sobre el ancho
						oHeight = height;
						dif = height / newHeight;
						oWidth = newWidth * dif;
						oY = 0;
						oX = Math.floor((width - oWidth) / 2);
					}
					// Create canvas
					const canvas = document.createElement("canvas");
					canvas.width = newWidth;
					canvas.height = newHeight;
					const canvasContext = canvas.getContext("2d");
					if (canvasContext) {
						canvasContext.drawImage(
							newCanvas, // image
							oX, // source x start
							oY, // source y start
							oWidth, // source width
							oHeight, // source height
							0,
							0,
							newWidth,
							newHeight,
						);
					}
					const dataUrl = canvas.toDataURL("image/jpeg");
					const resizedImage = dataURLToBlob(dataUrl);
					resolve(resizedImage);
				};

				const result: any = readerEvent.target ? readerEvent.target.result : null;
				image.src = result;
			};
			reader.readAsDataURL(picture);
		} catch {
			reject();
		}
	});
};

export { compressImage, imageResizeCropAndCompress };
