import 'package:wolf_3d_dart/wolf_3d_data_types.dart';

/// Defines the high-level AI or physical state of an entity.
enum EntityState {
  /// Non-moving decorative objects or items.
  staticObj,

  /// Enemies waiting for the player to enter their field of view.
  ambushing,

  /// Enemies standing still but capable of hearing noise.
  idle,

  /// Enemies moving along a predefined path.
  patrolling,

  /// Entities currently performing a combat action.
  attacking,

  /// Entities in a hit-stun or flinch animation.
  pain,

  /// Entities that have completed their dying sequence.
  dead,
}

/// The base class for all dynamic objects in the game world.
///
/// This includes players, enemies, and collectibles. It manages spatial
/// coordinates and provides core utilities for visibility checks.
abstract class Entity {
  /// Horizontal position in the 64x64 grid.
  double x;

  /// Vertical position in the 64x64 grid.
  double y;

  /// The specific sprite index from the global [WolfensteinData.sprites] list.
  int spriteIndex;

  /// Current rotation in radians.
  double angle;

  /// Current behavior or animation state.
  EntityState state;

  /// The original ID from the map's object layer.
  int mapId;

  /// Timestamp (in ms) of the last state change, used for animation timing.
  int lastActionTime;

  Entity({
    required this.x,
    required this.y,
    required this.spriteIndex,
    this.angle = 0.0,
    this.state = EntityState.staticObj,
    this.mapId = 0,
    this.lastActionTime = 0,
  });

  /// Updates the spatial coordinates using a [Coordinate2D].
  set position(Coordinate2D pos) {
    x = pos.x;
    y = pos.y;
  }

  /// Returns the current coordinates as a [Coordinate2D].
  Coordinate2D get position => Coordinate2D(x, y);

  /// Performs a grid-based line-of-sight check from a [source] to this entity.
  ///
  /// Uses an Integer Bresenham algorithm to traverse tiles between the points.
  /// Returns `false` if the path is obstructed by a wall (non-walkable tile).
  bool hasLineOfSightFrom(
    Coordinate2D source,
    double sourceAngle,
    double distance,
    bool Function(int x, int y) isWalkable,
  ) {
    int currentX = source.x.toInt();
    int currentY = source.y.toInt();
    int targetX = x.toInt();
    int targetY = y.toInt();

    int dx = (targetX - currentX).abs();
    int dy = -(targetY - currentY).abs();
    int sx = currentX < targetX ? 1 : -1;
    int sy = currentY < targetY ? 1 : -1;
    int err = dx + dy;

    while (true) {
      if (!isWalkable(currentX, currentY)) return false;
      if (currentX == targetX && currentY == targetY) break;

      int e2 = 2 * err;
      if (e2 >= dy) {
        err += dy;
        currentX += sx;
      }
      if (e2 <= dx) {
        err += dx;
        currentY += sy;
      }
    }
    return true;
  }
}
