/// CLI-specific input adapter that converts raw key bytes into engine actions.
library;

import 'package:wolf_3d_dart/src/input/wolf_3d_input.dart';
import 'package:wolf_3d_dart/wolf_3d_entities.dart';

/// Buffers one-frame terminal key presses for consumption by the engine loop.
class CliInput extends Wolf3dInput {
  /// Keyboard shortcut used by the CLI host to cycle renderer modes.
  String rendererToggleKey = 'r';

  /// Keyboard shortcut used by the CLI host to cycle ASCII themes.
  String asciiThemeCycleKey = 't';

  /// Human-friendly label for [rendererToggleKey] shown in CLI hints.
  String get rendererToggleKeyLabel => _formatShortcutLabel(rendererToggleKey);

  /// Human-friendly label for [asciiThemeCycleKey] shown in CLI hints.
  String get asciiThemeCycleKeyLabel =>
      _formatShortcutLabel(asciiThemeCycleKey);

  /// Returns true when [bytes] triggers the renderer-toggle shortcut.
  bool matchesRendererToggleShortcut(List<int> bytes) =>
      _matchesShortcut(bytes, rendererToggleKey);

  /// Returns true when [bytes] triggers the ASCII-theme shortcut.
  bool matchesAsciiThemeCycleShortcut(List<int> bytes) =>
      _matchesShortcut(bytes, asciiThemeCycleKey);

  String _formatShortcutLabel(String key) {
    final String trimmed = key.trim();
    if (trimmed.isEmpty) {
      return 'KEY';
    }
    if (trimmed == ' ') {
      return 'SPACE';
    }
    return trimmed.toUpperCase();
  }

  bool _matchesShortcut(List<int> bytes, String key) {
    final String trimmed = key.trim();
    if (trimmed.isEmpty) {
      return false;
    }

    if (trimmed == ' ') {
      return bytes.length == 1 && bytes[0] == 32;
    }

    if (bytes.length != 1) {
      return false;
    }

    final int expected = trimmed.codeUnitAt(0);
    final int actual = bytes[0];
    return actual == expected ||
        actual ==
            (expected >= 97 && expected <= 122 ? expected - 32 : expected) ||
        actual == (expected >= 65 && expected <= 90 ? expected + 32 : expected);
  }

  // Raw stdin arrives asynchronously, so presses are staged here until the
  // next engine frame snapshots them into the active state.
  bool _pForward = false;
  bool _pBackward = false;
  bool _pLeft = false;
  bool _pRight = false;
  bool _pFire = false;
  bool _pInteract = false;
  bool _pBack = false;
  WeaponType? _pWeapon;

  /// Queues a raw terminal key sequence for the next engine frame.
  void handleKey(List<int> bytes) {
    // Escape sequences for arrow keys (CSI A/B/C/D) in raw terminal mode.
    if (bytes.length >= 3 && bytes[0] == 27 && bytes[1] == 91) {
      if (bytes[2] == 65) _pForward = true; // Up
      if (bytes[2] == 66) _pBackward = true; // Down
      if (bytes[2] == 67) _pRight = true; // Right
      if (bytes[2] == 68) _pLeft = true; // Left
      return;
    }

    // Bare Escape key is a menu back action.
    if (bytes.length == 1 && bytes[0] == 27) {
      _pBack = true;
      return;
    }

    // Enter maps to menu select/confirm.
    if (bytes.length == 1 && (bytes[0] == 13 || bytes[0] == 10)) {
      _pInteract = true;
      return;
    }

    String char = String.fromCharCodes(bytes).toLowerCase();

    if (char == 'w') _pForward = true;
    if (char == 's') _pBackward = true;
    if (char == 'a') _pLeft = true;
    if (char == 'd') _pRight = true;

    // Fire and interact stay on separate keys so the terminal host can avoid
    // ambiguous control sequences used by some shells and terminals.
    if (char == 'j') _pFire = true;
    if (char == ' ') _pInteract = true;

    if (char == '1') _pWeapon = WeaponType.knife;
    if (char == '2') _pWeapon = WeaponType.pistol;
    if (char == '3') _pWeapon = WeaponType.machineGun;
    if (char == '4') _pWeapon = WeaponType.chainGun;
  }

  @override
  void update() {
    // Promote buffered presses into the engine-visible state for this frame.
    isMovingForward = _pForward;
    isMovingBackward = _pBackward;
    isTurningLeft = _pLeft;
    isTurningRight = _pRight;
    isFiring = _pFire;
    isInteracting = _pInteract;
    isBack = _pBack;
    requestedWeapon = _pWeapon;

    // Reset the pending buffer so each keypress behaves like a frame impulse.
    _pForward = _pBackward = _pLeft = _pRight = _pFire = _pInteract = false;
    _pBack = false;
    _pWeapon = null;
  }
}
