import { FOV } from "rot-js";
import { type World, type EntityId } from "../core/types";
import { idx, inBounds, isWall } from "../engine/world/world-logic";
import { GAME_CONFIG } from "../core/config/GameConfig";
import Phaser from "phaser";

export class FovManager {
  private fov!: any;
  private seen!: Uint8Array;
  private visible!: Uint8Array;
  private visibleStrength!: Float32Array;
  private worldWidth: number = 0;
  private worldHeight: number = 0;

  initialize(world: World) {
    this.worldWidth = world.width;
    this.worldHeight = world.height;
    this.seen = new Uint8Array(world.width * world.height);
    this.visible = new Uint8Array(world.width * world.height);
    this.visibleStrength = new Float32Array(world.width * world.height);

    this.fov = new FOV.PreciseShadowcasting((x: number, y: number) => {
      if (!inBounds(world, x, y)) return false;
      return !isWall(world, x, y);
    });
  }

  compute(world: World, playerId: EntityId) {
    this.visible.fill(0);
    this.visibleStrength.fill(0);

    const player = world.actors.get(playerId)!;
    const ox = player.pos.x;
    const oy = player.pos.y;

    this.fov.compute(ox, oy, GAME_CONFIG.player.viewRadius, (x: number, y: number, r: number, v: number) => {
      if (!inBounds(world, x, y)) return;

      const i = idx(world, x, y);
      this.visible[i] = 1;
      this.seen[i] = 1;

      const radiusT = Phaser.Math.Clamp(r / GAME_CONFIG.player.viewRadius, 0, 1);
      const falloff = 1 - radiusT * 0.6;
      const strength = Phaser.Math.Clamp(v * falloff, 0, 1);

      if (strength > this.visibleStrength[i]) this.visibleStrength[i] = strength;
    });
  }

  isSeen(x: number, y: number): boolean {
    if (!inBounds({ width: this.worldWidth, height: this.worldHeight } as any, x, y)) return false;
    return this.seen[y * this.worldWidth + x] === 1;
  }

  isVisible(x: number, y: number): boolean {
    if (!inBounds({ width: this.worldWidth, height: this.worldHeight } as any, x, y)) return false;
    return this.visible[y * this.worldWidth + x] === 1;
  }

  get seenArray() {
    return this.seen;
  }

  get visibleArray() {
    return this.visible;
  }
}
