import 'dart:typed_data';

import 'package:test/test.dart';
import 'package:wolf_3d_dart/src/entities/entities/enemies/guard.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.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';

void main() {
  group('Map overlay rendering', () {
    test('software renderer draws fullscreen map overlay when enabled', () {
      final engine = _buildEngine();
      engine.init();
      expect(engine.pushwallManager.pushwalls.containsKey('5,5'), isTrue);
      engine.entities.add(
        Guard(
          x: 8.5,
          y: 8.5,
          angle: 0,
          mapId: MapObject.guardStart,
          difficulty: Difficulty.medium,
        ),
      );

      final renderer = SoftwareRenderer();
      final frameNormal = renderer.render(engine);
      final List<int> normalPixels = List<int>.from(frameNormal.pixels);

      engine.isMapOverlayVisible = true;
      final frameMap = renderer.render(engine);
      final List<int> mapPixels = List<int>.from(frameMap.pixels);

      int changedPixels = 0;
      for (int i = 0; i < mapPixels.length; i++) {
        if (normalPixels[i] != mapPixels[i]) {
          changedPixels++;
        }
      }

      final int hudProbeX = frameMap.width ~/ 2;
      final int hudProbeY = frameMap.height - 2;
      final int hudProbeIndex = (hudProbeY * frameMap.width) + hudProbeX;

      expect(changedPixels, greaterThan(mapPixels.length ~/ 5));
      expect(mapPixels.contains(ColorPalette.vga32Bit[6]), isTrue);
      expect(mapPixels.contains(ColorPalette.vga32Bit[12]), isTrue);
      expect(mapPixels.contains(ColorPalette.vga32Bit[15]), isTrue);
      expect(mapPixels.contains(ColorPalette.vga32Bit[2]), isTrue);
      expect(mapPixels[hudProbeIndex], equals(normalPixels[hudProbeIndex]));
    });

    test('software renderer does not apply bonus flash while menu is open', () {
      final input = _MutableInput();
      final engine = _buildEngine(input: input);
      engine.init();
      input.isBack = true;
      engine.tick(const Duration(milliseconds: 16));
      input.isBack = false;

      expect(engine.isMenuOpen, isTrue);

      final renderer = SoftwareRenderer();

      engine.player.bonusFlash = 0.0;
      final List<int> menuBaseline = List<int>.from(renderer.render(engine).pixels);

      engine.player.bonusFlash = 1.0;
      final List<int> menuWithFlash = List<int>.from(renderer.render(engine).pixels);

      expect(menuWithFlash, equals(menuBaseline));
    });
  });
}

class _StaticInput extends Wolf3dInput {
  @override
  void update() {
    isMovingForward = false;
    isMovingBackward = false;
    isTurningLeft = false;
    isTurningRight = false;
    isInteracting = false;
    isBack = false;
    isMapToggle = false;
    isFiring = false;
    requestedWeapon = null;
    menuTapX = null;
    menuTapY = null;
  }
}

class _MutableInput extends Wolf3dInput {
  @override
  void update() {}
}

WolfEngine _buildEngine({Wolf3dInput? input}) {
  final wallGrid = _buildGrid();
  final objectGrid = _buildGrid();

  _fillBoundaries(wallGrid, 2);
  wallGrid[2][4] = 1;
  wallGrid[2][5] = 90;
  wallGrid[5][5] = 1;
  objectGrid[2][2] = MapObject.playerEast;
  objectGrid[5][5] = MapObject.pushwallTrigger;
  objectGrid[10][10] = MapObject.normalExitTrigger;

  return WolfEngine(
    data: WolfensteinData(
      version: GameVersion.shareware,
      dataVersion: DataVersion.unknown,
      registry: RetailAssetRegistry(),
      walls: [
        _solidSprite(1),
        _solidSprite(1),
        _solidSprite(2),
        _solidSprite(2),
      ],
      sprites: List.generate(436, (_) => _solidSprite(255)),
      sounds: const [],
      adLibSounds: const [],
      music: const [],
      vgaImages: const [],
      episodes: [
        Episode(
          name: 'Episode 1',
          levels: [
            WolfLevel(
              name: 'Level 1',
              wallGrid: wallGrid,
              areaGrid: List.generate(64, (_) => List.filled(64, -1)),
              objectGrid: objectGrid,
              music: Music.level01,
            ),
          ],
        ),
      ],
    ),
    difficulty: Difficulty.medium,
    startingEpisode: 0,
    frameBuffer: FrameBuffer(96, 96),
    input: input ?? _StaticInput(),
    onGameWon: () {},
  );
}

SpriteMap _buildGrid() => List.generate(64, (_) => List.filled(64, 0));

void _fillBoundaries(SpriteMap grid, int wallId) {
  for (int i = 0; i < 64; i++) {
    grid[0][i] = wallId;
    grid[63][i] = wallId;
    grid[i][0] = wallId;
    grid[i][63] = wallId;
  }
}

Sprite _solidSprite(int colorIndex) {
  return Sprite(Uint8List.fromList(List.filled(64 * 64, colorIndex)));
}
