import 'dart:math' as math;

import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
import 'package:wolf_3d_dart/wolf_3d_entities.dart';

/// Static definitions and logic for objects found in the map's second plane (Object Layer).
///
/// This includes player start positions, collectibles, static decorations,
/// and interactive triggers like level exits.
abstract class MapObject {
  // --- Player Spawns ---
  static const int playerNorth = 19;
  static const int playerEast = 20;
  static const int playerSouth = 21;
  static const int playerWest = 22;

  // --- Static Decorations ---
  // These are purely visual sprites that don't move or interact.
  static const int waterPuddle = 23;
  static const int greenBarrel = 24;
  static const int chairTable = 25;
  static const int floorLamp = 26;
  static const int chandelier = 27;
  static const int hangingSkeleton = 28;
  static const int dogFoodDecoration = 29;
  static const int whiteColumn = 30;
  static const int pottedPlant = 31;
  static const int blueSkeleton = 32;
  static const int vent = 33;
  static const int kitchenCans = 34;
  static const int exitSign = 35;
  static const int brownPlant = 36;
  static const int bowl = 37;
  static const int armoredSuit = 38;
  static const int emptyCage = 39;
  static const int cageWithSkeleton = 40;
  static const int bones = 41;
  static const int goldenKeyBowl = 42;

  // --- Collectibles ---
  static const int goldKey = 43;
  static const int silverKey = 44;
  static const int bed = 45;
  static const int basket = 46;
  static const int food = 47;
  static const int medkit = 48;
  static const int ammoClip = 49;
  static const int machineGun = 50;
  static const int chainGun = 51;
  static const int cross = 52;
  static const int chalice = 53;
  static const int chest = 54;
  static const int crown = 55;
  static const int extraLife = 56;

  // --- Environmental ---
  static const int bloodPoolSmall = 57;
  static const int barrel = 58;
  static const int wellFull = 59;
  static const int wellEmpty = 60;
  static const int bloodPoolLarge = 61;
  static const int flag = 62;
  static const int aardwolfSign = 63;
  static const int bonesAndSkull = 64;
  static const int wallHanging = 65;
  static const int stove = 66;
  static const int spearRack = 67;
  static const int vines = 68;

  // --- Logic & Triggers ---
  // Path markers used by patrolling enemies (T_Path-style route turns).
  static const int patrolEast = 90;
  static const int patrolNorth = 91;
  static const int patrolWest = 92;
  static const int patrolSouth = 93;
  static const int patrolEastAlt = 94;
  static const int patrolNorthAlt = 95;
  static const int patrolWestAlt = 96;
  static const int patrolSouthAlt = 97;

  static const int pushwallTrigger = 98;
  static const int secretExitTrigger = 99;
  static const int normalExitTrigger = 100;

  // --- Wall Textures (From VSWAP/MAPHEAD) ---
  static const int normalElevatorSwitch = 21;
  static const int secretElevatorSwitch = 41;

  // Bosses (Shared between WL1 and WL6)
  static const int bossHansGrosse = 214;

  // WL6 Exclusive Bosses
  static const int bossDrSchabbs = 215;
  static const int bossTransGrosse = 216;
  static const int bossUbermutant = 217;
  static const int bossDeathKnight = 218;
  static const int bossMechaHitler = 219;
  static const int bossHitlerGhost = 220;
  static const int bossGretelGrosse = 221;
  static const int bossGiftmacher = 222;
  static const int bossFettgesicht = 223;

  // --- Enemy Range Constants ---
  // Enemies are encoded in 8-ID blocks: 4 standing directions and 4 patrol directions.
  // Most enemy families shift by 36 IDs between difficulty tiers, while mutants shift by 18.
  static const int guardStart = 108; // Claims 108-115, 144-151, 180-187
  static const int officerStart = 116; // Claims 116-123, 152-159, 188-195
  static const int ssStart = 126; // Claims 126-133, 162-169, 198-205
  static const int dogStart = 134; // Claims 134-141, 170-177, 206-213
  static const int mutantStart = 216; // Claims 216-223, 234-241, 252-259

  // --- Missing Decorative Bodies ---
  static const int deadGuard = 124; // Decorative only in WL1
  static const int deadAardwolf = 125; // Decorative only in WL1

  /// Calculates the rotation in radians for a player or enemy based on their Map ID.
  static double getAngle(int id) {
    switch (id) {
      case playerNorth:
        return CardinalDirection.north.radians;
      case playerEast:
        return CardinalDirection.east.radians;
      case playerSouth:
        return CardinalDirection.south.radians;
      case playerWest:
        return CardinalDirection.west.radians;
    }

    if (id == bossHansGrosse) return 0.0;

    final EnemyType? type = EnemyType.fromMapId(id);
    if (type == null) return 0.0;

    final int? normalizedId = type.mapData.getNormalizedId(id, Difficulty.hard);
    if (normalizedId == null) return 0.0;

    // Enemy IDs are stored as 8-ID blocks per family.
    // Offsets 0-3 are standing directions and 4-7 are patrol directions.
    return CardinalDirection.fromEnemyIndex(
      normalizedId - type.mapData.baseId,
    ).radians;
  }

  /// Validates if an object should exist on the map given the current [difficulty].
  static bool isDifficultyAllowed(int objId, Difficulty difficulty) {
    // 1. Decorative dead bodies (used in WL1) always spawn.
    if (objId == deadGuard || objId == deadAardwolf) return true;

    // 2. Enemy filtering is handled by the Enemy factory logic,
    // so we pass them through here.
    if (EnemyType.fromMapId(objId) != null) {
      return true;
    }

    // 3. Static items (ammo, health, plants) are constant across all difficulties.
    return true;
  }

  /// Returns a patrol facing angle for path markers, or null if [objId]
  /// is not a patrol routing marker.
  static double? patrolAngleForMarker(int objId) {
    return switch (objId) {
      patrolEast => 0.0,
      patrolNorth => -math.pi / 4,
      patrolWest => -math.pi / 2,
      patrolSouth => -3 * math.pi / 4,
      patrolEastAlt => math.pi,
      patrolNorthAlt => 3 * math.pi / 4,
      patrolWestAlt => math.pi / 2,
      patrolSouthAlt => math.pi / 4,
      _ => null,
    };
  }
}
