-- Mart shop (engine/events/pokemart.asm DisplayPokemartDialogue_):
-- the BUY/SELL/QUIT menu loops until QUIT -- BUY and SELL keep it on
-- the stack underneath their list, and QUIT hands control back to the
-- caller (open_mart resumes its yielded script runner there).  Both
-- lists run in dialogue mode: the clerk speaks the real _Pokemart*
-- strings in the bottom text box with the money box top-right, then
-- the 1-99 quantity selector (DisplayChooseQuantityMenu) and a YES/NO
-- price confirm.  Key items and HMs can't be sold (.unsellableItem).

local Bag = require("src.inventory.Bag")
local ChoiceBox = require("src.ui.ChoiceBox")
local Font = require("src.render.Font")
local ListMenu = require("src.ui.ListMenu")
local Menu = require("src.ui.Menu")
local QuantityBox = require("src.ui.QuantityBox")
local Strings = require("src.core.Strings")
local TextBox = require("src.render.TextBox")
local romText = require("src.core.RomText")

local ShopMenu = {}

local function txt(game, key, fallback)
  return game.data.text[key] or fallback
end

-- engine/events/pokemart.asm:204 (.returnToMainPokemartMenu)
local function anythingElse(game)
  return txt(game, "_PokemartAnythingElseText",
             Strings("Is there anything\nelse I can do?"))
end

-- prompt refusals leave the list for the mart menu -- pokemart.asm:113
local function refuse(game, menu, list, text)
  if list then list:close() end
  game.stack:push(TextBox.new(game, text, function()
    menu.footer = anythingElse(game)
  end))
end

local function buy(game, stock, menu)
  local items = {}
  for _, id in ipairs(stock) do
    local def = game.data.items[id]
    if def then
      table.insert(items, {
        value = id,
        label = def.name,
        right = ("¥%d"):format(def.price),
      })
    end
  end
  local greet = txt(game, "_PokemartBuyingGreetingText", "Take your time.")
  local notEnough = txt(game, "_PokemartNotEnoughMoneyText",
                        Strings("You don't have\nenough money."))
  local bagFull = txt(game, "_PokemartItemBagFullText",
                      Strings("You can't carry\nany more items."))
  local list
  list = ListMenu.new(game, "BUY", items, {
    dialogue = true,
    money = function() return game.save.money end,
    footer = greet,
    onCancel = function() menu.footer = anythingElse(game) end,
    onChoose = function(item)
      local def = game.data.items[item.value]
      if game.save.money < def.price then
        list.footer = notEnough
        return
      end
      local affordable = math.min(99, math.floor(game.save.money / math.max(1, def.price)))
      game.stack:push(QuantityBox.new(game, {
        max = affordable,
        unitPrice = def.price,
        onDone = function(qty)
          if not qty then
            list.footer = greet
            return
          end
          local cost = qty * def.price
          -- _PokemartTellBuyPriceText + yes/no confirm
          list.footer = romText(game.data, "_PokemartTellBuyPriceText",
            "%s?\nThat will be\n¥%d. OK?", def.name, cost)
          game.stack:push(ChoiceBox.new(game, function(yes)
            if not yes then
              list.footer = greet
              return
            end
            if game.save.money < cost then
              refuse(game, menu, list, notEnough)
              return
            end
            if not Bag.add(game.save, item.value, qty, game.data) then
              refuse(game, menu, list, bagFull)
              return
            end
            game.save.money = game.save.money - cost
            -- SFX_PURCHASE drains before the receipt -- pokemart.asm:193
            game.stack:push(TextBox.new(game,
              txt(game, "_PokemartBoughtItemText",
                  Strings("Here you are!\nThank you!")),
              function() list.footer = greet end,
              { preSound = function()
                  return require("src.core.Sound").play(game.data, "Purchase")
                end }))
          end))
        end,
      }))
    end,
  })
  game.stack:push(list)
end

local function sell(game, menu)
  -- Sell list is ITEMLISTMENU with wPrintItemPrices cleared
  -- (pokemart.asm .sellMenuLoop): name + quantity only.  Price shows
  -- in the quantity chooser.  Stuffing "xN" into the label next to a
  -- right-aligned ¥ price made long names overlap (issue #116).
  local items = {}
  for _, id in ipairs(Bag.order(game.save)) do
    local def = game.data.items[id]
    table.insert(items, {
      value = id,
      label = def and def.name or id,
      right = "x" .. game.save.inventory[id],
    })
  end
  -- engine/events/pokemart.asm:50
  local greet = txt(game, "_PokemonSellingGreetingText",
                    Strings("What would you\nlike to sell?"))
  if #items == 0 then
    refuse(game, menu, nil, txt(game, "_PokemartItemBagEmptyText",
                                Strings("You don't have\nanything to sell.")))
    return
  end
  local unsellable = txt(game, "_PokemartUnsellableItemText",
                         Strings("I can't put a\nprice on that."))
  local list
  list = ListMenu.new(game, "SELL", items, {
    dialogue = true,
    money = function() return game.save.money end,
    footer = greet,
    onCancel = function() menu.footer = anythingElse(game) end,
    onSelectKey = function(item, l)
      if not item then return end
      if not l.swapIndex then
        l.swapIndex = l.index
        return
      end
      local order = Bag.order(game.save)
      order[l.swapIndex], order[l.index] = order[l.index], order[l.swapIndex]
      l.swapIndex = nil
      require("src.core.Sound").play(game.data, "Swap")
      local rebuilt = {}
      for _, id in ipairs(order) do
        local def = game.data.items[id]
        rebuilt[#rebuilt + 1] = {
          value = id,
          label = def and def.name or id,
          right = "x" .. game.save.inventory[id],
        }
      end
      l.items = rebuilt
    end,
    onChoose = function(item)
      local def = game.data.items[item.value]
      -- only key items and HMs are unsellable (pokemart.asm IsKeyItem /
      -- IsItemHM); zero-price items like ETHER sell for ¥0.  An unknown id
      -- (nil def) has no price, so treat it as unsellable too rather than
      -- indexing nil below -- guards saves that already picked up a bogus
      -- ITEM_NONE "0" from Blue's House before that pickup was fixed (#11).
      if not def or def.keyItem or item.value:find("^HM_") then
        refuse(game, menu, list, unsellable)
        return
      end
      local unit = math.floor(def.price / 2)
      game.stack:push(QuantityBox.new(game, {
        max = game.save.inventory[item.value] or 1,
        unitPrice = unit,
        onDone = function(qty)
          if not qty then
            list.footer = greet
            return
          end
          -- _PokemartTellSellPriceText + yes/no confirm
          list.footer = romText(game.data, "_PokemartTellSellPriceText",
            "I can pay you\n¥%d for that.", unit * qty)
          game.stack:push(ChoiceBox.new(game, function(yes)
            if not yes then
              list.footer = greet
              return
            end
            game.save.money = game.save.money + unit * qty
            -- home/inventory.asm:15 AddAmountSoldToMoney sounds SFX_PURCHASE
            require("src.core.Sound").play(game.data, "Purchase")
            Bag.remove(game.save, item.value, qty)
            local left = game.save.inventory[item.value]
            if left then
              item.right = "x" .. left
            else
              list:removeCurrent()
            end
            -- a sale prints nothing -- engine/events/pokemart.asm:112
            list.footer = greet
          end))
        end,
      }))
    end,
  })
  game.stack:push(list)
end

-- MONEY_BOX 11,0 (data/text_boxes.asm:35) over the greeting PrintText left
-- in the bottom box (home/text_script.asm:143)
local function drawClerk(menu)
  local game = menu.game
  love.graphics.setColor(1, 1, 1, 1)
  Font.drawBox(11, 0, 9, 3)
  love.graphics.setColor(0, 0, 0, 1)
  local money = ("¥%d"):format((game.save and game.save.money) or 0)
  Font.draw(money, 152 - Font.width(money), 8)
  love.graphics.setColor(1, 1, 1, 1)
  Font.drawBox(0, 12, 20, 6)
  love.graphics.setColor(0, 0, 0, 1)
  if menu.footer then
    local flat = {}
    for _, page in ipairs(TextBox.paginate(menu.footer)) do
      for _, line in ipairs(page) do flat[#flat + 1] = line end
    end
    local y = 112
    for i = math.max(1, #flat - 1), #flat do
      Font.draw(flat[i], 8, y)
      y = y + 16
    end
  end
  love.graphics.setColor(1, 1, 1, 1)
end

function ShopMenu.new(game, stock, onQuit)
  -- keepOpen: the mart menu stays underneath its list so closing the
  -- list lands back here; only QUIT (or B) leaves and fires onQuit
  local menu
  -- engine/events/pokemart.asm:220 .done, reached by QUIT and by B alike
  local function farewell()
    game.stack:push(TextBox.new(game,
      txt(game, "_PokemartThankYouText", "Thank you!"), onQuit))
  end
  menu = Menu.new(game, {
    { label = Strings("BUY"), keepOpen = true, onSelect = function() buy(game, stock, menu) end },
    { label = Strings("SELL"), keepOpen = true, onSelect = function() sell(game, menu) end },
    { label = Strings("QUIT"), onSelect = farewell },
  }, { tx = 0, ty = 0, tw = 8, th = 8 })
  menu.onCancel = farewell
  menu.footer = txt(game, "_PokemartGreetingText",
                    Strings("Hi there!\nMay I help you?"))
  menu.draw = function(self)
    drawClerk(self)
    Menu.draw(self)
  end
  return menu
end

return ShopMenu
