import { __decorate } from "../../../_virtual/_@oxc-project_runtime@0.95.0/helpers/decorate.js";
import { EFAudio } from "../../../elements/EFAudio.js";
import { TrackItem } from "./TrackItem.js";
import { extractWaveformData } from "./waveformUtils.js";
import { timelineStateContext } from "../timelineStateContext.js";
import { consume } from "@lit/context";
import { css, html, nothing } from "lit";
import { customElement, state } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js";

//#region src/gui/timeline/tracks/AudioTrack.ts
/** Padding in pixels to render beyond visible area (for smooth scrolling) */
const VIRTUAL_RENDER_PADDING_PX = 100;
let EFAudioTrack = class EFAudioTrack$1 extends TrackItem {
	constructor(..._args) {
		super(..._args);
		this.canvasRef = createRef();
		this._waveformData = null;
		this._isLoading = false;
	}
	static {
		this.styles = [...TrackItem.styles, css`
      .waveform-host {
        position: absolute;
        left: 0;
        top: 2px;
        right: 0;
        bottom: 2px;
        overflow: hidden;
      }
      .waveform-canvas {
        display: block;
        position: absolute;
        top: 0;
        height: 100%;
        pointer-events: none;
      }
      .shimmer-placeholder {
        position: absolute;
        left: 0;
        top: 2px;
        bottom: 2px;
        right: 0;
        background: linear-gradient(
          90deg,
          color-mix(in srgb, var(--ef-color-type-audio, #10b981) 20%, transparent) 0%,
          color-mix(in srgb, var(--ef-color-type-audio, #10b981) 42%, transparent) 50%,
          color-mix(in srgb, var(--ef-color-type-audio, #10b981) 20%, transparent) 100%
        );
        background-size: 200% 100%;
        border-radius: 2px;
      }
      .shimmer-placeholder.is-loading {
        animation: shimmer var(--ef-loading-shimmer-duration, 1.5s) linear infinite;
      }
      @keyframes shimmer {
        0% { background-position: 200% 0; }
        100% { background-position: -200% 0; }
      }
    `];
	}
	#lastSrc = null;
	#abortController = null;
	#resizeObserver;
	#renderRequested = false;
	#hostHeight = 0;
	/**
	* Load waveform data when the audio source changes
	*/
	async #loadWaveformData() {
		const audio = this.element;
		const src = audio?.src;
		if (!src || src === this.#lastSrc) return;
		this.#lastSrc = src;
		this.#abortController?.abort();
		this.#abortController = new AbortController();
		this._isLoading = true;
		try {
			const waveformData = await extractWaveformData(audio, this.#abortController.signal);
			if (waveformData) {
				this._waveformData = waveformData;
				this.#scheduleRender();
			}
		} catch (error) {
			if (!(error instanceof DOMException && error.name === "AbortError")) console.warn("Failed to load waveform data:", error);
		} finally {
			this._isLoading = false;
		}
	}
	/**
	* Schedule a canvas render on the next animation frame
	*/
	#scheduleRender() {
		if (this.#renderRequested) return;
		this.#renderRequested = true;
		requestAnimationFrame(() => {
			this.#renderRequested = false;
			this.#renderWaveform();
		});
	}
	/**
	* Get the track's position info relative to timeline scroll
	*/
	#getTrackPositionInfo() {
		const audio = this.element;
		const durationMs = audio.durationMs ?? 0;
		if (durationMs === 0) return null;
		const pixelsPerMs = this._timelineState?.pixelsPerMs ?? this.pixelsPerMs;
		const trackWidthPx = durationMs * pixelsPerMs;
		return {
			trackStartPx: (audio.startTimeMs ?? 0) * pixelsPerMs,
			trackWidthPx,
			viewportScrollLeft: this._timelineState?.viewportScrollLeft ?? 0,
			viewportWidth: this._timelineState?.viewportWidth ?? 800,
			pixelsPerMs
		};
	}
	/**
	* Render the waveform to canvas with virtual rendering.
	*
	* The approach:
	* 1. Calculate the visible portion of the track (intersection of track and viewport)
	* 2. Position the canvas at that visible portion within the track
	* 3. Draw only the waveform data for that visible time range
	* 4. Update position and content as scroll/zoom changes
	*/
	#renderWaveform() {
		const canvas = this.canvasRef.value;
		const waveformData = this._waveformData;
		if (!canvas || !waveformData) return;
		const positionInfo = this.#getTrackPositionInfo();
		if (!positionInfo) return;
		const { trackStartPx, trackWidthPx, viewportScrollLeft, viewportWidth, pixelsPerMs } = positionInfo;
		const visibleLeftPx = viewportScrollLeft - VIRTUAL_RENDER_PADDING_PX;
		const visibleRightPx = viewportScrollLeft + viewportWidth + VIRTUAL_RENDER_PADDING_PX;
		if (trackStartPx + trackWidthPx < visibleLeftPx || trackStartPx > visibleRightPx) {
			canvas.style.display = "none";
			return;
		}
		canvas.style.display = "block";
		const visibleStartInTrack = Math.max(0, visibleLeftPx - trackStartPx);
		const visibleEndInTrack = Math.min(trackWidthPx, visibleRightPx - trackStartPx);
		const visibleWidthPx = visibleEndInTrack - visibleStartInTrack;
		if (visibleWidthPx <= 0) return;
		const height = this.#hostHeight || 18;
		const dpr = window.devicePixelRatio || 1;
		const targetWidth = Math.ceil(visibleWidthPx * dpr);
		const targetHeight = Math.ceil(height * dpr);
		if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
			canvas.width = targetWidth;
			canvas.height = targetHeight;
		}
		canvas.style.left = `${visibleStartInTrack}px`;
		canvas.style.width = `${visibleWidthPx}px`;
		canvas.style.height = `${height}px`;
		const ctx = canvas.getContext("2d");
		if (!ctx) return;
		ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
		ctx.clearRect(0, 0, visibleWidthPx, height);
		const sourceInMs = this.element.sourceStartMs ?? 0;
		const timeStartMs = sourceInMs + visibleStartInTrack / pixelsPerMs;
		const timeEndMs = sourceInMs + visibleEndInTrack / pixelsPerMs;
		this.#drawWaveformRegion(ctx, waveformData, 0, visibleWidthPx, height, timeStartMs, timeEndMs);
	}
	/**
	* Draw a region of the waveform to canvas
	*/
	#drawWaveformRegion(ctx, waveformData, x, width, height, startMs, endMs) {
		const { peaks, samplesPerSecond } = waveformData;
		const startSample = Math.floor(startMs / 1e3 * samplesPerSecond);
		const sampleCount = Math.ceil(endMs / 1e3 * samplesPerSecond) - startSample;
		if (sampleCount <= 0 || width <= 0) return;
		const centerY = height / 2;
		const halfHeight = height / 2 - 2;
		const color = this.getElementTypeColor();
		ctx.fillStyle = color;
		ctx.globalAlpha = .8;
		ctx.beginPath();
		const pixelsPerSample = width / sampleCount;
		for (let i = 0; i <= sampleCount; i++) {
			const peakIndex = (startSample + i) * 2;
			if (peakIndex + 1 >= peaks.length) break;
			const maxValue = peaks[peakIndex + 1] ?? 0;
			const px = x + i * pixelsPerSample;
			const py = centerY - maxValue * halfHeight;
			if (i === 0) ctx.moveTo(px, py);
			else ctx.lineTo(px, py);
		}
		for (let i = sampleCount; i >= 0; i--) {
			const peakIndex = (startSample + i) * 2;
			if (peakIndex >= peaks.length) continue;
			const minValue = peaks[peakIndex] ?? 0;
			const px = x + i * pixelsPerSample;
			const py = centerY - minValue * halfHeight;
			ctx.lineTo(px, py);
		}
		ctx.closePath();
		ctx.fill();
		ctx.globalAlpha = .3;
		ctx.strokeStyle = color;
		ctx.lineWidth = 1;
		ctx.beginPath();
		ctx.moveTo(x, centerY);
		ctx.lineTo(x + width, centerY);
		ctx.stroke();
		ctx.globalAlpha = 1;
	}
	connectedCallback() {
		super.connectedCallback();
		this.#loadWaveformData();
		this.#resizeObserver = new ResizeObserver((entries) => {
			for (const entry of entries) {
				this.#hostHeight = entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height;
				this.#scheduleRender();
			}
		});
	}
	disconnectedCallback() {
		super.disconnectedCallback();
		this.#abortController?.abort();
		this.#resizeObserver?.disconnect();
	}
	updated(changedProperties) {
		super.updated(changedProperties);
		if (this.element?.src !== this.#lastSrc) this.#loadWaveformData();
		if (changedProperties.has("_timelineState")) this.#scheduleRender();
		if (this.canvasRef.value && this.#resizeObserver) {
			const container = this.canvasRef.value.parentElement;
			if (container) {
				this.#resizeObserver.disconnect();
				this.#resizeObserver.observe(container);
			}
		}
		this.#scheduleRender();
	}
	contents() {
		const audio = this.element;
		if (!(audio instanceof EFAudio)) return nothing;
		if ((audio.durationMs ?? 0) === 0) return nothing;
		if (!this._waveformData) return this.#renderPlaceholder();
		return html`
      <div class="waveform-host">
        <canvas ${ref(this.canvasRef)} class="waveform-canvas"></canvas>
      </div>
    `;
	}
	#renderPlaceholder() {
		return html`<div
      class="shimmer-placeholder ${this._isLoading ? "is-loading" : ""}"
    ></div>`;
	}
};
__decorate([consume({
	context: timelineStateContext,
	subscribe: true
}), state()], EFAudioTrack.prototype, "_timelineState", void 0);
__decorate([state()], EFAudioTrack.prototype, "_waveformData", void 0);
__decorate([state()], EFAudioTrack.prototype, "_isLoading", void 0);
EFAudioTrack = __decorate([customElement("ef-audio-track")], EFAudioTrack);

//#endregion
//# sourceMappingURL=AudioTrack.js.map