// Tilesets as data: one `.tiles.ts` file per atlas, `export default defineTileset({...})` — the
// material vocabulary (autotile rules) for maps painted by material id. The pure model + deriver
// live in ./autotile.ts; this file is the runtime handle plus the `Autotile2D` aspect that binds a
// tilemap node to its tileset:
//
//   // world.tiles.ts
//   export default defineTileset({
//     image: asset('./TileSet.png'),
//     tile: 32,
//     materials: {
//       grass: { id: 1, kind: 'patch', color: '#4a7c3a', fill: [261, [262, 0.1]],
//                over: { '*': { edges: [197, 262, 325, 260], outer: [198, 326, 324, 196],
//                               inner: [199, 327, 391, 263] } } },
//       road:  { id: 2, kind: 'path', color: '#8a8578', straights: [520, 521], corners: [522, 523, 524, 525] },
//     },
//   })
//
//   // meadow.scene2d.ts
//   import world from './world.tiles'
//   ground: { tilemap: { texture: asset('./TileSet.png'), tile: 32, atlas: [64, 64],
//                        cells: cells('…') },              // cells hold MATERIAL ids here
//             aspects: [use(Autotile2D, { tileset: world, seed: 3 })] }
//
// The aspect flips the meaning of the same `cells('…')` payload from raw atlas indices to material
// ids; the scene2d loader runs the deriver BEFORE the Tilemap is constructed (aspects are data on
// the node def), so no post-construction data path exists and derived indices are never persisted.

import { Aspect } from "../core/Aspect"
import type { Node2D } from "./Node2D"
import { deriveCells, type TilesetDef, type TileMaterial } from "./autotile"

export class Tileset {
  private _def: TilesetDef

  constructor(def: TilesetDef) { this._def = def }

  /** The tileset definition (read-only by convention — the editor owns the file). */
  get def(): TilesetDef { return this._def }

  /** Names of the declared materials, in file order. */
  get materialNames(): string[] { return Object.keys(this._def.materials ?? {}) }

  /** A material by name. */
  material(name: string): TileMaterial | undefined { return this._def.materials?.[name] }

  /** The cell value maps store for a named material (what you paint / pass in `data`). */
  id(name: string): number | undefined { return this._def.materials?.[name]?.id }

  /** Derive display atlas indices from a material-id grid (what Autotile2D does at load). */
  derive(cols: number, rows: number, data: ArrayLike<number>, seed = 0): Int32Array {
    return deriveCells(this._def, cols, rows, data, seed)
  }
}

/** Declare a tileset (the default export of a `.tiles.ts` file). */
export const defineTileset = (def: TilesetDef): Tileset => new Tileset(def)

/**
 * Marks a tilemap node's `cells` as MATERIAL ids of a tileset instead of raw atlas indices:
 * `aspects: [use(Autotile2D, { tileset: world })]`. The scene2d loader derives the display
 * indices (neighbor-mask autotile + seeded scatter, deterministic) before building the tilemap;
 * the aspect itself holds no behavior. Change `seed` to reshuffle scatter variants.
 */
export class Autotile2D extends Aspect<"autotile", Node2D> {
  static readonly aspect = "autotile"

  /** The imported `.tiles.ts` handle. */
  tileset: Tileset | null = null
  /** Scatter seed — same seed, same map, same result on every platform. */
  seed = 0
}
