///////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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.
///////////////////////////////////////////////////////////////////////////////////////////////////

// Functions we import from the main game loop. If you find there are some over there that aren't
// yet exported, modify this header then add the mechanics at the top of gameloop.pla.
include "gamelib.plh"

// Data structure definitions
include "playtype.plh"

// Global definitions
include "globalDefs.plh"
include "gen_modules.plh"
include "gen_items.plh"

// Definition of constants for functions exported by this module
include "itemutil.plh"

// 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 _displayItemStats(pItem1, pItem2)#1
predef _displayItemName(pItem)#1
predef _reloadWeapon(pl, pWeapon, echo)#1
word[] funcTbl = @_displayItemStats, @_displayItemName, @_reloadWeapon

// Other global variables here

///////////////////////////////////////////////////////////////////////////////////////////////////
def tabTo(cursorX)#0
  rawDisplayf1("^T%D", cursorX)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def formatDice(encoded)#1
  byte nDice, dieSize, add
  nDice = encoded >> 12
  dieSize = (encoded >> 8) & $F
  add = encoded & $F
  rawDisplayf2("%d-%d", nDice+add, (nDice*dieSize)+add)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def formatNum(num)#1
  rawDisplayf1("%d", num)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def formatStr(str)#1
  rawDisplayStr(str)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def formatEquipped(num)#1
  if num == 1
    rawDisplayStr("yes")
  else
    rawDisplayStr("no")
  fin
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def formatAttack(code)#1
  if !code; return 0; fin
  if code == 1
    rawDisplayStr("single")
  elsif code == 2
    rawDisplayStr("double")
  else
    rawDisplayf1("%d-shot", code)
  fin
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def byteField(pItem, field)#1
  if pItem
    return ^(pItem + field)
  fin
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def equippedField(pItem, field)#1
  if pItem
    if ^(pItem + field) & ITEM_FLAG_EQUIP
      return 1
    else
      return 2
    fin
  fin
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def wordField(pItem, field)#1
  if pItem
    return *(pItem + field)
  fin
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayTwoCol(fieldName, pItem1, pItem2, field, fieldFunc, formatFunc)#0
  word val1, val2
  val1 = fieldFunc(pItem1, field)
  val2 = fieldFunc(pItem2, field)
  if val1 or val2
    rawDisplayf1("\n%s", fieldName)
    if val1; tabTo(STATS_COL_1); formatFunc(val1); fin
    if val2; tabTo(STATS_COL_2); formatFunc(val2); fin
  fin
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayWeaponStats(pItem1, pItem2)#0
  displayTwoCol("Equip'd",   pItem1, pItem2, b_flags,         @equippedField, @formatEquipped)
  displayTwoCol("Skill",     pItem1, pItem2, s_itemKind,      @wordField, @formatStr)
  displayTwoCol("Ammo",      pItem1, pItem2, s_ammoKind,      @wordField, @formatStr)
  displayTwoCol("Clip size", pItem1, pItem2, b_clipSize,      @byteField, @formatNum)
  if (pItem1->b_flags & ITEM_FLAG_EQUIP) and !pItem2
    displayTwoCol("Current", pItem1, pItem2, b_clipCurrent,   @byteField, @formatNum)
  fin
  displayTwoCol("Melee",     pItem1, pItem2, r_meleeDmg,      @wordField, @formatDice)
  displayTwoCol("Proj",      pItem1, pItem2, r_projectileDmg, @wordField, @formatDice)
  displayTwoCol("Attack",    pItem1, pItem2, ba_attacks+0,    @byteField, @formatAttack)
  displayTwoCol("Attck+",    pItem1, pItem2, ba_attacks+1,    @byteField, @formatAttack)
  displayTwoCol("Attck+",    pItem1, pItem2, ba_attacks+2,    @byteField, @formatAttack)
  displayTwoCol("Range",     pItem1, pItem2, b_weaponRange,   @byteField, @formatNum)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def displayArmorStats(pItem1, pItem2)#0
  displayTwoCol("Equip'd", pItem1, pItem2, b_flags,      @equippedField, @formatEquipped)
  displayTwoCol("Protec",  pItem1, pItem2, b_armorValue, @byteField, @formatNum)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def _displayItemStats(pItem1, pItem2)#1
  word pMod1, pMod2

  // First, show the item type and name
  when pItem1->t_type
    is TYPE_PLAIN_ITEM
    is TYPE_FANCY_ITEM;   rawDisplayStr("\nItem");   break
    is TYPE_WEAPON;       rawDisplayStr("\nWeapon"); break
    is TYPE_ARMOR;        rawDisplayStr("\nArmor");  break
    otherwise fatal("tItem")
  wend
  tabTo(STATS_COL_1); _displayItemName(pItem1)
  if pItem2
    tabTo(STATS_COL_2); _displayItemName(pItem2)
  fin

  // Type-specific attributes
  when pItem1->t_type
    is TYPE_WEAPON; displayWeaponStats(pItem1, pItem2); break
    is TYPE_ARMOR;  displayArmorStats(pItem1, pItem2);  break
  wend

  // If either item has modifiers, show them
  pMod1 = NULL
  if pItem1->t_type <> TYPE_PLAIN_ITEM; pMod1 = pItem1=>p_modifiers; fin
  pMod2 = NULL
  if pItem2
    if pItem2->t_type <> TYPE_PLAIN_ITEM; pMod2 = pItem2=>p_modifiers; fin
  fin
  if pMod1 or pMod2
    rawDisplayStr("\nSpecial")
    while pMod1 or pMod2
      if pMod1
        rawDisplayf1("^T%D", STATS_COL_1)
        if pMod1=>w_modValue >= 999; rawDisplayStr("Full "); else rawDisplayf1("%d ", pMod1=>w_modValue); fin
        rawDisplayStr(pMod1=>s_name)
        pMod1 = pMod1=>p_nextObj
      fin
      if pMod2
        rawDisplayf1("^T%D", STATS_COL_2)
        if pMod2=>w_modValue >= 999; rawDisplayStr("Full "); else rawDisplayf1("%d ", pMod2=>w_modValue); fin
        rawDisplayStr(pMod2=>s_name)
        pMod2 = pMod2=>p_nextObj
      fin
    loop
  fin
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// For non-countable items, display singular name.
// For countable "stuff" (e.g. ammo), display the count and appropriate singular or plural name.
def _displayItemName(pItem)#1
  isPlural = FALSE
  if pItem->t_type == TYPE_FANCY_ITEM and pItem=>w_count > 1
    isPlural = TRUE
    rawDisplayf1("%d ", pItem=>w_count)
  fin
  buildString()
  printf1("%s", pItem=>s_name) // need proper plural processing
  rawDisplayStr(finishString(isPlural))
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def ammoMatches(ammo, kind)
  if ammo->t_type <> TYPE_FANCY_ITEM; return FALSE; fin
  return streqi(ammo=>s_itemKind, kind)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Reloads the given weapon, as full as it can be given the player's ammo supply.
// Returns 1 if any text echo'd (or would have been echo'd)
def _reloadWeapon(pl, pWeapon, echo)#1
  word item
  byte orig, n

  isPlural = FALSE
  if !pWeapon; return 0; fin  // if no weapon equipped, no reload possible

  // If ammo type is null, it means weapon doesn't use ammo in traditional sense.
  if !pWeapon=>s_ammoKind
    pWeapon->b_clipCurrent = 1
    return 0
  fin

  // Find matching ammo
  ctx = pWeapon; item = first(pl=>p_items, &(p) ammoMatches(p, ctx=>s_ammoKind))
  if !item
    if echo and (pWeapon->b_clipCurrent < pWeapon->b_clipSize or !pWeapon->b_clipSize)
      displayf3("%s has no ammo to reload %s %s! ", pl=>s_name, hisHerTheir(pl->c_gender), pWeapon=>s_name))
    fin
    return 1
  fin

  // Transfer ammo to weapon
  if pWeapon->b_clipSize
    n = min(item=>w_count, pWeapon->b_clipSize - pWeapon->b_clipCurrent)
  else
    n = min(item=>w_count, 1) // clipSize=0 implies auto-reloading weapon
  fin
  if n > 0
    pWeapon->b_clipCurrent = pWeapon->b_clipCurrent + n
    item=>w_count = item=>w_count - n
    if echo
      displayf2("%s %sreloads ", pl=>s_name, pWeapon->b_clipCurrent < pWeapon->b_clipSize ?? "partially " :: "")
      displayf2("%s %s", hisHerTheir(pl->c_gender), pWeapon=>s_name)
      if item=>w_count == 0
        displayStr(" and is now out")
      fin
      displayStr(". ")
    fin
    if item=>w_count == 0
      removeFromList(@pl=>p_items, item)
    fin
  fin
  return 1
end

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