import { PageInfo, PageRotation } from "../../../public/page";
import { IronPdfServiceClient } from "../../generated_proto/ironpdfengineproto/IronPdfService";
import { Access } from "../../access";
import {
	AsyncPdfPageSelectionToIndexes,
	handleEmptyResultP__Output,
	handlePdfDocumentResultP__Output,
	handleRemoteException,
} from "../util";
import { PageInfoFromProto, PageRotationToProto } from "./converter";
import { PdfPaperSize } from "../../../public/paper";
import { pdfPaperSizeToProto } from "../chrome/converter";
import { PdfiumGetPagesResultP__Output } from "../../generated_proto/ironpdfengineproto/PdfiumGetPagesResultP";
import { PdfiumRemovePagesResultP__Output } from "../../generated_proto/ironpdfengineproto/PdfiumRemovePagesResultP";
import { PdfPageSelection } from "../../../public/types";
import {PdfiumResizePageRequestP} from "../../generated_proto/ironpdfengineproto/PdfiumResizePageRequestP";

export async function getPageInfo(id: string): Promise<PageInfo[]> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise(
		(
			resolve: (_: PageInfo[]) => void,
			reject: (errorMsg: string) => void
		) => {
			client.pdfiumPageGetPages(
				{ document: { documentId: id } },
				(err, value) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						resolve(
							handlePdfiumGetPagesResultP__Output(value, reject)
						);
					}
				}
			);
		}
	);
}

export async function setPageRotation(
	id: string,
	pageRotation: PageRotation,
	options?: { pdfPageSelection?: PdfPageSelection }
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	const pi = await AsyncPdfPageSelectionToIndexes(
		id,
		options?.pdfPageSelection
	);
	return new Promise(
		(resolve: () => void, reject: (errorMsg: string) => void) => {
			client.pdfiumPageSetPagesRotation(
				{
					document: { documentId: id },
					pageIndexes: pi,
					pageRotation: PageRotationToProto(pageRotation),
				},
				(err, value) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handleEmptyResultP__Output(value, reject);
						resolve();
					}
				}
			);
		}
	);
}

export async function resizePage(
	id: string,
	pdfPaperSize: PdfPaperSize,
	options?: { pdfPageSelection?: PdfPageSelection }
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();

	const paperSizeSetting = pdfPaperSizeToProto(pdfPaperSize);
	const pi = await AsyncPdfPageSelectionToIndexes(
		id,
		options?.pdfPageSelection
	);

	if(paperSizeSetting == undefined || paperSizeSetting?.widthHeight == undefined){
		throw new Error(`Cannot resizePage. Unknown pdfPaperSize: ${pdfPaperSize} ${paperSizeSetting}`);
	}else{
		const pHeight = paperSizeSetting.widthHeight.height;
		const pWidth = paperSizeSetting.widthHeight.width;
		const promises = pi.map((pageIndex) => {
			return new Promise<void>(
				(resolve: () => void, reject: (errorMsg: string) => void) => {
					const req :PdfiumResizePageRequestP ={
						document : {documentId :id},
						pageHeight : pHeight,
						pageWidth: pWidth,
						pageIndex: pageIndex}
					client.pdfiumPageResizePage(req,
						(err, value) => {
							if (err) {
								reject(`${err.name}/n${err.message}`);
							} else if (value) {
								handleEmptyResultP__Output(value, reject);
								resolve();
							}
						}
					);
				}
			);
		});
		await Promise.all(promises);
		return;
	}
}

function handlePdfiumGetPagesResultP__Output(
	proto: PdfiumGetPagesResultP__Output,
	reject: (errorMsg: string) => void
): PageInfo[] {
	if (proto) {
		if (proto.exception) {
			handleRemoteException(proto.exception, reject);
		}
		if (proto.result) {
			return (
				proto.result.pages?.map((value) => PageInfoFromProto(value)) ??
				[]
			);
		}
	}
	throw new Error("Error empty message");
}

export async function insertPdf(
	mainDocumentId: string,
	anotherDocumentId: string,
	insertionIndex: number
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	return new Promise<void>(
		(resolve: () => void, reject: (errorMsg: string) => void) => {
			client.pdfiumPageInsertPdf(
				{
					mainDocument: { documentId: mainDocumentId },
					insertedDocument: { documentId: anotherDocumentId },
					insertionIndex: insertionIndex,
				},
				(err, value) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handleEmptyResultP__Output(value, reject);
						resolve();
					}
				}
			);
		}
	);
}

export async function removePage(
	id: string,
	options?: { PdfPageSelection?: PdfPageSelection }
): Promise<void> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	const pi = await AsyncPdfPageSelectionToIndexes(
		id,
		options?.PdfPageSelection
	);
	return new Promise<void>(
		(resolve: () => void, reject: (errorMsg: string) => void) => {
			client.pdfiumPageRemovePages(
				{
					document: { documentId: id },
					pageIndexes: pi,
				},
				(err, value) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handlePdfiumRemovePagesResultP__Output(
							value,
							reject
						);
						//ignore remaining
						resolve();
					}
				}
			);
		}
	);
}

export function handlePdfiumRemovePagesResultP__Output(
	proto: PdfiumRemovePagesResultP__Output | undefined,
	reject: (errorMsg: string) => void
): number {
	if (proto) {
		if (proto.exception) {
			handleRemoteException(proto.exception, reject);
		}
		if (proto.result) {
			return proto.result!;
		}
	}
	throw new Error("Error empty message");
}

export async function duplicate(
	id: string,
	options?: { PdfPageSelection?: PdfPageSelection }
): Promise<string> {
	const client: IronPdfServiceClient = await Access.ensureConnection();
	const pi = await AsyncPdfPageSelectionToIndexes(
		id,
		options?.PdfPageSelection
	);
	return new Promise<string>(
		(resolve: (_: string) => void, reject: (errorMsg: string) => void) => {
			client.pdfiumPageCopyPages(
				{
					document: { documentId: id },
					pageIndexes: pi,
				},
				(err, value) => {
					if (err) {
						reject(`${err.name}/n${err.message}`);
					} else if (value) {
						handlePdfDocumentResultP__Output(
							value,
							resolve,
							reject
						);
					}
				}
			);
		}
	);
}
