import { shallowGetTimegroups } from "./elements/EFTimegroup.js";
var TriggerCanvas = class {
	constructor() {
		this.canvasInitialized = false;
		this.canvas = document.createElement("canvas");
		const ctx = this.canvas.getContext("2d", { willReadFrequently: true });
		if (!ctx) throw new Error("Canvas 2d context not ready");
		this.ctx = ctx;
		this.ctx.fillStyle = "transparent";
	}
	initialize() {
		if (this.canvasInitialized) return;
		this.canvasInitialized = true;
		this.canvas.width = 1;
		this.canvas.height = 1;
		Object.assign(this.canvas.style, {
			position: "fixed",
			top: "0px",
			left: "0px",
			width: "100%",
			height: "100%",
			zIndex: "100000"
		});
		document.body.appendChild(this.canvas);
	}
	trigger() {
		this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
	}
};
var EFFramegen = class {
	trace(...args) {
		console.trace("[EF_FRAMEGEN]", ...args);
	}
	async syncLog(...args) {
		if (!this.BRIDGE) {
			console.log("[EF_FRAMEGEN]", ...args);
			return;
		}
		const sequence = ++this.logSequence;
		const message = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
		return new Promise((resolve) => {
			this.BRIDGE.syncLog(sequence, message, () => {
				resolve();
			});
		});
	}
	initializeVerificationCanvas() {
		if (this.verificationCanvas) return;
		this.verificationCanvas = document.createElement("canvas");
		const ctx = this.verificationCanvas.getContext("2d");
		if (!ctx) throw new Error("Verification canvas 2d context not ready");
		this.verificationCtx = ctx;
		const workbench = document.querySelector("ef-workbench");
		if (workbench) {
			this.verificationCanvas.width = workbench.clientWidth;
			this.verificationCanvas.height = 1;
			Object.assign(this.verificationCanvas.style, {
				position: "fixed",
				left: "0px",
				bottom: "0px",
				width: `${workbench.clientWidth}px`,
				height: "1px",
				zIndex: "99999"
			});
			document.body.appendChild(this.verificationCanvas);
		}
	}
	drawVerificationStrip(frameNumber) {
		this.initializeVerificationCanvas();
		if (!this.verificationCanvas || !this.verificationCtx) return;
		const width = this.verificationCanvas.width;
		const height = this.verificationCanvas.height;
		this.verificationCtx.clearRect(0, 0, width, height);
		const red = Math.floor(frameNumber / (256 * 256)) % 256;
		const green = Math.floor(frameNumber / 256) % 256;
		const blue = frameNumber % 256;
		this.verificationCtx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
		this.verificationCtx.fillRect(0, 0, width, height);
	}
	constructor() {
		this.time = 0;
		this.frameDurationMs = 0;
		this.frameBox = document.createElement("div");
		this.triggerCanvas = new TriggerCanvas();
		this.logSequence = 0;
		this.frameTasksInProgress = false;
		this.currentFrameNumber = 0;
		this.BRIDGE = window.FRAMEGEN_BRIDGE;
		if (this.BRIDGE) this.connectToBridge();
	}
	connectToBridge() {
		const BRIDGE = this.BRIDGE;
		if (!BRIDGE) throw new Error("No BRIDGE when attempting to connect to bridge");
		BRIDGE.onInitialize(async (renderOptions) => {
			await this.initialize(renderOptions);
			BRIDGE.initialized();
		});
		BRIDGE.onBeginFrame((frameNumber, isLast) => {
			this.beginFrame(frameNumber, isLast);
		});
		BRIDGE.onTriggerCanvas(() => {
			this.triggerCanvas.trigger();
		});
	}
	get showFrameBox() {
		return this.renderOptions?.showFrameBox ?? false;
	}
	async initialize(renderOptions) {
		this.renderOptions = renderOptions;
		const alignedFromMs = renderOptions.encoderOptions.alignedFromUs / 1e3;
		const alignedToMs = renderOptions.encoderOptions.alignedToUs / 1e3;
		const alignedDurationMs = alignedToMs - alignedFromMs;
		await this.syncLog("[EF_FRAMEGEN.initialize] Aligned boundary parameters:", {
			alignedFromUs: renderOptions.encoderOptions.alignedFromUs,
			alignedToUs: renderOptions.encoderOptions.alignedToUs,
			alignedFromMs: alignedFromMs.toFixed(3),
			alignedToMs: alignedToMs.toFixed(3),
			alignedDurationMs: alignedDurationMs.toFixed(3),
			sequenceNumber: renderOptions.encoderOptions.sequenceNumber,
			originalFromMs: renderOptions.encoderOptions.fromMs,
			originalToMs: renderOptions.encoderOptions.toMs,
			originalDurationMs: (renderOptions.encoderOptions.toMs - renderOptions.encoderOptions.fromMs).toFixed(3)
		});
		const workbench = document.querySelector("ef-workbench");
		if (!workbench) throw new Error("No workbench found");
		workbench.rendering = true;
		workbench.playing = false;
		const timegroups = shallowGetTimegroups(workbench);
		const firstGroup = timegroups[0];
		if (!firstGroup) throw new Error("No temporal elements found");
		const startingTimeMs = renderOptions.encoderOptions.fromMs;
		await firstGroup.waitForMediaDurations();
		await firstGroup.waitForFrameTasks();
		this.frameDurationMs = 1e3 / renderOptions.encoderOptions.video.framerate;
		this.time = startingTimeMs;
		if (this.showFrameBox) {
			Object.assign(this.frameBox.style, {
				width: "200px",
				height: "100px",
				font: "10px Arial",
				backgroundColor: "white",
				position: "absolute",
				top: "0px",
				right: "0px",
				zIndex: "100000"
			});
			document.body.prepend(this.frameBox);
		}
		this.triggerCanvas.initialize();
		await this.syncLog("[EF_FRAMEGEN.initialize] About to call renderAudio with:", {
			fromMs: alignedFromMs.toFixed(3),
			toMs: alignedToMs.toFixed(3),
			durationMs: alignedDurationMs.toFixed(3)
		});
		this.audioBufferPromise = firstGroup.renderAudio(renderOptions.encoderOptions.alignedFromUs / 1e3, renderOptions.encoderOptions.alignedToUs / 1e3);
	}
	async beginFrame(frameNumber, isLast) {
		if (this.renderOptions === void 0) throw new Error("No renderOptions");
		const workbench = document.querySelector("ef-workbench");
		if (!workbench) throw new Error("No workbench found");
		workbench.rendering = true;
		const timegroups = shallowGetTimegroups(workbench);
		const firstGroup = timegroups[0];
		if (!firstGroup) throw new Error("No temporal elements found");
		const frameTime = this.renderOptions.encoderOptions.fromMs + frameNumber * this.frameDurationMs;
		firstGroup.currentTimeMs = Number(Number(frameTime).toFixed(5));
		await firstGroup.waitForFrameTasks();
		if (this.showFrameBox) this.frameBox.innerHTML = `
        <div>🖼️   Frame: ${frameNumber}</div>
        <div>🕛 Segment: ${this.time.toFixed(4)}</div>
        <div>🕛   Frame: ${firstGroup.currentTimeMs.toFixed(4)}</div>
        <div>  from-to: ${this.renderOptions.encoderOptions.fromMs.toFixed(4)} - ${this.renderOptions.encoderOptions.toMs.toFixed(4)}</div>
      `;
		this.drawVerificationStrip(frameNumber);
		if (isLast && this.audioBufferPromise) {
			const renderedAudio = await this.audioBufferPromise;
			const channelCount = renderedAudio.numberOfChannels;
			const interleavedSamples = new Float32Array(channelCount * renderedAudio.length);
			for (let i = 0; i < renderedAudio.length; i++) for (let j = 0; j < channelCount; j++) interleavedSamples.set(renderedAudio.getChannelData(j).slice(i, i + 1), i * channelCount + j);
			if (this.BRIDGE) this.BRIDGE.frameReady(frameNumber, interleavedSamples.buffer);
			else {
				const fileReader = new FileReader();
				fileReader.readAsDataURL(new Blob([interleavedSamples.buffer]));
				await new Promise((resolve, reject) => {
					fileReader.onload = resolve;
					fileReader.onerror = reject;
				});
				return fileReader.result;
			}
		} else if (this.BRIDGE) this.BRIDGE.frameReady(frameNumber, /* @__PURE__ */ new ArrayBuffer(0));
		else {
			const fileReader = new FileReader();
			fileReader.readAsDataURL(new Blob([]));
			await new Promise((resolve, reject) => {
				fileReader.onload = resolve;
				fileReader.onerror = reject;
			});
			return fileReader.result;
		}
	}
};
if (typeof window !== "undefined") window.EF_FRAMEGEN = new EFFramegen();
