import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:wolf_3d_data/wolf_3d_data.dart';
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
import 'package:wolf_3d_engine/wolf_3d_engine.dart';
import 'package:wolf_3d_synth/wolf_3d_synth.dart';

class Wolf3d {
  Wolf3d._();
  static final Wolf3d _instance = Wolf3d._();
  static Wolf3d get I => _instance;

  // --- State ---
  final List<WolfensteinData> availableGames = [];
  WolfensteinData? _activeGame;

  // --- Core Systems ---
  final EngineAudio audio = WolfAudio();

  // --- Getters ---
  WolfensteinData get activeGame {
    if (_activeGame == null) {
      throw StateError("No active game selected. Call setActiveGame() first.");
    }
    return _activeGame!;
  }

  // --- Episode ---
  int _activeEpisode = 0;

  int get activeEpisode => _activeEpisode;

  void setActiveEpisode(int episodeIndex) {
    _activeEpisode = episodeIndex;
  }

  // Convenience getters for the active game's assets
  List<WolfLevel> get levels => activeGame.episodes[activeEpisode].levels;
  List<Sprite> get walls => activeGame.walls;
  List<Sprite> get sprites => activeGame.sprites;
  List<PcmSound> get sounds => activeGame.sounds;
  List<PcmSound> get adLibSounds => activeGame.adLibSounds;
  List<ImfMusic> get music => activeGame.music;
  List<VgaImage> get vgaImages => activeGame.vgaImages;

  // --- Actions ---
  void setActiveGame(WolfensteinData game) {
    _activeGame = game;
    audio.activeGame = game;
  }

  /// Initializes the engine by loading available game data.
  Future<void> init({String? directory}) async {
    await audio.init();
    availableGames.clear();

    // 1. Bundle asset loading (migrated from GameSelectScreen)
    final versionsToTry = [
      (version: GameVersion.retail, path: 'retail'),
      (version: GameVersion.shareware, path: 'shareware'),
    ];

    for (final version in versionsToTry) {
      try {
        final ext = version.version.fileExtension;
        final folder = 'assets/${version.path}';

        final data = WolfensteinLoader.loadFromBytes(
          version: version.version,
          vswap: await _tryLoad('$folder/VSWAP.$ext'),
          mapHead: await _tryLoad('$folder/MAPHEAD.$ext'),
          gameMaps: await _tryLoad('$folder/GAMEMAPS.$ext'),
          vgaDict: await _tryLoad('$folder/VGADICT.$ext'),
          vgaHead: await _tryLoad('$folder/VGAHEAD.$ext'),
          vgaGraph: await _tryLoad('$folder/VGAGRAPH.$ext'),
          audioHed: await _tryLoad('$folder/AUDIOHED.$ext'),
          audioT: await _tryLoad('$folder/AUDIOT.$ext'),
        );

        availableGames.add(data);
      } catch (e) {
        debugPrint(e.toString());
      }
    }

    // 2. External side-loading (non-web)
    if (!kIsWeb) {
      try {
        final externalGames = await WolfensteinLoader.discover(
          directoryPath: directory,
          recursive: true,
        );
        for (var entry in externalGames.entries) {
          if (!availableGames.any((g) => g.version == entry.key)) {
            availableGames.add(entry.value);
          }
        }
      } catch (e) {
        debugPrint("External discovery failed: $e");
      }
    }
  }

  Future<ByteData?> _tryLoad(String path) async {
    try {
      return await rootBundle.load(path);
    } catch (e) {
      debugPrint("Asset not found: $path");
      return null;
    }
  }
}
