// A textured 2D node. Cheap setters proxy straight to the native sprite; the data lives in
// creator-2d. Sprite-sheet animation is no longer built in — attach the SpriteAnimation aspect:
//   new Sprite({ texture }).aspect(SpriteAnimation, { size: [32, 48], idle: [0,1,2,3] }).anim.play('idle')

import { Color, type ColorInput } from "../core/color"
import { Vec2, type Vec2Like } from "../math/vec"
import { Canvas, isCanvas } from "../canvas/Canvas"
import { Node2D } from "./Node2D"
import type { Texture2D } from "./Texture2D"

/** Anything that can back a sprite: a texture, or a Canvas (baked to a texture on assign). */
export type TextureSource = Texture2D | Canvas

export type SpriteOptions = {
  texture?: TextureSource
  /** Pivot, [0,0] = top-left .. [1,1] = bottom-right. Use [0.5, 1] (feet) for Y-sorted depth. */
  anchor?: Vec2Like
  /** World size; defaults to the texture's pixel dimensions. */
  size?: Vec2Like
  /** Normalized UV sub-rect [u0,v0,u1,v1]. */
  frame?: [number, number, number, number]
  /** Tint multiplied with the texture. */
  color?: ColorInput
  opacity?: number
  layer?: number
  position?: Vec2Like
}

export class Sprite extends Node2D {
  private _texture?: Texture2D
  private _anchor = new Vec2(0.5, 0.5)
  private _color: ColorInput = 0xffffff
  private _opacity = 1
  private _flipX = false
  private _flipY = false
  private _size: Vec2 | null = null

  constructor(options: SpriteOptions = {}) {
    super()
    if (options.texture) this.texture = options.texture
    if (options.size) this.size = options.size
    if (options.anchor) this.anchor = options.anchor
    if (options.frame) this.frame = options.frame
    if (options.color !== undefined) this.color = options.color
    if (options.opacity !== undefined) this.opacity = options.opacity
    if (options.layer !== undefined) this.layer = options.layer
    if (options.position) this.position = options.position
  }

  get texture(): Texture2D | undefined { return this._texture }
  set texture(t: TextureSource | undefined) {
    const tex = isCanvas(t) ? t.texture() : t   // a Canvas bakes to a texture on assign
    this._texture = tex
    if (!tex) return
    _creator2d.setSprite(this.id, tex.id) // native resets UV + size to the full (pixel) texture
    // A Canvas is authored in LOGICAL units but baked at logical × pixelRatio, so default the sprite's
    // WORLD size to the canvas's LOGICAL size — pixelRatio then only affects crispness, not on-screen
    // size. (A plain texture keeps the native default: the texture's pixel dimensions.)
    if (!this._size && isCanvas(t)) this._size = new Vec2(t.width, t.height)
    if (this._size) _creator2d.setSpriteSize(this.id, this._size.x, this._size.y)
  }

  /** Current world size: explicit override if set, else the texture's pixel dimensions (a Canvas-backed
   *  sprite defaults to the canvas's LOGICAL size, so pixelRatio is crispness only, not size). */
  get size(): Vec2 { return this._size ?? (this._texture ? new Vec2(this._texture.width, this._texture.height) : new Vec2(0, 0)) }
  set size(v: Vec2Like) { this._size = new Vec2(v); _creator2d.setSpriteSize(this.id, this._size.x, this._size.y) }

  get anchor(): Vec2 { return this._anchor.clone() }
  set anchor(v: Vec2Like) { this._anchor = new Vec2(v); _creator2d.setSpriteAnchor(this.id, this._anchor.x, this._anchor.y) }

  /** Set the UV sub-rect (normalized). */
  set frame(uv: [number, number, number, number]) { _creator2d.setSpriteFrame(this.id, uv[0], uv[1], uv[2], uv[3]) }
  /** Set the sub-rect in texture pixels. */
  setFramePx(px: number, py: number, pw: number, ph: number): this {
    _creator2d.setSpriteFramePx(this.id, px, py, pw, ph)
    return this
  }

  get color(): ColorInput { return this._color }
  set color(c: ColorInput) { this._color = c; const [ r, g, b ] = Color.toRgb01(c); _creator2d.setSpriteColor(this.id, r, g, b) }

  get opacity(): number { return this._opacity }
  set opacity(a: number) { this._opacity = a; _creator2d.setSpriteOpacity(this.id, a) }

  get flipX(): boolean { return this._flipX }
  set flipX(v: boolean) { this._flipX = v; _creator2d.setSpriteFlip(this.id, v, this._flipY) }
  get flipY(): boolean { return this._flipY }
  set flipY(v: boolean) { this._flipY = v; _creator2d.setSpriteFlip(this.id, this._flipX, v) }
}
