-- ../pokecrystal/engine/events/move_tutor.asm:1 MoveTutor, the submenu
-- FadeToMenu / ChooseMonToLearnTMHM / CloseSubmenu wraps, and :54
-- CheckCanLearnMoveTutorMove, whose refusals print into a Textbox over the
-- party list (:101-103 `menu_coords 0, 12, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1`).
--
-- ChooseMonToLearnTMHM (../pokecrystal/engine/items/tmhm.asm:73) is
-- InitPartyMenuWithCancel with PARTYMENUACTION_TEACH_TMHM, which is the list
-- src/ui/gen2/PartyMenu.lua already draws when it is handed `tmhm`.

local Chrome = require("src.ui.gen2.Chrome")
local Happiness = require("src.core.gen2.Happiness")
local Mon = require("src.battle.gen2.Mon")
local PartyMenu = require("src.ui.gen2.PartyMenu")
local Strings = require("src.core.Strings")

local MoveTutor = {}
MoveTutor.__index = MoveTutor
MoveTutor.isOpaque = true

-- ../pokecrystal/data/text/common_2.asm:187 _TMHMNotCompatibleText and
-- common_3.asm:1424 _KnowsMoveText.  CopyName1 leaves the MOVE in
-- wStringBuffer2 and GetNickname the nickname in wStringBuffer1.
local NOT_COMPATIBLE = Strings.source("%s is\nnot compatible\vwith %s.")
local KNOWS_MOVE = Strings.source("%s knows\n%s.")

-- CanLearnTMHMMove's bitfield: `add_mt` gives the three tutor moves TMHM
-- flags 58-60 (../pokecrystal/constants/item_constants.asm:295-307), which
-- the cache splits out as `tutorMoves`.
function MoveTutor.canLearn(species, moveId)
  if type(species) ~= "table" or not moveId then return false end
  for _, id in ipairs(species.tmhm or {}) do
    if id == moveId then return true end
  end
  for _, id in ipairs(species.tutorMoves or {}) do
    if id == moveId then return true end
  end
  return false
end

-- The same bitfield as a pokemon.lua view, for PartyMenu's ABLE / NOT ABLE
-- column (src/ui/gen2/PartyMenu.lua:735, which walks `tmhm` only).
function MoveTutor.speciesView(pokemon)
  local cache = {}
  return setmetatable({}, { __index = function(_, key)
    local hit = cache[key]
    if hit ~= nil then return hit or nil end
    local def = pokemon and pokemon[key]
    if type(def) ~= "table" then
      cache[key] = false
      return nil
    end
    local merged = {}
    for field, value in pairs(def) do merged[field] = value end
    local list = {}
    for _, id in ipairs(def.tmhm or {}) do list[#list + 1] = id end
    for _, id in ipairs(def.tutorMoves or {}) do list[#list + 1] = id end
    merged.tmhm = list
    cache[key] = merged
    return merged
  end })
end

-- opts: move, moveName, onDone(learned)
function MoveTutor.new(game, opts)
  opts = opts or {}
  local self = setmetatable({}, MoveTutor)
  self.game = game
  local data = (game and game.data) or {}
  self.move = opts.move
  self.moveName = opts.moveName or opts.move or "?"
  self.onDone = opts.onDone
  self.pokemon = opts.pokemon or data.pokemon
  self.view = MoveTutor.speciesView(self.pokemon)
  self.list = self:buildList()
  return self
end

function MoveTutor:wantsFillScale() return true end
function MoveTutor:drawsWidescreen() return true end

function MoveTutor:buildList()
  return PartyMenu.new(self.game, {
    prompt = "teach",
    tmhm = { move = self.move },
    pokemon = self.view,
    onChoose = function(_, mon) self:picked(mon) end,
    onCancel = function() self:finish(false) end,
  })
end

function MoveTutor:say(body, onDone)
  if self.game and self.game.say then return self.game:say(body, onDone) end
  if onDone then onDone() end
end

function MoveTutor:playSfx(name)
  local world = self.game and self.game.world
  if world and world.playSfxNamed then world:playSfxNamed(name) end
end

-- CheckCanLearnMoveTutorMove (../pokecrystal/engine/events/move_tutor.asm:54);
-- all three failures fall back into the list, `jr nc, .loop` at :24.
function MoveTutor:picked(mon)
  if not mon then return end
  local name = Mon.displayName(mon)
  if not MoveTutor.canLearn(self.view[mon.species], self.move) then
    self:playSfx("Sfx_Wrong")
    return self:say(Strings(NOT_COMPATIBLE, self.moveName, name))
  end
  -- ../pokecrystal/engine/pokemon/knows_move.asm:16 sets carry and prints,
  -- which is the same .didnt_learn arm.
  for _, move in ipairs(mon.moves or {}) do
    if move.id == self.move then
      return self:say(Strings(KNOWS_MOVE, name, self.moveName))
    end
  end
  local game = self.game
  if not (game and game.learnMoveOn) then return self:finish(false) end
  game:learnMoveOn(mon, self.move, function(learned)
    if not learned then return end
    -- :87-88 `ld c, HAPPINESS_LEARNMOVE / callfar ChangeHappiness`.  The 4000
    -- coins are the script's own takecoins (maps/GoldenrodCity.asm:124).
    Happiness.change(mon, "LEARNMOVE")
    self:finish(true)
  end)
end

function MoveTutor:finish(learned)
  if self.done then return end
  self.done = true
  local stack = self.game and self.game.stack
  if stack then stack:pop() end
  if self.onDone then self.onDone(learned) end
end

function MoveTutor:update(dt)
  if self.done then return end
  self.list:update(dt)
end

function MoveTutor:drawPanel()
  self.list:drawPanel()
end

function MoveTutor:draw()
  self.list:draw()
end

-- PartyMenu:drawWidescreen's own blit: the party page is white to the window
-- edge, at the one integer scale Chrome.fitScale picks.
function MoveTutor:drawWidescreen(winW, winH)
  local G = love.graphics
  Chrome.letterbox(winW, winH, 1, 1, 1)
  local scale = Chrome.fitScale(winW, winH)
  G.push()
  G.translate(Chrome.fitOrigin(winW, winH, scale))
  G.scale(scale, scale)
  self:drawPanel()
  G.pop()
end

return MoveTutor
