import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
import { LRUCache } from "../../../utils/LRUCache.js";
import { Task } from "@lit/task";
const DECAY_WEIGHT = .8;
function makeAudioTimeDomainAnalysisTask(element) {
	const cache = new LRUCache(1e3);
	return new Task(element, {
		autoRun: EF_INTERACTIVE,
		onError: (error) => {
			console.error("byteTimeDomainTask error", error);
		},
		args: () => [
			element.audioBufferTask.status,
			element.currentSourceTimeMs,
			element.fftSize,
			element.fftDecay,
			element.fftGain,
			element.shouldInterpolateFrequencies
		],
		task: async (_, { signal }) => {
			await element.audioBufferTask.taskComplete;
			signal.throwIfAborted();
			if (!element.audioBufferTask.value) return null;
			if (element.currentSourceTimeMs < 0) return null;
			const currentTimeMs = element.currentSourceTimeMs;
			const frameIntervalMs = 1e3 / 30;
			const earliestFrameMs = currentTimeMs - (element.fftDecay - 1) * frameIntervalMs;
			const fromMs = Math.max(0, earliestFrameMs);
			const maxToMs = currentTimeMs + frameIntervalMs;
			const videoDurationMs = element.intrinsicDurationMs || 0;
			const toMs = videoDurationMs > 0 ? Math.min(maxToMs, videoDurationMs) : maxToMs;
			if (fromMs >= toMs) return null;
			const preliminaryCacheKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftDecay}:${element.fftGain}:${fromMs}:${currentTimeMs}`;
			const cachedData = cache.get(preliminaryCacheKey);
			if (cachedData) return cachedData;
			const { fetchAudioSpanningTime: fetchAudioSpan } = await import("../shared/AudioSpanUtils.js");
			const audioSpan = await fetchAudioSpan(element, fromMs, toMs, signal);
			if (!audioSpan || !audioSpan.blob) {
				console.warn("Time domain analysis skipped: no audio data available");
				return null;
			}
			const tempAudioContext = new OfflineAudioContext(2, 48e3, 48e3);
			const arrayBuffer = await audioSpan.blob.arrayBuffer();
			const audioBuffer = await tempAudioContext.decodeAudioData(arrayBuffer);
			const startOffsetMs = audioSpan.startMs;
			const framesData = await Promise.all(Array.from({ length: element.fftDecay }, async (_$1, frameIndex) => {
				const frameOffset = frameIndex * (1e3 / 30);
				const startTime = Math.max(0, (currentTimeMs - frameOffset - startOffsetMs) / 1e3);
				const cacheKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftGain}:${startOffsetMs}:${startTime}`;
				const cachedFrame = cache.get(cacheKey);
				if (cachedFrame) return cachedFrame;
				let audioContext;
				try {
					audioContext = new OfflineAudioContext(2, 48e3 * (1 / 30), 48e3);
				} catch (error) {
					throw new Error(`[EFMedia.byteTimeDomainTask] Failed to create OfflineAudioContext(2, ${48e3 * (1 / 30)}, 48000) for frame ${frameIndex} at time ${startTime}s: ${error instanceof Error ? error.message : String(error)}. This is for audio time domain analysis.`);
				}
				const source = audioContext.createBufferSource();
				source.buffer = audioBuffer;
				const analyser = audioContext.createAnalyser();
				analyser.fftSize = element.fftSize;
				analyser.minDecibels = -90;
				analyser.maxDecibels = -20;
				const gainNode = audioContext.createGain();
				gainNode.gain.value = element.fftGain;
				source.connect(gainNode);
				gainNode.connect(analyser);
				analyser.connect(audioContext.destination);
				source.start(0, startTime, 1 / 30);
				const dataLength = analyser.fftSize / 2;
				try {
					await audioContext.startRendering();
					const frameData = new Uint8Array(dataLength);
					analyser.getByteTimeDomainData(frameData);
					const points = new Uint8Array(dataLength);
					for (let i = 0; i < dataLength; i++) {
						const pointSamples = frameData.slice(i * (frameData.length / dataLength), (i + 1) * (frameData.length / dataLength));
						const rms = Math.sqrt(pointSamples.reduce((sum, sample) => {
							const normalized = (sample - 128) / 128;
							return sum + normalized * normalized;
						}, 0) / pointSamples.length);
						const avgSign = Math.sign(pointSamples.reduce((sum, sample) => sum + (sample - 128), 0));
						points[i] = Math.min(255, Math.round(128 + avgSign * rms * 128));
					}
					cache.set(cacheKey, points);
					return points;
				} finally {
					source.disconnect();
					analyser.disconnect();
				}
			}));
			const frameLength = framesData[0]?.length ?? 0;
			const smoothedData = new Uint8Array(frameLength);
			for (let i = 0; i < frameLength; i++) {
				let weightedSum = 0;
				let weightSum = 0;
				framesData.forEach((frame, frameIndex) => {
					const decayWeight = DECAY_WEIGHT ** frameIndex;
					weightedSum += (frame[i] ?? 0) * decayWeight;
					weightSum += decayWeight;
				});
				smoothedData[i] = Math.min(255, Math.round(weightedSum / weightSum));
			}
			cache.set(preliminaryCacheKey, smoothedData);
			return smoothedData;
		}
	});
}
export { makeAudioTimeDomainAnalysisTask };
