import 'dart:math' as math;

import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';

class Pushwall {
  int x;
  int y;
  int mapId;
  int dirX = 0;
  int dirY = 0;
  double offset = 0.0;
  int tilesMoved = 0;

  Pushwall(this.x, this.y, this.mapId);
}

class PushwallManager {
  final Map<String, Pushwall> pushwalls = {};
  Pushwall? activePushwall;

  void initPushwalls(Sprite wallGrid, Sprite objectGrid) {
    pushwalls.clear();
    activePushwall = null;

    for (int y = 0; y < objectGrid.length; y++) {
      for (int x = 0; x < objectGrid[y].length; x++) {
        if (objectGrid[y][x] == MapObject.pushwallTrigger) {
          pushwalls['$x,$y'] = Pushwall(x, y, wallGrid[y][x]);
        }
      }
    }
  }

  void update(Duration elapsed, Sprite wallGrid) {
    if (activePushwall == null) return;
    final pw = activePushwall!;

    // Original logic: 1/128 tile per tick.
    // At 70 ticks/sec, that is roughly 0.54 tiles per second.
    const double originalSpeed = 0.546875;
    pw.offset += (elapsed.inMilliseconds / 1000.0) * originalSpeed;

    // Once it crosses a full tile boundary, we update the collision grid!
    if (pw.offset >= 1.0) {
      pw.offset -= 1.0;
      pw.tilesMoved++;

      int nextX = pw.x + pw.dirX;
      int nextY = pw.y + pw.dirY;

      // Move the solid block in the physical grid
      wallGrid[nextY][nextX] = pw.mapId;
      wallGrid[pw.y][pw.x] = 0; // Clear the old space so the player can walk in

      // Update the dictionary key
      pushwalls.remove('${pw.x},${pw.y}');
      pw.x = nextX;
      pw.y = nextY;
      pushwalls['${pw.x},${pw.y}'] = pw;

      // Check if we should keep sliding
      bool blocked = false;
      int checkX = pw.x + pw.dirX;
      int checkY = pw.y + pw.dirY;

      if (checkX < 0 ||
          checkX >= wallGrid[0].length ||
          checkY < 0 ||
          checkY >= wallGrid.length) {
        blocked = true;
      } else if (wallGrid[checkY][checkX] != 0) {
        blocked = true; // Blocked by another wall or a door
      }

      // Standard Wolf3D pushwalls move exactly 2 tiles (or 1 if blocked)
      if (pw.tilesMoved >= 2 || blocked) {
        activePushwall = null;
        pw.offset = 0.0;
      }
    }
  }

  void handleInteraction(
    double playerX,
    double playerY,
    double playerAngle,
    Sprite wallGrid,
  ) {
    // Only one pushwall can move at a time in the original engine!
    if (activePushwall != null) return;

    int targetX = (playerX + math.cos(playerAngle)).toInt();
    int targetY = (playerY + math.sin(playerAngle)).toInt();

    String key = '$targetX,$targetY';
    if (pushwalls.containsKey(key)) {
      final pw = pushwalls[key]!;

      // Determine the push direction based on the player's relative position
      double dx = (targetX + 0.5) - playerX;
      double dy = (targetY + 0.5) - playerY;

      if (dx.abs() > dy.abs()) {
        pw.dirX = dx > 0 ? 1 : -1;
        pw.dirY = 0;
      } else {
        pw.dirX = 0;
        pw.dirY = dy > 0 ? 1 : -1;
      }

      // Make sure the tile behind the wall is empty before starting the push
      int checkX = targetX + pw.dirX;
      int checkY = targetY + pw.dirY;

      if (wallGrid[checkY][checkX] == 0) {
        activePushwall = pw;
      }
    }
  }
}
