/// Active gameplay screen for the Flutter host.
library;

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:window_manager/window_manager.dart';
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
import 'package:wolf_3d_dart/wolf_3d_input.dart';
import 'package:wolf_3d_dart/wolf_3d_renderer.dart';
import 'package:wolf_3d_flutter/renderer_settings_persistence_flutter.dart';
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
import 'package:wolf_3d_flutter/wolf_3d_input_flutter.dart';
import 'package:wolf_3d_gui/screens/debug_tools_screen.dart';
import 'package:wolf_3d_renderer/wolf_3d_ascii_renderer.dart';
import 'package:wolf_3d_renderer/wolf_3d_flutter_renderer.dart';
import 'package:wolf_3d_renderer/wolf_3d_glsl_renderer.dart';

enum RendererMode {
  /// Software pixel renderer presented via decoded framebuffer images.
  software,

  /// Text-mode renderer for debugging and retro terminal aesthetics.
  ascii,

  /// GLSL renderer with optional CRT-style post processing.
  hardware,
}

/// Semantic actions that host-level shortcuts can trigger.
///
/// These intents are intentionally UI-host focused (windowing, app shell), not
/// engine gameplay actions. The engine continues to receive input through
/// [Wolf3dFlutterInput].
enum HostShortcutIntent {
  /// Toggle desktop fullscreen on/off.
  toggleFullscreen,
}

/// Declarative mapping from a key pattern to a host shortcut intent.
///
/// [matches] identifies whether a key event should trigger this binding.
/// [suppressedActions] are one-frame engine actions that should be blocked
/// when the binding is consumed (for example, blocking `interact` on Alt+Enter
/// so Enter does not activate menu selections).
class HostShortcutBinding {
  /// Predicate that returns true when this shortcut should fire.
  final bool Function(KeyEvent event) matches;

  /// Host operation to perform when [matches] succeeds.
  final HostShortcutIntent intent;

  /// Engine actions to suppress for a single input update tick.
  final Set<WolfInputAction> suppressedActions;

  /// Creates a host shortcut binding with optional suppressed engine actions.
  const HostShortcutBinding({
    required this.matches,
    required this.intent,
    this.suppressedActions = const <WolfInputAction>{},
  });
}

/// Ordered set of host shortcut bindings.
///
/// The first binding whose [HostShortcutBinding.matches] returns true wins.
/// This keeps behavior deterministic when multiple shortcuts could overlap.
class HostShortcutRegistry {
  /// Ordered bindings consulted for each incoming key-down event.
  final List<HostShortcutBinding> bindings;

  /// Creates a registry with explicit [bindings].
  const HostShortcutRegistry({
    required this.bindings,
  });

  /// Returns the first binding that matches [event], or null when none do.
  HostShortcutBinding? match(KeyEvent event) {
    for (final HostShortcutBinding binding in bindings) {
      if (binding.matches(event)) {
        return binding;
      }
    }
    return null;
  }

  /// Default host shortcuts used by [GameScreen].
  ///
  /// Alt+Enter toggles fullscreen and suppresses the engine `interact` action
  /// for one frame so Enter does not also activate menu/game interactions.
  static const HostShortcutRegistry defaults = HostShortcutRegistry(
    bindings: <HostShortcutBinding>[
      HostShortcutBinding(
        matches: _isAltEnterShortcut,
        intent: HostShortcutIntent.toggleFullscreen,
        suppressedActions: <WolfInputAction>{WolfInputAction.interact},
      ),
    ],
  );
}

/// Optional imperative host shortcut override.
///
/// Return true when the event was fully handled. The handler receives the
/// shared [Wolf3dFlutterInput] so it can suppress engine actions as needed.
typedef HostShortcutHandler =
    bool Function(
      KeyEvent event,
      Wolf3dFlutterInput input,
    );

/// Launches a [WolfEngine] via [Wolf3d] and exposes renderer/input integrations.
class GameScreen extends StatefulWidget {
  /// Shared application facade owning the engine, audio, and input.
  final Wolf3d wolf3d;

  /// Optional host-level shortcut override.
  ///
  /// Return `true` when the event was consumed. Handlers may call
  /// [Wolf3dFlutterInput.suppressActionOnce] to keep actions from reaching the
  /// engine update loop.
  final HostShortcutHandler? hostShortcutHandler;

  /// Declarative host shortcut registry used when [hostShortcutHandler] is null.
  final HostShortcutRegistry hostShortcutRegistry;

  /// Creates a gameplay screen driven by [wolf3d].
  const GameScreen({
    required this.wolf3d,
    this.hostShortcutHandler,
    this.hostShortcutRegistry = HostShortcutRegistry.defaults,
    super.key,
  });

  @override
  State<GameScreen> createState() => _GameScreenState();
}

class _GameScreenState extends State<GameScreen> {
  late final WolfEngine _engine;
  final FlutterRendererSettingsPersistence _persistence =
      FlutterRendererSettingsPersistence();

  /// Mirrors [WolfRendererSettings.mode] into the Flutter renderer enum.
  RendererMode _rendererMode = RendererMode.hardware;

  @override
  void initState() {
    super.initState();
    const Set<WolfRendererMode> supportedModes = <WolfRendererMode>{
      WolfRendererMode.hardware,
      WolfRendererMode.software,
      WolfRendererMode.ascii,
    };
    _engine = widget.wolf3d.launchEngine(
      rendererCapabilities: const WolfRendererCapabilities(
        supportedModes: supportedModes,
        supportsAsciiThemes: true,
        supportsHardwareEffects: true,
        supportsFpsCounter: true,
      ),
      rendererSettings: const WolfRendererSettings(
        mode: WolfRendererMode.hardware,
      ),
      onRendererSettingsChanged: (settings) {
        unawaited(_persistence.save(settings));
        if (mounted) {
          setState(() {
            _syncRendererModeFrom(settings);
          });
        }
      },
      onGameWon: () {
        _engine.difficulty = null;
        widget.wolf3d.clearActiveDifficulty();
        Navigator.of(context).pop();
      },
      onQuit: () {
        SystemNavigator.pop();
      },
    );
    _syncRendererModeFrom(_engine.rendererSettings);
    _loadPersistedSettings();
  }

  Future<void> _loadPersistedSettings() async {
    final WolfRendererSettings? saved = await _persistence.load();
    if (saved != null && mounted) {
      _engine.updateRendererSettings(saved);
    }
  }

  void _syncRendererModeFrom(WolfRendererSettings settings) {
    switch (settings.mode) {
      case WolfRendererMode.hardware:
        _rendererMode = RendererMode.hardware;
        break;
      case WolfRendererMode.software:
        _rendererMode = RendererMode.software;
        break;
      case WolfRendererMode.ascii:
      case WolfRendererMode.sixel:
        _rendererMode = RendererMode.ascii;
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: _engine.difficulty != null,
      onPopInvokedWithResult: (didPop, _) {
        if (!didPop && _engine.difficulty == null) {
          widget.wolf3d.input.queueBackAction();
        }
      },
      child: Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: _openDebugTools,
          tooltip: 'Open Debug Tools',
          child: const Icon(Icons.bug_report),
        ),
        body: LayoutBuilder(
          builder: (context, constraints) {
            return Listener(
              onPointerDown: (event) {
                widget.wolf3d.input.onPointerDown(event);
              },
              onPointerUp: widget.wolf3d.input.onPointerUp,
              onPointerMove: widget.wolf3d.input.onPointerMove,
              onPointerHover: widget.wolf3d.input.onPointerMove,
              child: Stack(
                children: [
                  _buildRenderer(),

                  if (!_engine.isInitialized)
                    Container(
                      color: Colors.black,
                      child: const Center(
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            CircularProgressIndicator(color: Colors.teal),
                            SizedBox(height: 20),
                            Text(
                              "GET PSYCHED!",
                              style: TextStyle(
                                color: Colors.teal,
                                fontFamily: 'monospace',
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),

                  // A second full-screen overlay keeps the presentation simple while
                  // the engine is still warming up or decoding the first frame.
                  if (!_engine.isInitialized)
                    Container(
                      color: Colors.black,
                      child: const Center(
                        child: CircularProgressIndicator(color: Colors.teal),
                      ),
                    ),
                ],
              ),
            );
          },
        ),
      ),
    );
  }

  Widget _buildRenderer() {
    // Keep all renderers behind the same engine so mode switching does not
    // reset level state or audio playback.
    final WolfRendererSettings settings = _engine.rendererSettings;
    switch (_rendererMode) {
      case RendererMode.software:
        return WolfFlutterRenderer(
          engine: _engine,
          onKeyEvent: _handleRendererKeyEvent,
        );
      case RendererMode.ascii:
        final AsciiTheme theme =
            settings.asciiThemeId == WolfRendererSettings.asciiThemeQuadrant
            ? AsciiThemes.quadrant
            : AsciiThemes.blocks;
        return WolfAsciiRenderer(
          engine: _engine,
          theme: theme,
          onKeyEvent: _handleRendererKeyEvent,
        );
      case RendererMode.hardware:
        return WolfGlslRenderer(
          engine: _engine,
          effectsEnabled: settings.hardwareEffectsEnabled,
          onKeyEvent: _handleRendererKeyEvent,
          onUnavailable: _onGlslUnavailable,
        );
    }
  }

  void _handleRendererKeyEvent(KeyEvent event) {
    if (event is! KeyDownEvent) {
      return;
    }

    // Host shortcuts must be processed before game actions so they can
    // suppress overlapping keys (for example Alt+Enter consuming Enter).
    if (_handleHostShortcut(event)) {
      return;
    }

    if (event.logicalKey == widget.wolf3d.input.rendererToggleKey) {
      _engine.cycleRendererMode();
      return;
    }

    if (event.logicalKey == widget.wolf3d.input.fpsToggleKey) {
      setState(() => _engine.toggleFpsCounter());
      return;
    }

    if (event.logicalKey == widget.wolf3d.input.asciiThemeCycleKey) {
      if (_rendererMode == RendererMode.ascii) {
        _engine.cycleAsciiTheme();
      } else if (_rendererMode == RendererMode.hardware) {
        _engine.toggleHardwareEffects();
      }
    }
  }

  void _onGlslUnavailable() {
    if (!mounted || _rendererMode != RendererMode.hardware) {
      return;
    }
    _engine.updateRendererSettings(
      _engine.rendererSettings.copyWith(mode: WolfRendererMode.software),
    );
  }

  void _openDebugTools() {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => DebugToolsScreen(wolf3d: widget.wolf3d),
      ),
    );
  }

  bool _handleHostShortcut(KeyEvent event) {
    final HostShortcutHandler? customHandler = widget.hostShortcutHandler;
    if (customHandler != null) {
      // Custom handlers take full precedence to support future menu-driven
      // rebinding/override systems without modifying this screen.
      return customHandler(event, widget.wolf3d.input);
    }

    final HostShortcutBinding? binding = widget.hostShortcutRegistry.match(
      event,
    );
    if (binding == null) {
      return false;
    }

    // Suppress conflicting gameplay/menu actions for one update frame.
    for (final WolfInputAction action in binding.suppressedActions) {
      widget.wolf3d.input.suppressActionOnce(action);
    }

    switch (binding.intent) {
      case HostShortcutIntent.toggleFullscreen:
        unawaited(_toggleFullscreen());
    }

    return true;
  }

  /// Toggles desktop fullscreen state when supported by the host platform.
  ///
  /// This no-ops on unsupported targets and safely ignores missing plugin
  /// hosts to keep gameplay input resilient in embedded/test environments.
  Future<void> _toggleFullscreen() async {
    if (!_supportsDesktopWindowing) {
      return;
    }

    try {
      final bool isFullScreen = await windowManager.isFullScreen();
      await windowManager.setFullScreen(!isFullScreen);
    } on MissingPluginException {
      // No-op on hosts where the window manager plugin is unavailable.
    }
  }

  /// Whether runtime desktop window management APIs are expected to work.
  bool get _supportsDesktopWindowing {
    if (kIsWeb) {
      return false;
    }

    return switch (defaultTargetPlatform) {
      TargetPlatform.linux ||
      TargetPlatform.windows ||
      TargetPlatform.macOS => true,
      _ => false,
    };
  }
}

/// Returns true when [event] is Enter/NumpadEnter while Alt is pressed.
bool _isAltEnterShortcut(KeyEvent event) {
  final bool isEnter =
      event.logicalKey == LogicalKeyboardKey.enter ||
      event.logicalKey == LogicalKeyboardKey.numpadEnter;
  if (!isEnter) {
    return false;
  }

  final Set<LogicalKeyboardKey> pressedKeys =
      HardwareKeyboard.instance.logicalKeysPressed;
  return pressedKeys.contains(LogicalKeyboardKey.altLeft) ||
      pressedKeys.contains(LogicalKeyboardKey.altRight) ||
      pressedKeys.contains(LogicalKeyboardKey.alt);
}
