import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
import { EF_RENDERING } from "../EF_RENDERING.js";
import { EFTemporal } from "./EFTemporal.js";
import { TargetController } from "./TargetController.js";
import { TWMixin } from "../gui/TWMixin2.js";
import { CrossUpdateController } from "./CrossUpdateController.js";
import { Task } from "@lit/task";
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import _decorate from "@oxc-project/runtime/helpers/decorate";
import { createRef, ref } from "lit/directives/ref.js";
import { CSSStyleObserver } from "@bramus/style-observer";
let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
	constructor(..._args) {
		super(..._args);
		this.canvasRef = createRef();
		this.ctx = null;
		this.styleObserver = null;
		this.mode = "bars";
		this.color = "currentColor";
		this.target = "";
		this.barSpacing = .5;
		this.targetElement = null;
		this.lineWidth = 4;
		this.targetController = new TargetController(this);
		this.frameTask = new Task(this, {
			autoRun: EF_INTERACTIVE,
			args: () => {
				return [this.targetElement, this.targetElement?.frequencyDataTask.value];
			},
			task: async () => {
				if (!this.targetElement) return;
				await this.targetElement.frequencyDataTask.taskComplete;
				this.ctx ||= this.initCanvas();
				const ctx = this.ctx;
				if (!ctx) return;
				const frequencyData = this.targetElement.frequencyDataTask.value;
				const byteTimeData = this.targetElement.byteTimeDomainTask.value;
				if (!frequencyData || !byteTimeData) return;
				ctx.save();
				if (this.color === "currentColor") {
					const computedStyle = getComputedStyle(this);
					const currentColor = computedStyle.color;
					ctx.strokeStyle = currentColor;
					ctx.fillStyle = currentColor;
				} else {
					ctx.strokeStyle = this.color;
					ctx.fillStyle = this.color;
				}
				switch (this.mode) {
					case "bars":
						this.drawBars(ctx, frequencyData);
						break;
					case "bricks":
						this.drawBricks(ctx, frequencyData);
						break;
					case "line":
						this.drawLine(ctx, byteTimeData);
						break;
					case "curve":
						this.drawCurve(ctx, byteTimeData);
						break;
					case "pixel":
						this.drawPixel(ctx, frequencyData);
						break;
					case "wave":
						this.drawWave(ctx, frequencyData);
						break;
					case "spikes":
						this.drawSpikes(ctx, frequencyData);
						break;
					case "roundBars":
						this.drawRoundBars(ctx, frequencyData);
						break;
				}
				ctx.restore();
			}
		});
	}
	static {
		this.styles = css`
      :host {
        all: inherit;
        display: block;
        position: relative;
      }

      canvas {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
      }
    `;
	}
	render() {
		return html`<canvas ${ref(this.canvasRef)}></canvas><div class="text-5xl inline-block bg-red-500">`;
	}
	connectedCallback() {
		super.connectedCallback();
		try {
			if (this.targetElement) new CrossUpdateController(this.targetElement, this);
		} catch (_error) {}
		this.resizeObserver = new ResizeObserver(() => {
			this.resizeCanvas();
		});
		this.resizeObserver.observe(this);
		this.mutationObserver = new MutationObserver((mutationsList) => {
			for (const mutation of mutationsList) if (mutation.type === "attributes") this.frameTask.run();
		});
		this.mutationObserver.observe(this, { attributes: true });
		if (!EF_RENDERING()) {
			this.styleObserver = new CSSStyleObserver(["color"], () => {
				this.frameTask.run();
			});
			this.styleObserver.attach(this);
		}
	}
	disconnectedCallback() {
		super.disconnectedCallback();
		this.resizeObserver?.disconnect();
		this.mutationObserver?.disconnect();
		this.styleObserver?.detach();
	}
	resizeCanvas() {
		this.ctx = this.initCanvas();
		if (this.ctx) this.frameTask.run();
	}
	initCanvas() {
		const canvas = this.canvasRef.value;
		if (!canvas) return null;
		const rect = {
			width: this.offsetWidth,
			height: this.offsetHeight
		};
		const dpr = window.devicePixelRatio;
		canvas.style.width = `${rect.width}px`;
		canvas.style.height = `${rect.height}px`;
		canvas.width = rect.width * dpr;
		canvas.height = rect.height * dpr;
		const ctx = canvas.getContext("2d");
		if (!ctx) return null;
		ctx.reset();
		return ctx;
	}
	drawBars(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		const totalBars = frequencyData.length;
		const paddingInner = this.barSpacing;
		const paddingOuter = .01;
		const availableWidth = waveWidth;
		const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		frequencyData.forEach((value, i) => {
			const normalizedValue = value / 255;
			const barHeight = normalizedValue * waveHeight;
			const y = (waveHeight - barHeight) / 2;
			const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
			path.rect(x, y, barWidth, barHeight);
		});
		ctx.fill(path);
	}
	drawBricks(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		const columnWidth = waveWidth / frequencyData.length;
		const boxSize = columnWidth * .9;
		const verticalGap = boxSize * .2;
		const maxBricks = Math.floor(waveHeight / (boxSize + verticalGap));
		frequencyData.forEach((value, i) => {
			const normalizedValue = value / 255;
			const brickCount = Math.floor(normalizedValue * maxBricks);
			for (let j = 0; j < brickCount; j++) {
				const x = columnWidth * i;
				const y = waveHeight - (j + 1) * (boxSize + verticalGap);
				path.rect(x, y, boxSize, boxSize);
			}
		});
		ctx.fill(path);
	}
	drawRoundBars(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		const totalBars = frequencyData.length;
		const paddingInner = this.barSpacing;
		const paddingOuter = .01;
		const availableWidth = waveWidth;
		const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		frequencyData.forEach((value, i) => {
			const normalizedValue = value / 255;
			const height = normalizedValue * waveHeight;
			const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
			const y = (waveHeight - height) / 2;
			path.roundRect(x, y, barWidth, height, barWidth / 2);
		});
		ctx.fill(path);
	}
	drawLine(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		const sampleRate = 1;
		for (let i = 0; i < frequencyData.length; i += sampleRate) {
			const x = i / frequencyData.length * waveWidth;
			const y = (1 - (frequencyData[i] ?? 0) / 255) * waveHeight;
			if (i === 0) path.moveTo(x, y);
			else path.lineTo(x, y);
		}
		const lastX = waveWidth;
		const lastY = (1 - (frequencyData[frequencyData.length - 1] ?? 0) / 255) * waveHeight;
		path.lineTo(lastX, lastY);
		ctx.lineWidth = this.lineWidth;
		ctx.stroke(path);
	}
	drawCurve(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		frequencyData.forEach((value, i) => {
			const x = i / frequencyData.length * waveWidth;
			const y = (1 - value / 255) * waveHeight;
			if (i === 0) path.moveTo(x, y);
			else {
				const prevX = (i - 1) / frequencyData.length * waveWidth;
				const prevY = (1 - (frequencyData[i - 1] ?? 0) / 255) * waveHeight;
				const xc = (prevX + x) / 2;
				const yc = (prevY + y) / 2;
				path.quadraticCurveTo(prevX, prevY, xc, yc);
			}
		});
		ctx.lineWidth = this.lineWidth;
		ctx.stroke(path);
	}
	drawPixel(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		const baseline = waveHeight / 2;
		const barWidth = waveWidth / frequencyData.length;
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		frequencyData.forEach((value, i) => {
			const normalizedValue = value / 255;
			const x = i * (waveWidth / frequencyData.length);
			const barHeight = normalizedValue * (waveHeight / 2);
			const y = baseline - barHeight;
			path.rect(x, y, barWidth, barHeight * 2);
		});
		ctx.fill(path);
	}
	drawWave(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		const paddingOuter = .01;
		const availableWidth = waveWidth * (1 - 2 * paddingOuter);
		const startX = waveWidth * paddingOuter;
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		const firstValue = Math.min((frequencyData[0] ?? 0) / 255 * 2, 1);
		const firstY = (waveHeight - firstValue * waveHeight) / 2;
		path.moveTo(startX, firstY);
		frequencyData.forEach((value, i) => {
			const normalizedValue = Math.min(value / 255 * 2, 1);
			const x = startX + i / (frequencyData.length - 1) * availableWidth;
			const barHeight = normalizedValue * waveHeight;
			const y = (waveHeight - barHeight) / 2;
			if (i === 0) path.moveTo(x, y);
			else {
				const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
				const prevValue = Math.min((frequencyData[i - 1] ?? 0) / 255 * 2, 1);
				const prevBarHeight = prevValue * waveHeight;
				const prevY = (waveHeight - prevBarHeight) / 2;
				const xc = (prevX + x) / 2;
				const yc = (prevY + y) / 2;
				path.quadraticCurveTo(prevX, prevY, xc, yc);
			}
		});
		for (let i = frequencyData.length - 1; i >= 0; i--) {
			const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
			const x = startX + i / (frequencyData.length - 1) * availableWidth;
			const barHeight = normalizedValue * waveHeight;
			const y = (waveHeight + barHeight) / 2;
			if (i === frequencyData.length - 1) path.lineTo(x, y);
			else {
				const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
				const nextValue = Math.min((frequencyData[i + 1] ?? 0) / 255 * 2, 1);
				const nextBarHeight = nextValue * waveHeight;
				const nextY = (waveHeight + nextBarHeight) / 2;
				const xc = (nextX + x) / 2;
				const yc = (nextY + y) / 2;
				path.quadraticCurveTo(nextX, nextY, xc, yc);
			}
		}
		const lastY = (waveHeight + firstValue * waveHeight) / 2;
		const controlX = startX;
		const controlY = (lastY + firstY) / 2;
		path.quadraticCurveTo(controlX, controlY, startX, firstY);
		ctx.fill(path);
	}
	drawSpikes(ctx, frequencyData) {
		const canvas = ctx.canvas;
		const waveWidth = canvas.width;
		const waveHeight = canvas.height;
		const paddingOuter = .01;
		const availableWidth = waveWidth * (1 - 2 * paddingOuter);
		const startX = waveWidth * paddingOuter;
		ctx.clearRect(0, 0, waveWidth, waveHeight);
		const path = new Path2D();
		const firstValue = (frequencyData[0] ?? 0) / 255;
		const firstY = (waveHeight - firstValue * waveHeight) / 2;
		path.moveTo(startX, firstY);
		frequencyData.forEach((value, i) => {
			const normalizedValue = Math.min(value / 255 * 2, 1);
			const x = startX + i / (frequencyData.length - 1) * availableWidth;
			const barHeight = normalizedValue * (waveHeight / 2);
			const y = (waveHeight - barHeight * 2) / 2;
			if (i === 0) path.moveTo(x, y);
			else {
				const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
				const prevValue = (frequencyData[i - 1] ?? 0) / 255;
				const prevBarHeight = prevValue * (waveHeight / 2);
				const prevY = (waveHeight - prevBarHeight * 2) / 2;
				const xc = (prevX + x) / 2;
				const yc = (prevY + y) / 2;
				path.quadraticCurveTo(prevX, prevY, xc, yc);
			}
		});
		for (let i = frequencyData.length - 1; i >= 0; i--) {
			const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
			const x = startX + i / (frequencyData.length - 1) * availableWidth;
			const barHeight = normalizedValue * (waveHeight / 2);
			const y = (waveHeight + barHeight * 2) / 2;
			if (i === frequencyData.length - 1) path.lineTo(x, y);
			else {
				const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
				const nextValue = (frequencyData[i + 1] ?? 0) / 255;
				const nextBarHeight = nextValue * (waveHeight / 2);
				const nextY = (waveHeight + nextBarHeight * 2) / 2;
				const xc = (nextX + x) / 2;
				const yc = (nextY + y) / 2;
				path.quadraticCurveTo(nextX, nextY, xc, yc);
			}
		}
		const lastY = (waveHeight + firstValue * waveHeight) / 2;
		const controlX = startX;
		const controlY = (lastY + firstY) / 2;
		path.quadraticCurveTo(controlX, controlY, startX, firstY);
		ctx.fill(path);
	}
	get durationMs() {
		if (!this.targetElement) return 0;
		return this.targetElement.durationMs;
	}
	updated(changedProperties) {
		super.updated(changedProperties);
		if (changedProperties.size > 0) this.frameTask.run();
	}
};
_decorate([property({
	type: String,
	attribute: "mode"
})], EFWaveform.prototype, "mode", void 0);
_decorate([property({ type: String })], EFWaveform.prototype, "color", void 0);
_decorate([property({
	type: String,
	reflect: true
})], EFWaveform.prototype, "target", void 0);
_decorate([property({
	type: Number,
	attribute: "bar-spacing"
})], EFWaveform.prototype, "barSpacing", void 0);
_decorate([state()], EFWaveform.prototype, "targetElement", void 0);
_decorate([property({
	type: Number,
	attribute: "line-width"
})], EFWaveform.prototype, "lineWidth", void 0);
EFWaveform = _decorate([customElement("ef-waveform")], EFWaveform);
export { EFWaveform };
