import 'package:wolf_3d_dart/src/data_types/difficulty.dart';
import 'package:wolf_3d_dart/src/registry/keys/menu_pic_key.dart';
import 'package:wolf_3d_dart/src/registry/modules/menu_pic_module.dart';

/// Built-in menu-picture module for the Wolfenstein 3D v1.4 Shareware release.
///
/// Shareware VGAGRAPH contains fewer pictures than the retail version, so
/// the episode/difficulty/control-panel art sits at a shifted position in
/// the VGA image list.  The exact shift is computed at resolve time by
/// scanning the loaded image list for the landmark STATUSBARPIC, mirroring
/// the runtime heuristic in the original [WolfClassicMenuArt._indexOffset].
///
/// Offset determination is deferred until the first [resolve] call and
/// cached for subsequent lookups.  If the landmark cannot be found the
/// module falls back to the retail indices (compatible with unrecognised
/// or future shareware variants).
class SharewareMenuPicModule extends MenuPicModule {
  SharewareMenuPicModule({this.useOriginalWl1Map = false});

  /// When true, use the canonical original WL1 index table directly.
  ///
  /// Unknown/custom shareware-like data should use the heuristic path instead.
  final bool useOriginalWl1Map;

  // Retail-baseline indices (same layout as RetailMenuPicModule).
  static final Map<MenuPicKey, int> _retailBaseline = {
    MenuPicKey.title: 84,
    MenuPicKey.credits: 86,
    MenuPicKey.pg13: 85,
    MenuPicKey.controlBackground: 23,
    MenuPicKey.footer: 15,
    MenuPicKey.heading: 3,
    MenuPicKey.optionsLabel: 7,
    MenuPicKey.customizeLabel: 36,
    MenuPicKey.cursorActive: 8,
    MenuPicKey.cursorInactive: 9,
    MenuPicKey.markerSelected: 11,
    MenuPicKey.markerUnselected: 10,
    MenuPicKey.episode1: 27,
    MenuPicKey.episode2: 28,
    MenuPicKey.episode3: 29,
    MenuPicKey.episode4: 30,
    MenuPicKey.episode5: 31,
    MenuPicKey.episode6: 32,
    MenuPicKey.difficultyBaby: 16,
    MenuPicKey.difficultyEasy: 17,
    MenuPicKey.difficultyNormal: 18,
    MenuPicKey.difficultyHard: 19,
  };

  // Canonical menu picture indices for the original v1.4 shareware data
  // identity (DataVersion.version14Shareware).
  static final Map<MenuPicKey, int> _originalWl1Map = {
    MenuPicKey.title: 96,
    MenuPicKey.credits: 98,
    MenuPicKey.pg13: 97,
    MenuPicKey.controlBackground: 35,
    MenuPicKey.footer: 27,
    MenuPicKey.heading: 14,
    MenuPicKey.optionsLabel: 19,
    MenuPicKey.customizeLabel: 36,
    MenuPicKey.cursorActive: 20,
    MenuPicKey.cursorInactive: 21,
    MenuPicKey.markerSelected: 23,
    MenuPicKey.markerUnselected: 22,
    MenuPicKey.episode1: 39,
    MenuPicKey.difficultyBaby: 28,
    MenuPicKey.difficultyEasy: 29,
    MenuPicKey.difficultyNormal: 30,
    MenuPicKey.difficultyHard: 31,
  };

  // Landmark constant: STATUSBARPIC in retail picture-table coords.
  static const int _retailStatusBarIndex = 83;

  // Runtime-computed shift applied to every retail-baseline index.
  // null = not yet computed; 0 = no shift needed (or heuristic failed).
  int? _offset;

  /// Provide the raw VGA image dimensions list so the module can compute
  /// the version offset on first use.  Call this immediately after
  /// construction when the [WolfensteinData] instance is available.
  ///
  /// [imageSizes] is a list of `(width, height)` tuples in VGA image order.
  void initWithImageSizes(List<({int width, int height})> imageSizes) {
    _offset = _computeOffset(imageSizes);
  }

  int _computeOffset(List<({int width, int height})> sizes) {
    for (int i = 0; i < sizes.length - 3; i++) {
      final s = sizes[i];
      // STATUSBARPIC heuristic: wide short banner followed by three full-screen images.
      if (s.width >= 280 && s.height >= 24 && s.height <= 64) {
        final t1 = sizes[i + 1];
        final t2 = sizes[i + 2];
        final t3 = sizes[i + 3];
        if (_isFullScreen(t1) && _isFullScreen(t2) && _isFullScreen(t3)) {
          return i - _retailStatusBarIndex;
        }
      }
    }
    return 0; // no shift — unknown layout, use retail baseline
  }

  bool _isFullScreen(({int width, int height}) s) =>
      s.width >= 280 && s.height >= 140;

  @override
  MenuPicRef? resolve(MenuPicKey key) {
    if (useOriginalWl1Map) {
      final wl1 = _originalWl1Map[key];
      return wl1 == null ? null : MenuPicRef(wl1);
    }

    final base = _retailBaseline[key];
    if (base == null) return null;

    // The shareware/release drift is observed around STATUSBAR and later
    // full-screen art. Earlier control-panel assets keep retail indices.
    final shifted = base >= _retailStatusBarIndex
        ? base + (_offset ?? 0)
        : base;
    return MenuPicRef(shifted);
  }

  @override
  MenuPicKey episodeKey(int episodeIndex) {
    const keys = [
      MenuPicKey.episode1,
      MenuPicKey.episode2,
      MenuPicKey.episode3,
      MenuPicKey.episode4,
      MenuPicKey.episode5,
      MenuPicKey.episode6,
    ];
    if (episodeIndex >= 0 && episodeIndex < keys.length) {
      return keys[episodeIndex];
    }
    return MenuPicKey.episode1;
  }

  @override
  MenuPicKey difficultyKey(Difficulty difficulty) {
    return switch (difficulty) {
      Difficulty.baby => MenuPicKey.difficultyBaby,
      Difficulty.easy => MenuPicKey.difficultyEasy,
      Difficulty.medium => MenuPicKey.difficultyNormal,
      Difficulty.hard => MenuPicKey.difficultyHard,
    };
  }
}
