import { focusContext } from "./focusContext.js";
import { focusedElementContext } from "./focusedElementContext.js";
import { loopContext, playingContext } from "./playingContext.js";
import { targetTimegroupContext } from "./ContextMixin.js";
import { TimegroupController } from "../elements/TimegroupController.js";
import { EFTimegroup } from "../elements/EFTimegroup.js";
import { EFImage } from "../elements/EFImage.js";
import { TWMixin } from "./TWMixin2.js";
import { EFAudio } from "../elements/EFAudio.js";
import { EFVideo } from "../elements/EFVideo.js";
import { EFCaptions, EFCaptionsActiveWord } from "../elements/EFCaptions.js";
import { EFWaveform } from "../elements/EFWaveform.js";
import { msToTimeCode } from "../msToTimeCode.js";
import { consume } from "@lit/context";
import { LitElement, css, html, nothing } from "lit";
import { customElement, eventOptions, property, state } from "lit/decorators.js";
import _decorate from "@oxc-project/runtime/helpers/decorate";
import { createRef, ref } from "lit/directives/ref.js";
import { styleMap } from "lit/directives/style-map.js";
var ElementFilmstripController = class {
	constructor(host, filmstrip) {
		this.host = host;
		this.filmstrip = filmstrip;
		this.host.addController(this);
	}
	remove() {
		this.host.removeController(this);
	}
	hostDisconnected() {
		this.host.removeController(this);
	}
	hostUpdated() {
		this.filmstrip.requestUpdate();
	}
};
const CommonEffectKeys = new Set([
	"offset",
	"easing",
	"composite",
	"computedOffset"
]);
var FilmstripItem = class extends TWMixin(LitElement) {
	constructor(..._args) {
		super(..._args);
		this.element = new EFTimegroup();
		this.pixelsPerMs = .04;
	}
	static {
		this.styles = [css`
      :host {
        display: block;
      }
    `];
	}
	get isFocused() {
		return this.element && this.focusContext?.focusedElement === this.element;
	}
	get gutterStyles() {
		return {
			position: "relative",
			left: `${this.pixelsPerMs * (this.element.startTimeWithinParentMs - this.element.sourceStartMs)}px`,
			width: `${this.pixelsPerMs * (this.element.intrinsicDurationMs ?? this.element.durationMs)}px`
		};
	}
	get trimPortionStyles() {
		return {
			width: `${this.pixelsPerMs * this.element.durationMs}px`,
			left: `${this.pixelsPerMs * this.element.sourceStartMs}px`
		};
	}
	render() {
		return html`<div style=${styleMap(this.gutterStyles)}>
      <div
        class="bg-slate-300"
        ?data-focused=${this.isFocused}
        @mouseenter=${() => {
			if (this.focusContext) this.focusContext.focusedElement = this.element;
		}}
        @mouseleave=${() => {
			if (this.focusContext) this.focusContext.focusedElement = null;
		}}
      >
        <div
          ?data-focused=${this.isFocused}
          class="border-outset relative mb-[1px] block h-[1.1rem] text-nowrap border border-slate-500 bg-blue-200 text-sm data-[focused]:bg-slate-400"
          style=${styleMap(this.trimPortionStyles)}
        >
          ${this.animations()}
        </div>
      </div>
      ${this.renderChildren()}
    </div>`;
	}
	renderChildren() {
		return renderFilmstripChildren(Array.from(this.element.children), this.pixelsPerMs);
	}
	contents() {
		return html``;
	}
	animations() {
		const animations = this.element.getAnimations();
		return animations.map((animation) => {
			const effect = animation.effect;
			if (!(effect instanceof KeyframeEffect)) return nothing;
			const start = effect.getTiming().delay ?? 0;
			const duration = effect.getTiming().duration;
			if (duration === null) return nothing;
			const keyframes = effect.getKeyframes();
			const firstKeyframe = keyframes[0];
			if (!firstKeyframe) return nothing;
			const properties = new Set(Object.keys(firstKeyframe));
			for (const key of CommonEffectKeys) properties.delete(key);
			return html`<div
        class="relative h-[5px] bg-blue-500 opacity-50"
        label="animation"
        style=${styleMap({
				left: `${this.pixelsPerMs * start}px`,
				width: `${this.pixelsPerMs * Number(duration)}px`
			})}
      >
        <!-- <div class="text-nowrap">${Array.from(properties).join(" ")}</div> -->
        ${effect.getKeyframes().map((keyframe) => {
				return html`<div
            class="absolute top-0 h-full w-1 bg-red-500"
            style=${styleMap({ left: `${this.pixelsPerMs * keyframe.computedOffset * Number(duration)}px` })}
          ></div>`;
			})}
      </div>`;
		});
	}
	update(changedProperties) {
		if (changedProperties.has("element") && this.element instanceof LitElement) {
			this.filmstripController?.remove();
			this.filmstripController = new ElementFilmstripController(this.element, this);
		}
		super.update(changedProperties);
	}
};
_decorate([consume({
	context: focusContext,
	subscribe: true
})], FilmstripItem.prototype, "focusContext", void 0);
_decorate([consume({
	context: focusedElementContext,
	subscribe: true
})], FilmstripItem.prototype, "focusedElement", void 0);
_decorate([property({
	type: Object,
	attribute: false
})], FilmstripItem.prototype, "element", void 0);
_decorate([property({ type: Number })], FilmstripItem.prototype, "pixelsPerMs", void 0);
let EFAudioFilmstrip = class EFAudioFilmstrip$1 extends FilmstripItem {
	contents() {
		return html``;
	}
};
EFAudioFilmstrip = _decorate([customElement("ef-audio-filmstrip")], EFAudioFilmstrip);
let EFVideoFilmstrip = class EFVideoFilmstrip$1 extends FilmstripItem {
	contents() {
		return html` 📼 `;
	}
};
EFVideoFilmstrip = _decorate([customElement("ef-video-filmstrip")], EFVideoFilmstrip);
let EFCaptionsFilmstrip = class EFCaptionsFilmstrip$1 extends FilmstripItem {
	contents() {
		return html` 📝 `;
	}
};
EFCaptionsFilmstrip = _decorate([customElement("ef-captions-filmstrip")], EFCaptionsFilmstrip);
let EFWaveformFilmstrip = class EFWaveformFilmstrip$1 extends FilmstripItem {
	contents() {
		return html` 🌊 `;
	}
	renderChildren() {
		return nothing;
	}
};
EFWaveformFilmstrip = _decorate([customElement("ef-waveform-filmstrip")], EFWaveformFilmstrip);
let EFImageFilmstrip = class EFImageFilmstrip$1 extends FilmstripItem {
	contents() {
		return html` 🖼️ `;
	}
};
EFImageFilmstrip = _decorate([customElement("ef-image-filmstrip")], EFImageFilmstrip);
let EFTimegroupFilmstrip = class EFTimegroupFilmstrip$1 extends FilmstripItem {
	contents() {
		return html`
      <span>TIME GROUP</span>
      ${renderFilmstripChildren(Array.from(this.element.children || []), this.pixelsPerMs)}
    </div>
    `;
	}
};
EFTimegroupFilmstrip = _decorate([customElement("ef-timegroup-filmstrip")], EFTimegroupFilmstrip);
let EFHTMLFilmstrip = class EFHTMLFilmstrip$1 extends FilmstripItem {
	contents() {
		return html`
      <span>${this.element.tagName}</span>
      ${renderFilmstripChildren(Array.from(this.element.children || []), this.pixelsPerMs)}
    `;
	}
};
EFHTMLFilmstrip = _decorate([customElement("ef-html-filmstrip")], EFHTMLFilmstrip);
let EFHierarchyItem = class EFHierarchyItem$1 extends TWMixin(LitElement) {
	constructor(..._args2) {
		super(..._args2);
		this.element = new EFTimegroup();
	}
	get icon() {
		return "📼";
	}
	get isFocused() {
		return this.element && this.focusContext?.focusedElement === this.element;
	}
	displayLabel() {
		return nothing;
	}
	render() {
		return html` 
      <div>
        <div
          class="peer 
            flex h-[1.1rem] items-center overflow-hidden text-nowrap  border border-slate-500
          bg-slate-200 pl-2 text-xs font-mono hover:bg-slate-400 data-[focused]:bg-slate-400"
          ?data-focused=${this.isFocused}
          @mouseenter=${() => {
			if (this.focusContext) this.focusContext.focusedElement = this.element;
		}}
          @mouseleave=${() => {
			if (this.focusContext) this.focusContext.focusedElement = null;
		}}
        >
          ${this.icon} ${this.displayLabel()}
        </div>
        <div
          class="p-[1px] pb-0 pl-2 pr-0 peer-hover:bg-slate-300 peer-data-[focused]:bg-slate-300 peer-hover:border-slate-400 peer-data-[focused]:border-slate-400""
        >
          ${this.renderChildren()}
        </div>
      </div>`;
	}
	renderChildren() {
		return renderHierarchyChildren(Array.from(this.element.children));
	}
};
_decorate([property({
	type: Object,
	attribute: false
})], EFHierarchyItem.prototype, "element", void 0);
_decorate([consume({ context: focusContext })], EFHierarchyItem.prototype, "focusContext", void 0);
_decorate([consume({
	context: focusedElementContext,
	subscribe: true
})], EFHierarchyItem.prototype, "focusedElement", void 0);
EFHierarchyItem = _decorate([customElement("ef-hierarchy-item")], EFHierarchyItem);
let EFTimegroupHierarchyItem = class EFTimegroupHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return "🕒";
	}
	displayLabel() {
		return this.element.mode ?? "(no mode)";
	}
};
EFTimegroupHierarchyItem = _decorate([customElement("ef-timegroup-hierarchy-item")], EFTimegroupHierarchyItem);
let EFAudioHierarchyItem = class EFAudioHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return "🔊";
	}
	displayLabel() {
		return this.element.src ?? "(no src)";
	}
};
EFAudioHierarchyItem = _decorate([customElement("ef-audio-hierarchy-item")], EFAudioHierarchyItem);
let EFVideoHierarchyItem = class EFVideoHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return "📼";
	}
	displayLabel() {
		return this.element.src ?? "(no src)";
	}
};
EFVideoHierarchyItem = _decorate([customElement("ef-video-hierarchy-item")], EFVideoHierarchyItem);
let EFCaptionsHierarchyItem = class EFCaptionsHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return "📝 Captions";
	}
};
EFCaptionsHierarchyItem = _decorate([customElement("ef-captions-hierarchy-item")], EFCaptionsHierarchyItem);
let EFCaptionsActiveWordHierarchyItem = class EFCaptionsActiveWordHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return "🗣️ Active Word";
	}
};
EFCaptionsActiveWordHierarchyItem = _decorate([customElement("ef-captions-active-word-hierarchy-item")], EFCaptionsActiveWordHierarchyItem);
let EFWaveformHierarchyItem = class EFWaveformHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return "🌊";
	}
	renderChildren() {
		return nothing;
	}
};
EFWaveformHierarchyItem = _decorate([customElement("ef-waveform-hierarchy-item")], EFWaveformHierarchyItem);
let EFImageHierarchyItem = class EFImageHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return "🖼️";
	}
	displayLabel() {
		return this.element.src ?? "(no src)";
	}
};
EFImageHierarchyItem = _decorate([customElement("ef-image-hierarchy-item")], EFImageHierarchyItem);
let EFHTMLHierarchyItem = class EFHTMLHierarchyItem$1 extends EFHierarchyItem {
	get icon() {
		return html`<code>${`<${this.element.tagName.toLowerCase()}>`}</code>`;
	}
};
EFHTMLHierarchyItem = _decorate([customElement("ef-html-hierarchy-item")], EFHTMLHierarchyItem);
const renderHierarchyChildren = (children) => {
	return children.map((child) => {
		if (child instanceof HTMLElement && child.dataset?.efHidden) return nothing;
		if (child instanceof EFTimegroup) return html`<ef-timegroup-hierarchy-item
        .element=${child}
      ></ef-timegroup-hierarchy-item>`;
		if (child instanceof EFImage) return html`<ef-image-hierarchy-item
        .element=${child}
      ></ef-image-hierarchy-item>`;
		if (child instanceof EFAudio) return html`<ef-audio-hierarchy-item
        .element=${child}
      ></ef-audio-hierarchy-item>`;
		if (child instanceof EFVideo) return html`<ef-video-hierarchy-item
        .element=${child}
      ></ef-video-hierarchy-item>`;
		if (child instanceof EFCaptions) return html`<ef-captions-hierarchy-item
        .element=${child}
      ></ef-captions-hierarchy-item>`;
		if (child instanceof EFCaptionsActiveWord) return html`<ef-captions-active-word-hierarchy-item
        .element=${child}
      ></ef-captions-active-word-hierarchy-item>`;
		if (child instanceof EFWaveform) return html`<ef-waveform-hierarchy-item
        .element=${child}
      ></ef-waveform-hierarchy-item>`;
		return html`<ef-html-hierarchy-item
      .element=${child}
    ></ef-html-hierarchy-item>`;
	});
};
const renderFilmstripChildren = (children, pixelsPerMs) => {
	return children.map((child) => {
		if (child instanceof HTMLElement && child.dataset?.efHidden) return nothing;
		if (child instanceof EFTimegroup) return html`<ef-timegroup-filmstrip
        .element=${child}
        .pixelsPerMs=${pixelsPerMs}
      >
      </ef-timegroup-filmstrip>`;
		if (child instanceof EFImage) return html`<ef-image-filmstrip
        .element=${child}
        .pixelsPerMs=${pixelsPerMs}
      ></ef-image-filmstrip>`;
		if (child instanceof EFAudio) return html`<ef-audio-filmstrip
        .element=${child}
        .pixelsPerMs=${pixelsPerMs}
      ></ef-audio-filmstrip>`;
		if (child instanceof EFVideo) return html`<ef-video-filmstrip
        .element=${child}
        .pixelsPerMs=${pixelsPerMs}
      ></ef-video-filmstrip>`;
		if (child instanceof EFCaptions) return html`<ef-captions-filmstrip
        .element=${child}
        .pixelsPerMs=${pixelsPerMs}
      ></ef-captions-filmstrip>`;
		if (child instanceof EFWaveform) return html`<ef-waveform-filmstrip
        .element=${child}
        .pixelsPerMs=${pixelsPerMs}
      ></ef-waveform-filmstrip>`;
		return html`<ef-html-filmstrip
      .element=${child}
      .pixelsPerMs=${pixelsPerMs}
    ></ef-html-filmstrip>`;
	});
};
let EFFilmstrip = class EFFilmstrip$1 extends TWMixin(LitElement) {
	constructor(..._args3) {
		super(..._args3);
		this.pixelsPerMs = .04;
		this.scrubbing = false;
		this.timelineScrolltop = 0;
		this.currentTimeMs = 0;
		this.autoScale = false;
		this.resizeObserver = new ResizeObserver(() => {
			if (this.autoScale) this.updatePixelsPerMs();
		});
		this.gutterRef = createRef();
		this.hierarchyRef = createRef();
		this.playheadRef = createRef();
	}
	static {
		this.styles = [css`
      :host {
        display: block;
        overflow: hidden;
        width: 100%;
        height: 100%;
      }
    `];
	}
	connectedCallback() {
		super.connectedCallback();
		this.#bindToTargetTimegroup();
		window.addEventListener("keypress", this.#handleKeyPress);
		this.resizeObserver.observe(this);
	}
	disconnectedCallback() {
		super.disconnectedCallback();
		window.removeEventListener("keypress", this.#handleKeyPress);
		this.resizeObserver.disconnect();
	}
	updatePixelsPerMs() {
		const target = this.targetTimegroup;
		const gutter = this.gutterRef.value;
		if (target && gutter && gutter.clientWidth > 0) this.pixelsPerMs = gutter.clientWidth / (target.durationMs || 1);
	}
	#bindToTargetTimegroup() {
		if (this.timegroupController) this.timegroupController.remove();
		const target = this.targetTimegroup;
		if (target) {
			this.timegroupController = new TimegroupController(target, this);
			this.currentTimeMs = target.currentTimeMs;
		}
	}
	#handleKeyPress = (event) => {
		if (event.key === " ") {
			const [target] = event.composedPath();
			const interactiveSelector = "input, textarea, button, select, a, [contenteditable]";
			const closestInteractive = target?.closest(interactiveSelector);
			if (closestInteractive) return;
			event.preventDefault();
			if (this.#contextElement) this.#contextElement.playing = !this.#contextElement.playing;
		}
	};
	syncGutterScroll() {
		if (this.gutter && this.hierarchyRef.value) {
			this.hierarchyRef.value.scrollTop = this.gutter.scrollTop;
			this.timelineScrolltop = this.gutter.scrollTop;
		}
	}
	syncHierarchyScroll() {
		if (this.gutter && this.hierarchyRef.value) {
			this.gutter.scrollTop = this.hierarchyRef.value.scrollTop;
			this.timelineScrolltop = this.hierarchyRef.value.scrollTop;
		}
	}
	scrub(e) {
		if (this.playing) return;
		if (!this.scrubbing) return;
		const gutter = this.shadowRoot?.querySelector("#gutter");
		if (!gutter) return;
		const rect = gutter.getBoundingClientRect();
		if (this.targetTimegroup) {
			const layerX = e.pageX - rect.left + gutter.scrollLeft;
			this.targetTimegroup.currentTimeMs = layerX / this.pixelsPerMs;
		}
	}
	startScrub(e) {
		e.preventDefault();
		this.scrubbing = true;
		queueMicrotask(() => {
			const gutter = this.shadowRoot?.querySelector("#gutter");
			if (!gutter) return;
			const rect = gutter.getBoundingClientRect();
			if (this.targetTimegroup) {
				const layerX = e.pageX - rect.left + gutter.scrollLeft;
				this.targetTimegroup.currentTimeMs = layerX / this.pixelsPerMs;
			}
		});
		addEventListener("mouseup", () => {
			this.scrubbing = false;
		}, { once: true });
	}
	scrollScrub(e) {
		if (this.targetTimegroup && this.gutter && !this.playing) {
			if (e.deltaX !== 0) e.preventDefault();
			if (this.gutterRef.value && this.gutterRef.value.scrollLeft === 0 && e.deltaX < 0) {
				this.gutter.scrollBy(0, e.deltaY);
				return;
			}
			if (this.gutter.scrollWidth - this.gutter.scrollLeft === this.gutter.clientWidth && e.deltaX > 0) {
				this.gutter.scrollBy(0, e.deltaY);
				return;
			}
			if (this) {
				this.gutter.scrollBy(e.deltaX, e.deltaY);
				this.targetTimegroup.currentTimeMs += e.deltaX / this.pixelsPerMs;
			}
		}
	}
	get gutter() {
		return this.gutterRef.value;
	}
	render() {
		const target = this.targetTimegroup;
		return html` <div
      class="grid h-full bg-slate-100"
      style=${styleMap({
			gridTemplateColumns: "200px 1fr",
			gridTemplateRows: "1.5rem 1fr"
		})}
    >
      <div
        class="z-20 col-span-2 border-b-slate-600 bg-slate-100 shadow shadow-slate-300"
      >
        ${!this.autoScale ? html`<input
              type="range"
              .value=${this.pixelsPerMs}
              min="0.01"
              max="0.1"
              step="0.001"
              @input=${(e) => {
			const target$1 = e.target;
			this.pixelsPerMs = Number.parseFloat(target$1.value);
		}}
            />` : nothing}
        <code>${msToTimeCode(this.currentTimeMs, true)} </code> /
        <code>${msToTimeCode(target?.durationMs ?? 0, true)}</code>
        <ef-toggle-play class="inline-block mx-2">
          <div slot="pause"> 
            <button>⏸️</button>
          </div>
          <div slot="play">
            <button>▶️</button>
          </div>
        </ef-toggle-play>
        <ef-toggle-loop><button>${this.loop ? "🔁" : html`<span class="opacity-50 line-through">🔁</span>`}</button></ef-toggle-loop>
      </div>
      <div
        class="z-10 pl-1 pr-1 pt-[8px] shadow shadow-slate-600 overflow-auto"
        ${ref(this.hierarchyRef)}
        @scroll=${this.syncHierarchyScroll}
      >
        ${renderHierarchyChildren(target ? [target] : [])}
      </div>
      <div
        class="flex h-full w-full cursor-crosshair overflow-auto bg-slate-200 pt-[8px]"
        id="gutter"
        ${ref(this.gutterRef)}
        @scroll=${this.syncGutterScroll}
        @wheel=${this.scrollScrub}
      >
        <div
          class="relative h-full w-full"
          style="width: ${this.pixelsPerMs * (target?.durationMs ?? 0)}px;"
          @mousemove=${this.scrub}
          @mousedown=${this.startScrub}
        >
          <div
            class="border-red pointer-events-none absolute z-10 h-full w-[2px] border-r-2 border-red-700"
            style=${styleMap({
			left: `${this.pixelsPerMs * this.currentTimeMs}px`,
			top: `${this.timelineScrolltop}px`
		})}
            ${ref(this.playheadRef)}
          ></div>

          ${renderFilmstripChildren(target ? [target] : [], this.pixelsPerMs)}
        </div>
      </div>
    </div>`;
	}
	updated(changes) {
		if (!this.targetTimegroup) return;
		if (changes.has("currentTimeMs")) {
			if (this.targetTimegroup.currentTimeMs !== this.currentTimeMs) this.targetTimegroup.currentTimeMs = this.currentTimeMs;
		}
	}
	get #contextElement() {
		return this.closest("ef-workbench, ef-preview");
	}
	willUpdate(changedProperties) {
		if (changedProperties.has("targetTimegroup")) this.#bindToTargetTimegroup();
		if (this.autoScale) this.updatePixelsPerMs();
		super.willUpdate(changedProperties);
	}
};
_decorate([property({ type: Number })], EFFilmstrip.prototype, "pixelsPerMs", void 0);
_decorate([state()], EFFilmstrip.prototype, "scrubbing", void 0);
_decorate([state()], EFFilmstrip.prototype, "timelineScrolltop", void 0);
_decorate([consume({
	context: playingContext,
	subscribe: true
}), state()], EFFilmstrip.prototype, "playing", void 0);
_decorate([consume({
	context: loopContext,
	subscribe: true
}), state()], EFFilmstrip.prototype, "loop", void 0);
_decorate([state()], EFFilmstrip.prototype, "currentTimeMs", void 0);
_decorate([property({
	type: Boolean,
	reflect: true,
	attribute: "auto-scale"
})], EFFilmstrip.prototype, "autoScale", void 0);
_decorate([eventOptions({ passive: false })], EFFilmstrip.prototype, "syncGutterScroll", null);
_decorate([eventOptions({ passive: false })], EFFilmstrip.prototype, "syncHierarchyScroll", null);
_decorate([eventOptions({ capture: false })], EFFilmstrip.prototype, "scrub", null);
_decorate([eventOptions({ capture: false })], EFFilmstrip.prototype, "startScrub", null);
_decorate([eventOptions({ passive: false })], EFFilmstrip.prototype, "scrollScrub", null);
_decorate([property({ type: String })], EFFilmstrip.prototype, "target", void 0);
_decorate([consume({
	context: targetTimegroupContext,
	subscribe: true
}), state()], EFFilmstrip.prototype, "targetTimegroup", void 0);
EFFilmstrip = _decorate([customElement("ef-filmstrip")], EFFilmstrip);
export { EFFilmstrip };
