-- The START menu (engine/menus/start_menu.asm): entries appear as they
-- become usable -- POKéDEX once Oak gives it, POKéMON once you have any,
-- SAVE with a confirmation, plus ITEM / OPTION / LINK / QUIT.  The built
-- item list runs through the ui.start_menu.items hook before the menu
-- opens, so mods insert or remove rows without patching this file.

local Font = require("src.render.Font")
local Logger = require("src.core.Logger")
local Menu = require("src.ui.Menu")
local Renderer = require("src.render.Renderer")
local Runtime = require("src.mods.Runtime")
local Screens = require("src.ui.Screens")
local Strings = require("src.core.Strings")
local Theme = require("src.ui.Theme")

local StartMenu = {}

local function sameItems(_, items) return items end

function StartMenu.new(game)
  local flags = game.save.flags or {}
  local items = {}
  local menu

  -- vanilla start submenus return here on B (RedisplayStartMenu): the
  -- generic Menu pops the start menu when a row is selected, so each
  -- submenu gets an onCancel that re-opens it
  local function reopen() Screens.push(game, "StartMenu") end

  -- POKéDEX: only after Oak hands it over
  if flags.EVENT_GOT_POKEDEX then
    table.insert(items, { label = Strings("POKéDEX"), onSelect = function()
      Screens.push(game, "PokedexMenu", { onCancel = reopen })
    end })
  end

  -- POKéMON is always listed (draw_start_menu.asm prints it even with
  -- an empty party; selecting it then just no-ops)
  table.insert(items, { label = Strings("POKéMON"),
    keepOpen = #game.save.party == 0, onSelect = function()
    if #game.save.party == 0 then return end
    Screens.push(game, "PartyMenu", { onCancel = reopen })
  end })

  -- StartMenu_Item draws LIST_MENU_BOX over the still-drawn START menu and
  -- only redisplays it on the way out (start_sub_menus.asm:302-329) #1745
  table.insert(items, { label = Strings("ITEM"), keepOpen = true,
    onSelect = function()
      Screens.push(game, "BagMenu", { onClose = function()
        if menu and game.stack:top() == menu then game.stack:pop() end
      end })
    end })

  -- the player's name opens the trainer card (StartMenu_TrainerInfo)
  table.insert(items, { label = game.save.player.name or "RED",
    onSelect = function()
      Screens.push(game, "TrainerCard", { onCancel = reopen })
    end })

  -- SAVE shows the player/badges/dex/time panel then asks to confirm
  -- (PrintSaveScreenText); StartMenu_SaveReset never clears the START menu
  -- box, so it stays on screen beside the panel (start_sub_menus.asm:641-647)
  table.insert(items, { label = Strings("SAVE"), keepOpen = true,
    onSelect = function()
    local TextBox = require("src.render.TextBox")
    local badges = require("src.inventory.Badges").count(game.data, game.save)
    local owned = 0
    for _ in pairs(game.save.pokedex and game.save.pokedex.owned or {}) do
      owned = owned + 1
    end
    -- the panel is a static snapshot; the cart prints it once
    -- (main_menu.asm:390-401)
    local t = math.floor(game.save.playTime or 0)
    -- PrintSaveScreenText draws its own border at hlcoord 4,0 (b=8, c=$e) and
    -- leaves it up under the prompt -- engine/menus/main_menu.asm:381-405
    local panel
    panel = {
      -- the panel overlaps the kept-open START menu box (start_sub_menus.asm:
      -- 641-647), so neither can be docked to a screen edge on its own
      holdsUIAnchors = true,
      delay = 0,
      update = function()
        -- ld c, 30 / jp DelayFrames: the bare panel holds before the
        -- prompt (main_menu.asm:404-405)
        panel.delay = panel.delay + 1
        if panel.delay == 30 then panel.openPrompt() end
      end,
      draw = function()
        Font.drawBox(4, 0, 16, 10)
        love.graphics.setColor(0, 0, 0, 1)
        Font.draw(Strings("PLAYER"), 5 * 8, 2 * 8)
        Font.draw(game.save.player.name or "RED", 12 * 8, 2 * 8)
        Font.draw(Strings("BADGES"), 5 * 8, 4 * 8)
        Font.draw(("%2d"):format(badges), 17 * 8, 4 * 8)
        Font.draw(Strings("POKéDEX"), 5 * 8, 6 * 8)
        Font.draw(("%3d"):format(owned), 16 * 8, 6 * 8)
        Font.draw(Strings("TIME"), 5 * 8, 8 * 8)
        Font.draw(("%3d:%02d"):format(math.floor(t / 3600),
                                      math.floor(t / 60) % 60), 13 * 8, 8 * 8)
        love.graphics.setColor(1, 1, 1, 1)
      end,
    }
    local function closePanel()
      if game.stack:top() == panel then game.stack:pop() end
      -- SaveMenu returns into HoldTextDisplayOpen, not RedisplayStartMenu
      -- (start_sub_menus.asm:645-647): the kept-open START menu goes too
      if menu and game.stack:top() == menu then game.stack:pop() end
    end
    panel.openPrompt = function()
      game.stack:push(TextBox.new(game,
        Strings("Would you like to\nSAVE the game?"), nil, {
        -- SaveTheGame_YesOrNo pins its TWO_OPTION_MENU at hlcoord 0, 7 rather
        -- than the shared right-hand one -- engine/menus/save.asm:186-192
        choiceBox = Theme.saveBox,
        choice = function(yes)
          if not yes then closePanel() return end
          -- SaveMenu .save (engine/menus/save.asm:164-181): "Now saving..."
          -- is a bare PlaceString held by DelayFrames 120, then GameSavedText,
          -- which ends in `done` and so never reaches TX_PROMPT_BUTTON.
          -- Neither page takes a button press (#765); the second waits on
          -- SFX_SAVE (PlaySoundWaitForCurrent + WaitForSoundToFinish) and then
          -- DelayFrames 30.  The write itself is invisible either side of the
          -- "Now saving..." hold, so it stays on that box's onDone.
          game.stack:push(TextBox.new(game, Strings("Now saving..."), function()
            game:writeSave()
            game.stack:push(TextBox.new(game,
              Strings("%s saved\nthe game!", game.save.player.name or "RED"),
              closePanel, { auto = {
                sound = function()
                  return require("src.core.Sound").play(game.data, "Save")
                end,
                delay = 30,
              } }))
          end, { auto = { delay = 120 } }))
        end,
      }))
    end
    game.stack:push(panel)
  end })

  table.insert(items, { label = Strings("OPTION"), onSelect = function()
    Screens.push(game, "OptionsMenu", { onCancel = reopen })
  end })

  -- LINK needs a party
  if #game.save.party > 0 then
    table.insert(items, { label = Strings("LINK"), onSelect = function()
      local LinkState = require("src.link.LinkState")
      game.stack:push(LinkState.new(game))
    end })
  end

  -- the manager's pause-menu entry (18-mod-manager-ux): gated on at least
  -- one discovered mod so a vanilla install's menu is unchanged
  local status = game.modStatus
  if status and #(status.available or {}) > 0 then
    table.insert(items, { label = Strings("MODS"), onSelect = function()
      Screens.push(game, "ManagerState")
    end })
  end

  -- the original's EXIT just closed the menu (CloseStartMenu); with a
  -- window close button covering that, QUIT instead power-cycles back
  -- to the title after a confirm (defaultNo guards accidental quits)
  table.insert(items, { label = Strings("QUIT"), onSelect = function()
    local TextBox = require("src.render.TextBox")
    game.stack:push(TextBox.new(game, Strings("RETURN TO MAIN\nMENU?"), nil, {
      defaultNo = true,
      choice = function(yes)
        if yes then game:returnToTitle() end
      end,
    }))
  end })

  local hooked = Runtime.call("ui.start_menu.items", sameItems, game, items)
  if type(hooked) == "table" then
    items = hooked
  else
    Logger.error("ui.start_menu.items returned %s; keeping the vanilla items",
                 type(hooked))
  end

  -- the start menu's mask is PAD_DOWN | PAD_UP | PAD_START | PAD_B | PAD_A
  -- (engine/menus/draw_start_menu.asm), so START closes it back to the
  -- overworld -- unlike most menus, whose masks omit PAD_START.
  --
  -- item count isn't fixed: POKéDEX/LINK/MODS come and go with save state,
  -- and mods can append their own rows through the hook above, so the
  -- double-spaced box (the original's style) can grow past the 18-tile
  -- canvas. Cap it at however many rows actually fit and scroll the rest,
  -- with Menu's moreArrow showing while there's more below.
  local rowStep = 2
  local maxVisible = math.floor((Renderer.HEIGHT / 8 - 2) / rowStep)
  menu = Menu.new(game, items,
    -- the START menu hugs the top-right corner of the SCREEN, not of a
    -- centred letterbox: at 9,0 x 11 it is already flush with the top and
    -- right of the 20x18 grid, so the anchor keeps it flush when the view
    -- is zoomed out and the letterbox no longer fills the window
    { tx = 9, ty = 0, tw = 11, maxVisible = maxVisible, startCloses = true,
      anchor = "topright" })
  -- the cursor position survives closing the menu
  -- (wBattleAndStartSavedMenuItem, home/start_menu.asm)
  menu.index = math.min(game.startMenuIndex or 1, #items)
  menu:clampScroll()
  local baseUpdate = menu.update
  menu.update = function(self, dt)
    baseUpdate(self, dt)
    game.startMenuIndex = self.index
  end

  -- inside the Safari Zone the start menu also shows remaining steps and
  -- SAFARI BALLs (PrintSafariZoneSteps, engine/overworld/player_state.asm:
  -- 219-224): a 9x5 border at the top-left with "steps/500" and "BALL xx".
  -- It opens with `cp SAFARI_ZONE_EAST / ret c`, so only the nine interior
  -- maps ($D9..$E1) get it -- SAFARI_ZONE_GATE is $9C and falls under that
  -- early out, and used to show "502/500" while the player was still
  -- standing at the counter (#540).  Same map set as the step counter's
  -- (FieldDefaults safari.stepMaps via OverworldState:inSafariStepZone).
  local ow = game.overworld
  if game.save.safari and ow and ow.map and ow.inSafariStepZone
     and ow:inSafariStepZone() then
    local baseDraw = menu.draw
    menu.draw = function(self)
      baseDraw(self)
      local safari = game.save.safari
      Font.drawBox(0, 0, 9, 5)
      love.graphics.setColor(0, 0, 0, 1)
      Font.draw(("%3d"):format(math.floor(safari.steps or 0)), 8, 8)
      Font.draw("/500", 32, 8)
      Font.draw(Strings("BALL"), 8, 24)
      Font.draw(("%2d"):format(math.floor(safari.balls or 0)), 48, 24)
      love.graphics.setColor(1, 1, 1, 1)
    end
  end
  return menu
end

return StartMenu
