// GLB skeletal animation as an aspect. Auto-attached by Model (a glb always has its clips), so you
// reach it directly: model.anim.play('Run'). Clips come baked in the glb; play by name or index.

import { Aspect } from "../core/Aspect"
import { nodeRegistry } from "./Node"
import type { Model } from "./Model"

let animatorRegistered = false
const ensureAnimator = (): void => {
  if (animatorRegistered) return
  animatorRegistered = true
  _creator.setOnAnimationComplete((entityId, clipIndex) => nodeRegistry.get(entityId)?._onAnimationComplete(clipIndex) ?? true)
}

export class ModelAnimation extends Aspect<"anim", Model> {
  static readonly aspect = "anim"

  private _playing = false
  private _clip = 0
  private _speed = 1
  private _loop = false

  onAttach(): void {
    this.node._animComplete = (clipIndex) => {
      if (clipIndex !== this._clip) return true
      if (!this._loop) { this._playing = false; this.node._emitAnim("completed", clipIndex) }
      else this.node._emitAnim("loopReached", clipIndex)
      return this._playing
    }
  }

  /** The clips baked into the glb. */
  get clips(): { name: string, duration: number }[] { return _creator.getGlbAnimations(this.node.id) }

  get playing(): boolean { return this._playing }
  set playing(value: boolean) {
    this._playing = value
    _creator.setAnimation(this.node.id, value, this._clip, this._speed)
  }

  /** Play a clip by name or index. Chainable. */
  play(clip?: string | number, options: { loop?: boolean } = {}): this {
    if (typeof clip === "number") this._clip = clip
    else if (typeof clip === "string") {
      const i = this.clips.findIndex((c) => c.name === clip)
      if (i >= 0) this._clip = i
    }
    if (options.loop !== undefined) this._loop = options.loop
    this.playing = true
    ensureAnimator()
    return this
  }
  stop(): this { this.playing = false; return this }

  get loop(): boolean { return this._loop }
  set loop(v: boolean) { this._loop = v }
  get speed(): number { return this._speed }
  set speed(v: number) {
    this._speed = v
    _creator.setAnimation(this.node.id, this._playing, this._clip, v)
  }
  get time(): number { return _creator.getAnimationTime(this.node.id) }
  set time(v: number) { _creator.setAnimationTime(this.node.id, this._clip, v) }
}
