-- Probe: does WASD still walk CAMERA-RELATIVE on the 1ST/3RD rungs?
--
-- The regression report is "in first and third person the wasd keys now
-- move in cardinal directions". Cardinal means the yaw is not being
-- applied -- either FreeMove.tick is not the handler that ran (so the
-- engine's grid walk did, which is cardinal by construction), or it ran
-- and moveWorld got a yaw of zero.
--
-- So measure both: which handler took the frame, what the yaw was, and
-- which way the player actually travelled for a held W.
--
--   POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/freemove_probe.lua \
--   "/c/Program Files/LOVE/lovec.exe" .
return function(game)
  local U = dofile("tests/drivers/util.lua")
  local Pipelines = require("src.render.Pipelines")

  local handle = game.mods.exports["DRAMATIC_SHAPE"]
  if not (handle and handle.lib) then
    U.log("DRAMATIC_SHAPE is not loaded")
    return love.event.quit()
  end
  local V = handle.lib
  local FirstPerson = V.require("FirstPerson")
  local FreeMove = V.require("FreeMove")
  local Voxel = V.require("VoxelState")
  local ChunkMesher = V.require("ChunkMesher")

  require("src.world.OverworldController").rollEncounter = function() return nil end

  -- FirstPerson captures the mouse only with WINDOW FOCUS, and a driver
  -- window never has it -- so without this the look reads as dead for a
  -- reason that has nothing to do with the code under test. Force the
  -- answer it gates on; setRelativeMode then arms and the relative-motion
  -- wrap claims the deltas exactly as it would for a player.
  love.window.hasFocus = function() return true end

  -- count which walk handler actually takes the frames
  local ticks = 0
  local innerTick = FreeMove.tick
  FreeMove.tick = function(...)
    ticks = ticks + 1
    return innerTick(...)
  end

  U.teleport(game, "ROUTE_1", 5, 8, "down")
  U.wait(60)

  local function settle()
    for _ = 1, 900 do
      if ChunkMesher.pending() == 0 then break end
      U.wait(1)
    end
    for _ = 1, 300 do
      if FirstPerson.blend >= 1 and Voxel.ready then break end
      U.wait(1)
    end
    U.wait(30)
  end

  -- W is the UP button; the B button's key is "x" (see Input's
  -- DEFAULT_BINDINGS -- the driver must press KEYS, not button names, to
  -- exercise the real path)
  local function holdKey(k, frames)
    love.keypressed(k, k, false)
    U.wait(frames)
    love.keyreleased(k, k)
    U.wait(4)
  end

  for _, rung in ipairs({ { "1ST", Voxel.FP_LEVEL }, { "3RD", Voxel.TP_LEVEL } }) do
    Pipelines.setLevel("voxel", rung[2])
    settle()
    -- face EAST: yaw is the free-roam look, and a camera-relative W must
    -- then walk +X. A cardinal W walks -Y (north) whatever the camera does.
    FirstPerson.yaw = math.pi / 2
    U.wait(10)

    local ow = game.stack:top()
    local p = ow and ow.player
    if not p then U.log(rung[1] .. ": no player") break end
    local x0, y0 = p.px, p.py
    ticks = 0
    holdKey("w", 40)
    local dx, dy = p.px - x0, p.py - y0
    U.log(("%s: driving=%s freeMove ticks=%d yaw=%.2f  W moved dx=%.1f dy=%.1f")
          :format(rung[1], tostring(FirstPerson.driving()), ticks,
                  FirstPerson.yaw, dx, dy))
    local wx, wz = FirstPerson.moveWorld(0, 1)
    U.log(("%s: moveWorld(0,1) = %.2f,%.2f  (want a mostly-X vector at this yaw)")
          :format(rung[1], wx, wz))

    -- ------- and does the LOOK still turn?
    --
    -- A yaw that never moves is the same symptom from the player's seat:
    -- it stays at the cardinal angle the rung was entered on (FACING_ANGLE
    -- is one of four compass points), so W walks due north for ever and
    -- "wasd moves in cardinal directions" is exactly what it feels like.
    -- The capture mode's pointer wraps are installed OUTSIDE FirstPerson's,
    -- so this is the path that could have regressed.
    -- FirstPerson only claims relative motion while it has CAPTURED the
    -- mouse, and it captures only with window focus. A driver window that
    -- never got focus would show a dead look for a reason that has nothing
    -- to do with the code -- so record the discriminator rather than read
    -- a zero and blame the wrap.
    local okF, focus = pcall(function() return love.window.hasFocus() end)
    local okR, rel = pcall(function() return love.mouse.getRelativeMode() end)
    U.log(("%s: engaged=%s focus=%s relativeMode=%s")
          :format(rung[1], tostring(FirstPerson.engaged()),
                  okF and tostring(focus) or "?",
                  okR and tostring(rel) or "?"))

    local before = FirstPerson.yaw
    for _ = 1, 10 do
      love.mousemoved(400, 300, 12, 0, false)
      U.wait(1)
    end
    U.wait(4)
    U.log(("%s: mouse look -- yaw %.3f -> %.3f (delta %.3f)%s")
          :format(rung[1], before, FirstPerson.yaw, FirstPerson.yaw - before,
                  math.abs(FirstPerson.yaw - before) < 1e-6
                  and "   <-- THE LOOK IS DEAD" or ""))
  end

  -- ------- and now the way a PLAYER gets there: the "3" hotkey
  --
  -- Pipelines.setLevel above is the driver's shortcut. A player cycles the
  -- rung with 3, which goes through the mod's own cycleVoxel. If that
  -- leaves Voxel.level disagreeing with the pipeline's level, the camera
  -- can be first-person while FreeMove.engaged() says no -- and then the
  -- ENGINE's grid handler takes the frame, which walks cardinally. That is
  -- the reported symptom exactly, so it is worth entering the rung the
  -- same way the report did.
  Pipelines.setLevel("voxel", 0)
  U.wait(20)
  for i = 1, 8 do
    love.keypressed("3", "3", false)
    U.wait(3)
    love.keyreleased("3", "3")
    U.wait(12)
    local lvl = Pipelines.level("voxel")
    U.log(("hotkey 3 x%d -> pipeline level=%s  Voxel.level=%s  freeCam=%s "
           .. "engaged=%s driving=%s")
          :format(i, tostring(lvl), tostring(Voxel.level),
                  tostring(Voxel.isFreeCam(Voxel.level)),
                  tostring(FirstPerson.engaged()),
                  tostring(FirstPerson.driving())))
    if Voxel.isFreeCam(Voxel.level) then
      settle()
      local ow = game.stack:top()
      local p = ow and ow.player
      FirstPerson.yaw = math.pi / 2
      U.wait(6)
      local yaw0 = FirstPerson.yaw
      local x0, y0 = p.px, p.py
      ticks = 0
      holdKey("w", 30)
      U.log(("  walked from the HOTKEY rung: ticks=%d yaw=%.2f dx=%.1f dy=%.1f%s")
            :format(ticks, yaw0, p.px - x0, p.py - y0,
                    ticks == 0 and "   <-- ENGINE GRID WALK (cardinal)" or ""))
    end
  end

  FreeMove.tick = innerTick
  U.log("done")
end
