import {
	AffixFonts,
	BarcodeStampOptions,
	BarcodeType,
	BaseStampOptions,
	CssMediaType,
	CustomPaperSize,
	FitToPaperModes,
	HorizontalAlignment,
	HtmlStampOptions,
	ImageBehavior,
	ImageStampOptions,
	Length,
	MeasurementUnit,
	PageInfo,
	PaperSizeUnit,
	PdfPageSelection,
	PdfPaperOrientation,
	PdfPaperSize,
	PdfPassword,
	TextStampOptions,
	UseMargins,
	VerticalAlignment,
	WaitFor,
	WaitForType,
	TableOfContentsTypes,
} from "../../../public/types";

import {ChromePdfRenderOptionsP} from "../../generated_proto/ironpdfengineproto/ChromePdfRenderOptionsP";
import {ChromeFitToPaperModesP} from "../../generated_proto/ironpdfengineproto/ChromeFitToPaperModesP";
import {ChromePdfPaperOrientationP} from "../../generated_proto/ironpdfengineproto/ChromePdfPaperOrientationP";
import {ChromePdfCssMediaTypeP} from "../../generated_proto/ironpdfengineproto/ChromePdfCssMediaTypeP";
import {ChromePdfPaperSizeP} from "../../generated_proto/ironpdfengineproto/ChromePdfPaperSizeP";
import {ChromeUseMarginsP} from "../../generated_proto/ironpdfengineproto/ChromeUseMarginsP";
import {ChromeHtmlHeaderFooterP} from "../../generated_proto/ironpdfengineproto/ChromeHtmlHeaderFooterP";
import {ChromeTextHeaderFooterP} from "../../generated_proto/ironpdfengineproto/ChromeTextHeaderFooterP";
import {FontTypeP} from "../../generated_proto/ironpdfengineproto/FontTypeP";
import {ChromeHttpLoginCredentialsP} from "../../generated_proto/ironpdfengineproto/ChromeHttpLoginCredentialsP";
import {PaperSize} from "../../../public/paper";
import {HtmlAffix, TextAffix} from "../../../public/affix";
import {ChromePdfRenderOptions, defaultChromePdfRenderOptions, HttpLoginCredentials,} from "../../../public/render";
import {ChromeImageBehaviorP} from "../../generated_proto/ironpdfengineproto/ChromeImageBehaviorP";
import {PageRotationToProto} from "../pdfium/converter";
import {
	_ironpdfengineproto_ChromeApplyStampRequestStreamP_InfoP
} from "../../generated_proto/ironpdfengineproto/ChromeApplyStampRequestStreamP";
import {LengthP} from "../../generated_proto/ironpdfengineproto/LengthP";
import {MeasurementUnitP} from "../../generated_proto/ironpdfengineproto/MeasurementUnitP";
import {ChromeBarcodeEncodingP} from "../../generated_proto/ironpdfengineproto/ChromeBarcodeEncodingP";
import {ChromeTableOfContentsTypesP} from "../../generated_proto/ironpdfengineproto/ChromeTableOfContentsTypesP";
import {VerticalAlignmentP} from "../../generated_proto/ironpdfengineproto/VerticalAlignmentP";
import {HorizontalAlignmentP} from "../../generated_proto/ironpdfengineproto/HorizontalAlignmentP";
import {isNullOrUndefined, PdfPageSelectionToIndexes} from "../util";
import {PdfiumPageP} from "../../generated_proto/ironpdfengineproto/PdfiumPageP";

export function fitToPaperModeToProto(
	fitToPaperMode: FitToPaperModes
): ChromeFitToPaperModesP{
	return {enumValue: fitToPaperMode};
}

export function PdfPaperOrientationToProto(
	pdfPaperOrientation: PdfPaperOrientation
): ChromePdfPaperOrientationP {
	return {enumValue: pdfPaperOrientation};
}

export function CssMediaTypeToProto(
	cssMediaType: CssMediaType
): ChromePdfCssMediaTypeP {
	return {enumValue: cssMediaType};
}

export function httpLoginCredentialsToProto(
	httpLoginCredentials?: HttpLoginCredentials | undefined
): ChromeHttpLoginCredentialsP | null {
	if (!httpLoginCredentials) return null;
	throw new Error("not implement");
}

interface WaitForDetail {
	type: number;
	timeout: number;
	networkIdleDuration: number;
	numAllowedInFlight: number;
	renderDelayDuration: number;
	htmlElementQueryStr: string;
}

export function convertWaitFor(waitFor: WaitFor | undefined): WaitForDetail {
	const defaultMaxWaitTime = 10000;
	const defaultNetworkIdleDuration = 500;
	const defaultNumAllowedInFlight = 0;
	const defaultRenderDelayDuration = 20;
	const defaultHtmlElementQueryStr = "";
	if (!waitFor) {
		return {
			type: 0,
			timeout: defaultMaxWaitTime,
			networkIdleDuration: defaultNetworkIdleDuration,
			numAllowedInFlight: defaultNumAllowedInFlight,
			renderDelayDuration: defaultRenderDelayDuration,
			htmlElementQueryStr: defaultHtmlElementQueryStr
		};
	}
	switch (waitFor.type) {
		case WaitForType.PageLoad:
			return {
				type: 0,
				timeout: defaultMaxWaitTime,
				networkIdleDuration: defaultNetworkIdleDuration,
				numAllowedInFlight: defaultNumAllowedInFlight,
				renderDelayDuration: defaultRenderDelayDuration,
				htmlElementQueryStr: defaultHtmlElementQueryStr
			};
		case WaitForType.JavaScript:
			return {
				type: 1,
				timeout: waitFor.maxWaitTime ?? defaultMaxWaitTime,
				networkIdleDuration: defaultNetworkIdleDuration,
				numAllowedInFlight: defaultNumAllowedInFlight,
				renderDelayDuration: defaultRenderDelayDuration,
				htmlElementQueryStr: defaultHtmlElementQueryStr
			};
		case WaitForType.RenderDelay:
			return {
				type: 2,
				timeout: defaultMaxWaitTime,
				networkIdleDuration: defaultNetworkIdleDuration,
				numAllowedInFlight: defaultNumAllowedInFlight,
				renderDelayDuration:
					waitFor.delay ?? defaultRenderDelayDuration,
				htmlElementQueryStr: defaultHtmlElementQueryStr
			};

		case WaitForType.NetworkIdle0:
			return {
				type: 3,
				timeout: waitFor.maxWaitTime ?? defaultMaxWaitTime,
				networkIdleDuration: defaultNetworkIdleDuration,
				numAllowedInFlight: defaultNumAllowedInFlight,
				renderDelayDuration: defaultRenderDelayDuration,
				htmlElementQueryStr: defaultHtmlElementQueryStr
			};
		case WaitForType.NetworkIdle2:
			return {
				type: 4,
				timeout: waitFor.maxWaitTime ?? defaultMaxWaitTime,
				networkIdleDuration: defaultNetworkIdleDuration,
				numAllowedInFlight: defaultNumAllowedInFlight,
				renderDelayDuration: defaultRenderDelayDuration,
				htmlElementQueryStr: defaultHtmlElementQueryStr
			};
		case WaitForType.NetworkIdleN:
			return {
				type: 5,
				timeout: waitFor.maxWaitTime ?? defaultMaxWaitTime,
				networkIdleDuration:
					waitFor.networkIdleDuration ?? defaultNetworkIdleDuration,
				numAllowedInFlight:
					waitFor.maxNumAllowedInflight ?? defaultNumAllowedInFlight,
				renderDelayDuration: defaultRenderDelayDuration,
				htmlElementQueryStr: defaultHtmlElementQueryStr
			};
		case WaitForType.HtmlElement:
			return {
				type: 6,
				timeout: waitFor.maxWaitTime ?? defaultMaxWaitTime,
				networkIdleDuration: defaultNetworkIdleDuration,
				numAllowedInFlight: defaultNumAllowedInFlight,
				renderDelayDuration: defaultRenderDelayDuration,
				htmlElementQueryStr: waitFor.htmlQueryStr
			};
	}
}

/**
 * Separator used when joining string arrays into the single-string proto fields
 * consumed by {@code DictStringMarshaler} on the IronPdfEngine side
 * (e.g. {@code auto_bookmark_css_selectors}, {@code element_query_selectors}).
 *
 * <b>Must match</b> {@code IronSoftware.Pdfium.DictStringMarshaler.ELEM_SEP}
 * used by the C# engine. If cross-language rendering options stop being picked up by
 * the engine, this constant is the first place to double-check against the C# source.
 */
export const DICT_ELEM_SEP = "(,IRON)";
 
/**
 * Joins a string array into the single-string format the engine expects. Returns
 * {@code undefined} when the input is nullish or effectively empty, so callers can
 * skip setting the optional proto field.
 */
function joinDictArray(values?: string[] | undefined): string | undefined {
	if (!values || values.length === 0) return undefined;
	const filtered = values.filter((v): v is string => typeof v === "string" && v.length > 0);
	if (filtered.length === 0) return undefined;
	return filtered.join(DICT_ELEM_SEP);
}
 
/**
 * Validate auto-bookmark heading levels. Mirrors the C# {@code ValidateAutoBookmarkHeadingLevels}
 * check added during PR review: when {@code autoBookmarksFromHeadings} is enabled, both
 * min and max heading levels must be in [1, 6] and min <= max.
 */
function validateAutoBookmarkHeadingLevels(options: ChromePdfRenderOptions): void {
	if (options.autoBookmarksFromHeadings !== true) {
		return;
	}
	const min = options.autoBookmarkMinHeadingLevel ?? 1;
	const max = options.autoBookmarkMaxHeadingLevel ?? 6;
	if (min < 1 || min > 6) {
		throw new RangeError(
			`autoBookmarkMinHeadingLevel must be between 1 and 6 (inclusive). Got: ${min}`
		);
	}
	if (max < 1 || max > 6) {
		throw new RangeError(
			`autoBookmarkMaxHeadingLevel must be between 1 and 6 (inclusive). Got: ${max}`
		);
	}
	if (min > max) {
		throw new RangeError(
			`autoBookmarkMinHeadingLevel (${min}) must be less than or equal to autoBookmarkMaxHeadingLevel (${max}).`
		);
	}
}

export function chromePdfRenderOptionsToProto(
	renderOption?: ChromePdfRenderOptions | undefined
): ChromePdfRenderOptionsP {
	if (!renderOption) renderOption = defaultChromePdfRenderOptions();
	validateAutoBookmarkHeadingLevels(renderOption);

	const waitForDetail: WaitForDetail = convertWaitFor(renderOption.waitFor);

	const proto: ChromePdfRenderOptionsP = {
		createPdfFormsFromHtml: renderOption.createPdfFormsFromHtml ?? true,
		customCssUrl: renderOption.customCssUrl ?? "",
		enableJavaScript: renderOption.enableJavaScript ?? true,
		fitToPaperMode: fitToPaperModeToProto(renderOption.fitToPaperMode ?? FitToPaperModes.Default),
		grayScale: renderOption.grayScale ?? false,

		marginTop: (renderOption.margin?.top || renderOption.margin?.default) ?? 25,
		marginBottom: (renderOption.margin?.bottom || renderOption.margin?.default) ?? 25,
		marginLeft: (renderOption.margin?.left || renderOption.margin?.default) ?? 25,
		marginRight: (renderOption.margin?.right || renderOption.margin?.default) ?? 25,

		paperOrientation: PdfPaperOrientationToProto(
			renderOption.paperOrientation ?? PdfPaperOrientation.Portrait
		),

		printHtmlBackgrounds: renderOption.printHtmlBackgrounds ?? true,

		timeout: renderOption.timeout ?? 60,
		waitFor: {
			Type: waitForDetail.type,
			Timeout: waitForDetail.timeout,
			NetworkIdleDuration: waitForDetail.networkIdleDuration,
			NumAllowedInFlight: waitForDetail.numAllowedInFlight,
			RenderDelayDuration: waitForDetail.renderDelayDuration,
			HtmlElementQueryStr: waitForDetail.htmlElementQueryStr,
		},
		// WaitForNetworkIdleDuration: waitForDetail.networkIdleDuration,
		// WaitForType: waitForDetail.type,
		// WaitForNumAllowedInflight: waitForDetail.numAllowedInFlight,
		// WaitForTimeout: waitForDetail.timeout,

		title: renderOption.title ?? "",
		viewPortHeight: 1280,
		viewPortWidth: 1024,
                zoom: 100,
                javascript: renderOption.javascript ?? "",
                firstPageNumber: renderOption.firstPageNumber ?? 1,
                tableOfContents: tableOfContentsTypesToProto(
                        renderOption.tableOfContents ?? TableOfContentsTypes.None
                ),
        };

	if(!isNullOrUndefined(renderOption.cssMediaType)){
		proto.cssMediaType = CssMediaTypeToProto(renderOption.cssMediaType);
	}

	if(!isNullOrUndefined(renderOption.inputEncoding)){
		proto.inputEncoding = renderOption.inputEncoding;
	}

	if(!isNullOrUndefined(renderOption.useMarginsOnHeaderAndFooter)){
		proto.useMarginsOnHeaderFooter = useMarginsToProto(
			renderOption.useMarginsOnHeaderAndFooter
		)
	}

	if (!isNullOrUndefined(renderOption.htmlHeader)) {
		proto.htmlHeader = htmlAffixToProto(renderOption.htmlHeader);
	}

	if (!isNullOrUndefined(renderOption.htmlFooter)) {
		proto.htmlFooter = htmlAffixToProto(renderOption.htmlFooter);
	}

	if (!isNullOrUndefined(renderOption.textHeader)) {
		proto.textHeader = textAffixToProto(renderOption.textHeader);
	}

	if (!isNullOrUndefined(renderOption.textFooter)) {
		proto.textFooter = textAffixToProto(renderOption.textFooter);
	}

	if (!isNullOrUndefined(renderOption.paperSize)) {
		if (pdfPaperSizeIsCustom(renderOption.paperSize)) {
			const wh = customPaperSizeToMm(
				renderOption.paperSize as CustomPaperSize
			);
			proto.customPaperHeight = wh.height;
			proto.customPaperWidth = wh.width;
			proto.paperSize = {enumValue: 106} //custom
		}else{
			proto.paperSize = paperSizeToProto(
				renderOption.paperSize as PaperSize
			);
		}
	}

	// Auto-bookmark generation from HTML headings (optional proto fields).
	// Mirrors the post-review C# behavior: always set css-selector / element-query
	// fields (empty string when no selectors), and pass min/max heading levels through
	// verbatim (validated above). `autoBookmarksFromHeadings` itself is only emitted
	// when enabled so existing engine defaults remain untouched.
	if (renderOption.autoBookmarksFromHeadings === true) {
		proto.autoBookmarksFromHeadings = true;
		proto.autoBookmarkMinHeadingLevel = renderOption.autoBookmarkMinHeadingLevel ?? 1;
		proto.autoBookmarkMaxHeadingLevel = renderOption.autoBookmarkMaxHeadingLevel ?? 6;
	}
	proto.autoBookmarkCssSelectors = joinDictArray(renderOption.autoBookmarkCssSelectors) ?? "";
 
	// Element query selectors for engine-side element location tracking.
	proto.elementQuerySelectors = joinDictArray(renderOption.elementQuerySelectors) ?? "";

	return proto;
}

export function pdfPaperSizeIsCustom(pdfPaperSize: PdfPaperSize) {
	return pdfPaperSize instanceof Object &&
		"width" in pdfPaperSize &&
		"height" in pdfPaperSize &&
		"unit" in pdfPaperSize;
}

export function htmlAffixToProto(
	htmlAffix: HtmlAffix
): ChromeHtmlHeaderFooterP {
	return {
		htmlFragment: htmlAffix.htmlFragment ?? "",
		// baseUrl: htmlAffix.baseUrl, //not supported
		dividerLineColor: htmlAffix.dividerLineColor ?? "#B1B1B1",
		drawDividerLine: htmlAffix.dividerLine ?? false,
		loadStylesAndCSSFromMainHtmlDocument:
			htmlAffix.loadStylesAndCSSFromMainHtmlDocument ?? false,
		maxHeight: htmlAffix.maxHeight ?? 176.388,
	};
}

export function textAffixToProto(
	textAffix: TextAffix
): ChromeTextHeaderFooterP {
	return {
		leftText: textAffix?.leftText ?? "",
		rightText: textAffix?.rightText ?? "",
		centerText: textAffix?.centerText ?? "",
		font: fontToProto(textAffix.font) ?? null,
		fontSize: textAffix.fontSize ?? 12,
		dividerLineColor: textAffix.dividerLineColor ?? "#B1B1B1",
		drawDividerLine: textAffix.dividerLine ?? false,
	};
}

export function fontToProto(affixFonts?: AffixFonts): FontTypeP | undefined {
	if (!affixFonts) return {name: AffixFonts[AffixFonts.Arial]?.replace("_","-") ?? ""};
	return {name: AffixFonts[affixFonts]?.replace("_","-") ?? ""};
}

export function useMarginsToProto(
        useMargins: UseMargins
): ChromeUseMarginsP {
        return {enumValue: useMargins};
}

export function tableOfContentsTypesToProto(
        toc: TableOfContentsTypes
): ChromeTableOfContentsTypesP {
        return {enumValue: toc};
}

export function paperSizeToProto(
	paperSize: PaperSize
): ChromePdfPaperSizeP{
	return {enumValue: paperSize};
}

function toMillimeters(paperSize: PaperSize): WidthHeight {
	switch (paperSize) {
		case PaperSize.Letter:
			return {width: 216, height: 279};
		case PaperSize.Legal:
			return {width: 216, height: 356};
		case PaperSize.A4:
			return {width: 210, height: 297};
		case PaperSize.CSheet:
			return {width: 432, height: 559};
		case PaperSize.DSheet:
			return {width: 559, height: 864};
		case PaperSize.ESheet:
			return {width: 864, height: 1118};
		case PaperSize.LetterSmall:
			return {width: 216, height: 279};
		case PaperSize.Tabloid:
			return {width: 279, height: 432};
		case PaperSize.Ledger:
			return {width: 432, height: 279};
		case PaperSize.Statement:
			return {width: 140, height: 216};
		case PaperSize.Executive:
			return {width: 184, height: 267};
		case PaperSize.A3:
			return {width: 297, height: 420};
		case PaperSize.A4Small:
			return {width: 210, height: 297};
		case PaperSize.A5:
			return {width: 148, height: 210};
		case PaperSize.B4:
			return {width: 250, height: 353};
		case PaperSize.B5:
			return {width: 176, height: 250};
		case PaperSize.Folio:
			return {width: 216, height: 330};
		case PaperSize.Quarto:
			return {width: 215, height: 275};
		case PaperSize.Standard10x14:
			return {width: 254, height: 356};
		case PaperSize.Standard11x17:
			return {width: 279, height: 432};
		case PaperSize.Note:
			return {width: 216, height: 279};
		case PaperSize.Number9Envelope:
			return {width: 98, height: 225};
		case PaperSize.Number10Envelope:
			return {width: 105, height: 241};
		case PaperSize.Number11Envelope:
			return {width: 114, height: 264};
		case PaperSize.Number12Envelope:
			return {width: 121, height: 279};
		case PaperSize.Number14Envelope:
			return {width: 127, height: 292};
		case PaperSize.DLEnvelope:
			return {width: 110, height: 220};
		case PaperSize.C5Envelope:
			return {width: 162, height: 229};
		case PaperSize.C3Envelope:
			return {width: 324, height: 458};
		case PaperSize.C4Envelope:
			return {width: 229, height: 324};
		case PaperSize.C65Envelope:
			return {width: 114, height: 229};
		case PaperSize.B4Envelope:
			return {width: 250, height: 353};
		case PaperSize.B5Envelope:
			return {width: 176, height: 250};
		case PaperSize.B6Envelope:
			return {width: 176, height: 125};
		case PaperSize.ItalyEnvelope:
			return {width: 110, height: 230};
		case PaperSize.MonarchEnvelope:
			return {width: 98, height: 191};
		case PaperSize.PersonalEnvelope:
			return {width: 92, height: 165};
		case PaperSize.USStandardFanfold:
			return {width: 378, height: 279};
		case PaperSize.GermanStandardFanfold:
			return {width: 216, height: 305};
		case PaperSize.GermanLegalFanfold:
			return {width: 216, height: 330};
		case PaperSize.IsoB4:
			return {width: 250, height: 353};
		case PaperSize.JapanesePostcard:
			return {width: 100, height: 148};
		case PaperSize.Standard9x11:
			return {width: 229, height: 279};
		case PaperSize.Standard10x11:
			return {width: 254, height: 279};
		case PaperSize.Standard15x11:
			return {width: 381, height: 279};
		case PaperSize.InviteEnvelope:
			return {width: 220, height: 220};
		case PaperSize.LetterExtra:
			return {width: 236, height: 305};
		case PaperSize.LegalExtra:
			return {width: 236, height: 381};
		case PaperSize.TabloidExtra:
			return {width: 297, height: 457};
		case PaperSize.A4Extra:
			return {width: 236, height: 322};
		case PaperSize.LetterTransverse:
			return {width: 210, height: 279};
		case PaperSize.A4Transverse:
			return {width: 210, height: 297};
		case PaperSize.LetterExtraTransverse:
			return {width: 236, height: 305};
		case PaperSize.APlus:
			return {width: 227, height: 356};
		case PaperSize.BPlus:
			return {width: 305, height: 487};
		case PaperSize.LetterPlus:
			return {width: 216, height: 322};
		case PaperSize.A4Plus:
			return {width: 210, height: 330};
		case PaperSize.A5Transverse:
			return {width: 148, height: 210};
		case PaperSize.B5Transverse:
			return {width: 182, height: 257};
		case PaperSize.A3Extra:
			return {width: 322, height: 445};
		case PaperSize.A5Extra:
			return {width: 174, height: 235};
		case PaperSize.B5Extra:
			return {width: 201, height: 276};
		case PaperSize.A2:
			return {width: 420, height: 594};
		case PaperSize.A3Transverse:
			return {width: 297, height: 420};
		case PaperSize.A3ExtraTransverse:
			return {width: 322, height: 445};
		case PaperSize.JapaneseDoublePostcard:
			return {width: 200, height: 148};
		case PaperSize.A6:
			return {width: 105, height: 148};
		case PaperSize.LetterRotated:
			return {width: 279, height: 216};
		case PaperSize.A3Rotated:
			return {width: 420, height: 297};
		case PaperSize.A4Rotated:
			return {width: 297, height: 210};
		case PaperSize.A5Rotated:
			return {width: 210, height: 148};
		case PaperSize.B4JisRotated:
			return {width: 364, height: 257};
		case PaperSize.B5JisRotated:
			return {width: 257, height: 182};
		case PaperSize.JapanesePostcardRotated:
			return {width: 148, height: 100};
		case PaperSize.JapaneseDoublePostcardRotated:
			return {width: 148, height: 200};
		case PaperSize.A6Rotated:
			return {width: 148, height: 105};
		case PaperSize.B6Jis:
			return {width: 128, height: 182};
		case PaperSize.B6JisRotated:
			return {width: 182, height: 128};
		case PaperSize.Standard12x11:
			return {width: 305, height: 279};
		case PaperSize.Prc16K:
			return {width: 146, height: 215};
		case PaperSize.Prc32K:
			return {width: 97, height: 151};
		case PaperSize.Prc32KBig:
			return {width: 97, height: 151};
		case PaperSize.PrcEnvelopeNumber1:
			return {width: 102, height: 165};
		case PaperSize.PrcEnvelopeNumber2:
			return {width: 102, height: 176};
		case PaperSize.PrcEnvelopeNumber3:
			return {width: 125, height: 176};
		case PaperSize.PrcEnvelopeNumber4:
			return {width: 110, height: 208};
		case PaperSize.PrcEnvelopeNumber5:
			return {width: 110, height: 220};
		case PaperSize.PrcEnvelopeNumber6:
			return {width: 120, height: 230};
		case PaperSize.PrcEnvelopeNumber7:
			return {width: 160, height: 230};
		case PaperSize.PrcEnvelopeNumber8:
			return {width: 120, height: 309};
		case PaperSize.PrcEnvelopeNumber9:
			return {width: 229, height: 324};
		case PaperSize.PrcEnvelopeNumber10:
			return {width: 324, height: 458};
		case PaperSize.Prc16KRotated:
			return {width: 215, height: 146};
		case PaperSize.Prc32KRotated:
			return {width: 151, height: 97};
		case PaperSize.Prc32KBigRotated:
			return {width: 151, height: 97};
		case PaperSize.PrcEnvelopeNumber1Rotated:
			return {width: 165, height: 102};
		case PaperSize.PrcEnvelopeNumber2Rotated:
			return {width: 176, height: 102};
		case PaperSize.PrcEnvelopeNumber3Rotated:
			return {width: 176, height: 125};
		case PaperSize.PrcEnvelopeNumber4Rotated:
			return {width: 208, height: 110};
		case PaperSize.PrcEnvelopeNumber5Rotated:
			return {width: 220, height: 110};
		case PaperSize.PrcEnvelopeNumber6Rotated:
			return {width: 230, height: 120};
		case PaperSize.PrcEnvelopeNumber7Rotated:
			return {width: 230, height: 160};
		case PaperSize.PrcEnvelopeNumber8Rotated:
			return {width: 309, height: 120};
		case PaperSize.PrcEnvelopeNumber9Rotated:
			return {width: 324, height: 229};
		case PaperSize.PrcEnvelopeNumber10Rotated:
			return {width: 458, height: 324};
		default:
			throw new Error("Unsupported paper size");
	}
}

export function pdfPaperSizeToProto(
	pdfPaperSize?: PdfPaperSize | undefined
): { paperSizeP: ChromePdfPaperSizeP; widthHeight?: WidthHeight | undefined } | undefined {
	if (!pdfPaperSize) return undefined;

	if (typeof pdfPaperSize === "object" && "width" in pdfPaperSize) {
		return {
			paperSizeP: { enumValue: 106 },
			widthHeight: {
				width: pdfPaperSize.width,
				height: pdfPaperSize.height,
			},
		};
	} else {
		const wh = toMillimeters(pdfPaperSize);
		return {
			paperSizeP: {enumValue: pdfPaperSize},
			widthHeight: {width: wh.width, height: wh.height},
		};
	}
}

export function imageBehaviorToProto(
	imageBehavior?: ImageBehavior | undefined
): ChromeImageBehaviorP | null {
	if (!imageBehavior) return null;
	return {enumValue: imageBehavior};
}

export function customPaperSizeToMm(
	customPaperSize: CustomPaperSize
): WidthHeight {
	switch (customPaperSize.unit) {
		case PaperSizeUnit.Centimeter:
			return {
				width: customPaperSize.width * 10,
				height: customPaperSize.height * 10,
			};
		case PaperSizeUnit.Inch:
			return {
				width: customPaperSize.width * 25.4,
				height: customPaperSize.height * 25.4,
			};
		case PaperSizeUnit.Millimeter:
			return {
				width: customPaperSize.width,
				height: customPaperSize.height,
			};
		case PaperSizeUnit.Points:
			return {
				width: customPaperSize.width * 0.352777778,
				height: customPaperSize.height * 0.352777778,
			};
	}
}

export type WidthHeight = { width: number; height: number };

function BaseStampOptionsToProto(
	id: string,
	pagesInfo: PageInfo[],
	defaultRenderDelay: number,
	password?: PdfPassword | undefined,
	options?: BaseStampOptions | undefined,
	pageSelection?: PdfPageSelection | undefined
): _ironpdfengineproto_ChromeApplyStampRequestStreamP_InfoP {
	const pi =
		PdfPageSelectionToIndexes(pagesInfo, pageSelection) ??
		Array.from({
			length: pagesInfo.length,
		}).map((_, index) => index);
	const pages: PdfiumPageP[] = pi.map((pageIndex) => {
		// if (pagesInfo) {
		const pageP: PdfiumPageP = {
			pageIndex: pageIndex
		};

		const pageInfo = pagesInfo[pageIndex];
		if(!isNullOrUndefined(pageInfo)){
			pageP.width =  pageInfo.millimeterWidth;
			pageP.height = pageInfo.millimeterHeight;
			pageP.pageRotation = PageRotationToProto(
				pageInfo.pageRotation
			);
			pageP.printWidth = pageInfo.printerPointWidth;
			pageP.printHeight = pageInfo.printerPointHeight;
		}
		return pageP;
		// }
		// return { pageIndex: pageIndex };
	});

	return {
		document: {documentId: id},
		hyperlink: options?.hyperlink ?? "",
                horizontalAlignment: horizontalAlignmentToProto(
                        options?.horizontalAlignment ?? HorizontalAlignment.Center
                ),
                verticalAlignment: verticalAlignmentToProto(
                        options?.verticalAlignment ?? VerticalAlignment.Middle
                ),
		horizontalOffset: lengthToProto(options?.horizontalOffset),
		verticalOffset: lengthToProto(options?.verticalOffset),
		isStampBehindContent: options?.behindExistingContent ?? false,
		opacity: options?.opacity ?? 100,
		rotation: options?.rotation ?? 0,
		scale: options?.scale ?? 100,
		targetPages: pages ?? [],
		ownerPassword: password?.ownerPassword ?? "",
		userPassword: password?.userPassword ?? "",
		renderDelay: options?.renderDelay ?? defaultRenderDelay,
		timeout: options?.timeout ?? 60,
		maxHeight: lengthToProto(options?.maxHeight),
		minHeight: lengthToProto(options?.minHeight),
		maxWidth: lengthToProto(options?.maxWidth),
		minWidth: lengthToProto(options?.minWidth),
	};
}

export function HtmlStampOptionsToProto(
	id: string,
	pagesInfo: PageInfo[],
	password?: PdfPassword | undefined,
	options?: HtmlStampOptions | undefined,
	pageSelection?: PdfPageSelection | undefined
): _ironpdfengineproto_ChromeApplyStampRequestStreamP_InfoP {
	const base = BaseStampOptionsToProto(
		id,
		pagesInfo,
		100,
		password,
		options,
		pageSelection
	);
	base.htmlStamper = {
		//baseUrl: options?.baseUrl,  //not supported
		cssMediaType: cssMediaTypeToProto(options?.cssMediaType),
	};
	return base;
}

export function TextStampOptionsToProto(
	id: string,
	pagesInfo: PageInfo[],
	password?: PdfPassword | undefined,
	options?: TextStampOptions | undefined,
	pageSelection?: PdfPageSelection | undefined
): _ironpdfengineproto_ChromeApplyStampRequestStreamP_InfoP {
	const base = BaseStampOptionsToProto(
		id,
		pagesInfo,
		0,
		password,
		options,
		pageSelection
	);
	base.textStamper = {
		textColor: options?.textColor ?? "#000000",
		fontSize: options?.fontSize ?? 12,
		backgroundColor: options?.backgroundColor ?? "#00FFFFFF",
		fontFamily: options?.fontFamily ?? "Arial",
		isBold: options?.isBold ?? false,
		isItalic: options?.isItalic ?? false,
		isStrikethrough: options?.isStrikethrough ?? false,
		isUnderline: options?.isUnderline ?? false,
		useGoogleFont: options?.useGoogleFont ?? false,
	};
	return base;
}

export function ImageStampOptionsToProto(
	id: string,
	pagesInfo: PageInfo[],
	password?: PdfPassword | undefined,
	options?: ImageStampOptions | undefined,
	pageSelection?: PdfPageSelection | undefined
): _ironpdfengineproto_ChromeApplyStampRequestStreamP_InfoP {
	const base = BaseStampOptionsToProto(
		id,
		pagesInfo,
		0,
		password,
		options,
		pageSelection
	);
	base.imageStamper = {};
	return base;
}

export function BarcodeStampOptionsToProto(
	id: string,
	pagesInfo: PageInfo[],
	password?: PdfPassword | undefined,
	options?: BarcodeStampOptions | undefined,
	pageSelection?: PdfPageSelection | undefined
): _ironpdfengineproto_ChromeApplyStampRequestStreamP_InfoP {
	const base = BaseStampOptionsToProto(
		id,
		pagesInfo,
		0,
		password,
		options,
		pageSelection
	);
	base.barcodeStamper = {
		widthPx: options?.widthPx ?? 250,
		heightPx: options?.heightPx ?? 250,
		barcodeType: barcodeTypeToProto(
			options?.barcodeType ?? BarcodeType.qrCode
		),
	};
	return base;
}

export function lengthToProto(length?: Length): LengthP | null {
	if (!length) return null;
	return {
		value: length.value,
		unit: measurementUnitToProto(length.unit),
	};
}

export function measurementUnitToProto(
	measurementUnit: MeasurementUnit
): MeasurementUnitP {
	return {
		enumValue: measurementUnit,
	};
}

export function horizontalAlignmentToProto(
	horizontalAlignment?: HorizontalAlignment | undefined
): HorizontalAlignmentP | null {
	if (!horizontalAlignment) return null;
	return {
		enumValue: horizontalAlignment,
	};
}

export function verticalAlignmentToProto(
	verticalAlignment?: VerticalAlignment | undefined
): VerticalAlignmentP | null {
	if (!verticalAlignment) return null;
	return {
		enumValue: verticalAlignment,
	};
}

export function cssMediaTypeToProto(
	cssMediaType?: CssMediaType | undefined
): ChromePdfCssMediaTypeP | null {
	if (!cssMediaType) return null;
	return {
		enumValue: cssMediaType,
	};
}

export function barcodeTypeToProto(
	barcodeType?: BarcodeType | undefined
): ChromeBarcodeEncodingP | null {
	if (!barcodeType) return null;
	return {
		enumValue: barcodeType,
	};
}
