///////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1
// (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-1.1>.
// Unless required by applicable law or agreed to in writing, software distributed under
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, either express or implied. See the License for the specific language
// governing permissions and limitations under the License.
//////////////////f/////////////////////////////////////////////////////////////////////////////////

include "gamelib.plh"
include "playtype.plh"
include "globalDefs.plh"
include "itemutil.plh"
include "gen_modules.plh"
include "gen_items.plh"

// 9 rows per text line, minus 4 lines for header/footer
const PAGE_SIZE = (BIGWIN_HEIGHT / 9) - 4

const STATS_COL_1 = 45
const STATS_COL_2 = 140

// Exported functions go here. First a predef for each one, then a table with function pointers
// in the same order as the constants are defined in the header.
predef _buyFromStore(storeCode, profitPercent)#1
predef _sellToStore(profitPercent)#1
word[] funcTbl = @_buyFromStore, @_sellToStore

word pItemTblModule, pItemutilModule
const MAX_PAGE_ITEMS = 20 // should be plenty
word pageItems[MAX_PAGE_ITEMS]
word pagePrices[MAX_PAGE_ITEMS]
byte playerNum, playerCount
word pPlayer
word charmRatio

///////////////////////////////////////////////////////////////////////////////////////////////////
// Definitions used by assembly code
asm _defs
!convtab "../../include/hiBitAscii.ct"
!source "../../include/global.i"
!source "../../include/plasma.i"
!source "../../include/mem.i"
tmp         = $2
pTmp        = $4
ysav        = $34
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Find the end of a null-terminated array
def countArray(arr)#1
  byte count
  for count = 0 to 127
    if !*arr; break; fin
    arr = arr + 2
  next
  return count
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def loadExtraModules()#0
  mmgr(START_LOAD, 1) // code is in partition 1
  pItemTblModule = mmgr(QUEUE_LOAD, MOD_GEN_ITEM_TABLES<<8 | RES_TYPE_MODULE)
  pItemutilModule = mmgr(QUEUE_LOAD, MOD_ITEMUTIL<<8 | RES_TYPE_MODULE)
  mmgr(FINISH_LOAD, 0)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def unloadExtraModules()#0
  mmgr(FREE_MEMORY, pItemTblModule)
  mmgr(FREE_MEMORY, pItemutilModule)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayTitle(titleAction, columnAction, pageNum, nPages)#0
  clearWindow()
  rawDisplayf2("^I %s %s ", pPlayer=>s_name, titleAction)
  if (nPages > 1)
    rawDisplayf2("p. %d/%d ", pageNum+1, nPages)
  fin
  rightJustifyStr(sprintf1(" %d party gold ", global=>w_gold), BIGWIN_RIGHT - 15)
  rawDisplayf1("^N\n^V014^L%s^T046Price^T085Item^L", columnAction)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayItemLine(num)#0
  rawDisplayf2("\n  %c.^T046%d^T085", num + 'A', pagePrices[num])
  pItemutilModule()=>itemutil_displayItemName(pageItems[num])
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayPaging(action, nItems, pageNum, nPages)#0
  rawDisplayStr("\n^V164")
  if nItems or playerCount > 1
    if nItems
      rawDisplayf1("%s [A", action)
      if nItems > 1; rawDisplayf1("-%c", nItems-1+'A'); fin
      rawDisplayStr("], ")
    fin
    if nPages > 1
      rawDisplayStr("Pg [")
      if pageNum+1 < nPages; rawDisplayStr(">"); fin
      if pageNum; rawDisplayStr("<"); fin
      rawDisplayStr("], ")
    fin
    if playerCount > 1
      rawDisplayf1("Plyr [1-%d], ", playerCount)
    fin
    rawDisplayStr("or [Esc].")
  else
    rawDisplayStr("Press [Esc].")
  fin
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayBuyPage(pItemTbl, markupRatio, pageNum, nPages)#1
  byte numOnPage
  word pItemNum, pItem, price

  // Clear stuff from previous page
  heapCollect()

  displayTitle("buying", "Browse", pageNum, nPages)
  pItemNum = pItemTbl + ((pageNum*PAGE_SIZE) << 1)
  for numOnPage = 0 to PAGE_SIZE-1
    if !(*pItemNum); break; fin
    pItem = createItem(*pItemNum)
    pageItems[numOnPage] = pItem
    price = max(1, pItem=>w_price + addRatio(pItem=>w_price, markupRatio))
    price = max(1, price - addRatio(price, charmRatio))
    pagePrices[numOnPage] = price
    displayItemLine(numOnPage)
    pItemNum = pItemNum + 2
  next
  displayPaging("Browse", numOnPage, pageNum, nPages)
  return numOnPage
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayItemBrowse(pItem1, price, pItem2)#0
  clearWindow()
  rawDisplayf1("^T108^I Browse ^N\n\n^T%D^LMerchandise^L", STATS_COL_1)
  if pItem2
    rawDisplayf2("^T%D^L%s^L", STATS_COL_2, pPlayer=>s_name)
  fin
  pItemutilModule()=>itemutil_displayItemStats(pItem1, pItem2)
  rawDisplayf2("\n\nPrice^T%D%d", STATS_COL_1, price)
  rawDisplayf2("\nAvail^T%D%d", STATS_COL_1, global=>w_gold)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def matchEquipped(pMatch, nSkip)#1
  word pItem
  pItem = pPlayer=>p_items
  while pItem
    if (pItem->t_type == pMatch->t_type) and (pItem->t_type == TYPE_WEAPON or pItem->t_type == TYPE_ARMOR)
      if (pItem->b_flags & ITEM_FLAG_EQUIP)
        if pItem->t_type <> TYPE_ARMOR or (pItem=>s_itemKind == pMatch=>s_itemKind)
          if nSkip == 0
            return pItem
          fin
          nSkip = nSkip - 1
        fin
      fin
    fin
    pItem = pItem=>p_nextObj
  loop
  return NULL
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayItemMenu(price, hasNextComp)#0
  rawDisplayf1("\n\n[Esc]^T%D", STATS_COL_1)
  if price <= global=>w_gold
    rawDisplayStr("B)uy it")
  else
    rawDisplayStr("(too much)")
  fin
  if hasNextComp
    rawDisplayf1("^T%DN)ext compare", STATS_COL_2)
  fin
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayMsg(msg, doBeep, pauseAmt)#0
  rawDisplayf1("^V164^T000^C%s", msg)  // Vertical 164, Horz 0, Clear-to-end-of-line
  if doBeep; beep; fin
  if pauseAmt; pause(pauseAmt); fin
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def askQuantity(nMax, setSize)#1
  word num
  if nMax == 1; return 1; fin
  displayMsg("How many ", FALSE, 0) // no beep, no pause
  if setSize
    rawDisplayf1("sets of %d ", setSize)
  fin
  rawDisplayf1("(1-%d)? ", nMax)
  num = parseDec(getStringResponse())
  if num < 1 or num > nMax; return 0; fin
  return num
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Used by buy/sell. Different from the main getYN function in not clearing the text window
def storeAskYN()#1
  byte sel
  rawDisplayStr("? (Y/N)")
  sel = getUpperKey
  return sel == 'Y'
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def browseItem(num)#0
  word pItem, price, compSkip, pComp, quantity
  byte sel
  pItem = pageItems[num]
  price = pagePrices[num]
  compSkip = 0
  while TRUE
    displayItemBrowse(pItem, price, matchEquipped(pItem, compSkip))
    displayItemMenu(price, compSkip or matchEquipped(pItem, 1+compSkip))
    sel = getUpperKey()
    if sel == 'B'
      if price > global=>w_gold
        displayMsg("Need more gold.", True, 400) // beep, pause 400
        return
      fin

      displayMsg("Buy ", False, 0) // no beep, no pause
      pItemutilModule()=>itemutil_displayItemName(pItem)
      if !storeAskYN; return; fin

      pComp = scanForNamedObj(pPlayer=>p_items, pItem=>s_name)
      if pComp
        if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 0
          pComp=>w_count = min(30000, pComp=>w_count + pItem=>w_count)
        else
          displayMsg("Duplicate item.", TRUE, 400) // beep, pause 400
          return
        fin
      elsif roomInPack(pPlayer)
        addToList(@pPlayer=>p_items, pItem)
      else
        displayMsg("No room.", TRUE, 400) // beep, pause 400
        return
      fin
      global=>w_gold = global=>w_gold - price
      displayMsg("Done.", FALSE, 800) // no beep, pause 800
      break
    elsif sel == 'N' and (compSkip or matchEquipped(pItem, 1+compSkip))
      compSkip++
      if !matchEquipped(pItem, compSkip); compSkip = 0; fin
    elsif sel == $1B // Esc
      break
    else
      beep()
    fin
  loop
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def setPlayer(num)#0
  playerNum = num
  pPlayer = index(global=>p_players, playerNum)
  // Charisma discounts 3% per point when buying from a merchant; adds 3% when selling to merchant
  charmRatio = percentToRatio(pPlayer->b_charisma * 3)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def storeSetup()#0
  loadExtraModules()
  setBigWindow()

  playerCount = countList(global=>p_players)
  setPlayer(0)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def _buyFromStore(storeCode, profitPercent)#1
  word pItemTbl, choice, ratio
  byte nItemsOnPage, pageNum, nPages, redisplay

  storeSetup()
  pItemTbl = pItemTblModule()=>items_forStoreCode(storeCode)

  nPages = (countArray(pItemTbl) + PAGE_SIZE - 1) / PAGE_SIZE
  pageNum = 0

  ratio = percentToRatio(profitPercent) / 2  // half of profit on buying, half on selling

  redisplay = TRUE
  while TRUE
    if redisplay
      nItemsOnPage = displayBuyPage(pItemTbl, ratio, pageNum, nPages)
    fin
    choice = getUpperKey()
    redisplay = TRUE
    if choice == '<' and pageNum
      pageNum--
    elsif choice == '>' and pageNum+1 < nPages
      pageNum++
    elsif choice >= '1' and (choice-'1') < playerCount and (choice-'1') <> playerNum
      setPlayer(choice - '1')
    elsif choice >= 'A' and (choice-'A' < nItemsOnPage)
      browseItem(choice-'A')
    elsif choice == $1B // Esc
      break
    else
      beep()
      redisplay = FALSE
    fin
  loop

  unloadExtraModules()
  heapCollect()
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def iterateSellables(skipItems, markdownRatio)#1
  word pItem, itemsOnPage, totalItems, price
  byte ok
  itemsOnPage = 0
  totalItems = 0
  pItem = pPlayer=>p_items
  while pItem
    ok = pItem=>w_price > 0
    if pItem->t_type == TYPE_FANCY_ITEM
      ok = pItem=>w_count > 0 // too much trouble to figure out prices of stackables
    elsif pItem->t_type == TYPE_WEAPON or pItem->t_type == TYPE_ARMOR
      if pItem->b_flags & ITEM_FLAG_EQUIP; ok = FALSE; fin // don't sell equipped things
    fin
    if ok
      price = max(0, pItem=>w_price - addRatio(pItem=>w_price, markdownRatio))
      price = price + addRatio(price, charmRatio)
      if !price; ok = FALSE; fin
    fin
    if ok
      if totalItems >= skipItems and itemsOnPage < PAGE_SIZE
        pageItems[itemsOnPage] = pItem
        pagePrices[itemsOnPage] = price
        displayItemLine(itemsOnPage)
        itemsOnPage++
      fin
      totalItems++
    fin
    pItem = pItem=>p_nextObj
  loop
  if skipItems == 9999; return totalItems; fin
  return itemsOnPage
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displaySellPage(markdownRatio, pageNum, nPages)#1
  word nItems
  displayTitle("selling", "Sell", pageNum, nPages)
  nItems = iterateSellables(pageNum * PAGE_SIZE, markdownRatio)
  if !nItems
    rawDisplayStr("\n\nNothing to sell here.")
  fin
  displayPaging("Sell", nItems, pageNum, nPages)
  return nItems
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def sellItem(num)#0
  word pItem, price, quant, gain

  pItem = pageItems[num]
  price = pagePrices[num]

  displayMsg("Sell ", False, 0) // no beep, no pause
  pItemutilModule()=>itemutil_displayItemName(pItem)
  if !storeAskYN; return; fin

  // For countable items, sell in sets of w_storeAmount
  if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count
    if pItem=>w_count < pItem=>w_storeAmount
      displayMsg("Must have at least ", FALSE, 0)
      rawDisplayf1("%d to sell.", pItem=>w_storeAmount)
      beep; pause(800); return
    fin
    quant = pItem=>w_count / pItem=>w_storeAmount
    if quant > 1
      quant = askQuantity(quant, pItem=>w_storeAmount)
      if !quant; return; fin
    fin
    gain = quant * price
    if (quant * pItem=>w_storeAmount) < pItem=>w_count
      pItem=>w_count = pItem=>w_count - (quant * pItem=>w_storeAmount)
    else
      removeFromList(@pPlayer=>p_items, pItem)
    fin
  else
    gain = price
    removeFromList(@pPlayer=>p_items, pItem)
  fin

  gain = addGold(gain)
  displayMsg("Sold for ", FALSE, 0)
  rawDisplayf1("%d gold.", gain)
  pause(800)
  return
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def _sellToStore(profitPercent)#1
  word pItemTbl, choice, ratio
  byte nItemsOnPage, pageNum, totalItems, nPages, redisplay

  pageNum = 0
  storeSetup()

  ratio = percentToRatio(profitPercent) / 2 // half profit on buying, half on selling
  totalItems = iterateSellables(9999, 0)    // initialize count for paging calcs

  redisplay = TRUE
  while TRUE
    nPages = (totalItems + PAGE_SIZE - 1) / PAGE_SIZE // recalc each time since totalItems changes
    if redisplay
      nItemsOnPage = displaySellPage(ratio, pageNum, nPages)
    fin
    choice = getUpperKey()
    redisplay = TRUE
    if choice == '<' and pageNum
      pageNum--
    elsif choice == '>' and pageNum+1 < nPages
      pageNum++
    elsif choice >= '1' and (choice-'1') < playerCount and (choice-'1') <> playerNum
      setPlayer(choice - '1')
      totalItems = iterateSellables(9999, 0)
    elsif choice >= 'A' and (choice-'A' < nItemsOnPage)
      sellItem(choice-'A')
      totalItems = iterateSellables(9999, 0)
      if totalItems == 0
        break
      fin
      if pageNum > 0 and totalItems <= pageNum * PAGE_SIZE // sold last item on pg >= 2
        pageNum--
      fin
    elsif choice == $1B // Esc
      break
    else
      beep()
      redisplay = FALSE
    fin
  loop

  unloadExtraModules()
  heapCollect()
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Boilerplate module initialization code
return @funcTbl
done
