import { Container, Rectangle, Sprite, Texture } from "pixi.js";
import type { GenerateTextureOptions, TextureSource } from "pixi.js";

interface TextureRenderer {
  generateTexture(options: GenerateTextureOptions | Container): Texture;
}

/**
 * Packs a flat record of named textures into a single atlas and returns a
 * TileAtlas wrapping it. All textures must be the same tileSize × tileSize.
 */
export function buildTileAtlas(
  renderer: TextureRenderer,
  tiles: Record<string, Texture>,
  tileSize: number,
): TileAtlas {
  const entries = Object.entries(tiles);
  const count = entries.length;
  const cols = Math.ceil(Math.sqrt(count));
  const rows = Math.ceil(count / cols);

  const container = new Container();
  const layout: { name: string; col: number; row: number }[] = [];

  entries.forEach(([name, texture], i) => {
    const col = i % cols;
    const row = Math.floor(i / cols);
    const sprite = new Sprite(texture);
    sprite.x = col * tileSize;
    sprite.y = row * tileSize;
    container.addChild(sprite);
    layout.push({ name, col, row });
  });

  const atlas = renderer.generateTexture({
    target: container,
    resolution: 1,
    frame: new Rectangle(0, 0, cols * tileSize, rows * tileSize),
  });
  container.destroy();

  const textures: Record<string, Texture> = {};
  for (const { name, col, row } of layout) {
    textures[name] = new Texture({
      source: atlas.source,
      frame: new Rectangle(col * tileSize, row * tileSize, tileSize, tileSize),
    });
  }

  return new TileAtlas(atlas.source, textures);
}

/** Holds a packed atlas and provides named texture lookup. */
export class TileAtlas {
  private readonly _source: TextureSource;
  private readonly _textures: Record<string, Texture>;

  constructor(source: TextureSource, textures: Record<string, Texture>) {
    this._source = source;
    this._textures = textures;
  }

  /** Returns the sub-texture for the given name; throws if not found. */
  get(name: string): Texture {
    const tex = this._textures[name];
    if (tex === undefined) throw new Error(`TileAtlas: unknown tile "${name}"`);
    return tex;
  }

  /** The underlying GPU source, for passing to the Tilemap constructor. */
  get source(): TextureSource {
    return this._source;
  }
}