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

include "gamelib.plh"
include "globalDefs.plh"
include "godmode.plh"
include "playtype.plh"
include "gen_images.plh"
include "gen_modules.plh"
include "gen_items.plh"
include "gen_players.plh"
include "gen_flags.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 the header.
predef _cheatCmd(key)#1
predef _addItem()#1
predef _addPlayer()#1
word[] funcTbl = @_cheatCmd, @_addItem, @_addPlayer

predef parseDecWithDefault(str, default)#1

asm _defs

; Use hi-bit ASCII for Apple II
!convtab "../../include/hiBitAscii.ct"

; Headers
!source "../../include/global.i"
!source "../../include/plasma.i"
!source "../../include/mem.i"

end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Read a string from the keyboard, turn it into a PLASMA string and return a pointer to the string.
asm readStr()#1
  +asmPlasmRet 0
  bit setROM
  jsr ROM_getln1
  bit setLcRW+lcBank2
  txa
  pha
  beq +
- lda inbuf-1,x
  and #$7F
  sta inbuf,x
  dex
  bne -
+ pla
  sta inbuf,x
  lda #<inbuf
  ldy #>inbuf
  rts
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def testSoundGen()#0
  word pStr, n, dur, updelay, upvelo, upnoise, dndelay, dnvelo, dnnoise, print
  ^$C051; ^$C054
  updelay = 0
  upvelo  = 0
  dndelay = 0
  dnvelo  = 0
  upnoise = 0
  dnnoise = 0
  dur = 1000
  print = TRUE
  while TRUE
    if print
      puts("\n")
      printf3("Up: A:dly=%d B:velo=%d C:noise=%d\n", updelay, upvelo, upnoise)
      printf3("Dn: D:dly=%d E:velo=%d F:noise=%d\n", dndelay, dnvelo, dnnoise)
      printf1("Dur: G=%d\n", dur)
    fin
    puts("Noise cmd, e.g. 50G: ")
    pStr = readStr()
    if ^pStr >= 2
      print = TRUE
      n = parseDec(pStr)
      when charToUpper(^(pStr + ^pStr))
        is 'A'; updelay = n; break
        is 'B'; upvelo  = n; break
        is 'C'; upnoise = clampByte(n); break
        is 'D'; dndelay = n; break
        is 'E'; dnvelo  = n; break
        is 'F'; dnnoise = clampByte(n); break
        is 'G'; dur = n; break
        otherwise
          puts("Error.\n")
      wend
    else
      print = FALSE
      ^$25 = ^$25 - 1
    fin
    genSound(dnnoise, dnvelo, dndelay, upnoise, upvelo, updelay, dur)
  loop
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def parseDecWithDefault(str, default)#1
  if ^str == 0
    return default
  fin
  return parseDec(str)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def splitScreenMode()#0
  flipToPage1()
  textHome()
  ^$c053
  ^$25 = 19
  crout()
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Godmode cheats
def kbdTeleport()#1
  byte d3, num
  word x, y
  byte dir

  splitScreenMode()
  getPos(@x, @y)
  dir = getDir()
  printf3("Current: X=%d Y=%d Facing=%d\n", x, y, dir)

  d3 = mapIs3D
  num = mapNum
  printf1("3D [%d]: ", mapIs3D)
  d3 = parseDecWithDefault(readStr(), mapIs3D)
  if d3 > 1; d3 = 1; fin
  printf1("Map [%d]: ", mapNum)
  num = parseDecWithDefault(readStr(), mapNum)
  printf1("X [%d]  : ", x)
  x = parseDecWithDefault(readStr(), x)
  printf1("Y [%d] : ", y)
  y = parseDecWithDefault(readStr(), y)
  printf1("Facing [%d]: ", dir)
  dir = parseDecWithDefault(readStr(), dir)

  ^$c052

  queue_setMap(d3, num, x, y, dir)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def showPos()#1
  word x, y
  byte dir

  splitScreenMode()
  getPos(@x, @y)
  printf2("X=%d Y=%d ", x, y)
  if mapIs3D
    printf3("Facing=%d Sky=%d Ground=%d", getDir(), skyNum, groundNum)
  fin
  puts("\nHit any key.\n")
  rdkey()
  ^$c052
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def advPortrait(dir)#1
  while TRUE
    if dir < 0
      portraitNum = portraitNum <= 1 ?? PO_LAST :: portraitNum-1
    else
      portraitNum = portraitNum >= PO_LAST ?? 1 :: portraitNum+1
    fin
    if lookupResourcePart(3, portraitNum); break; fin
  loop
  setPortrait(portraitNum)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def nextPortrait()#1
  return advPortrait(1)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def prevPortrait()#1
  return advPortrait(-1)
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def testCombat#1
  word x, y
  getPos(@x, @y)
  checkEncounter(x, y, TRUE)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Switch to the next sky color (3D only)
def nextSky()#1
  setSky((skyNum + 1) % 18)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Switch to the next ground color (3D only)
def nextGround()#1
  setGround((groundNum + 1) % 18)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Edit flags cheat
def editFlags()#1
  word pModule, funcTbl, flagName, flagNum
  flipToPage1
  ^$c051
  *$4000 = 0 // so renderer knows we messed up the page
  mmgr(START_LOAD, 1) // code is in partition 1
  pModule = mmgr(QUEUE_LOAD, MOD_GEN_FLAGS<<8 | RES_TYPE_MODULE)
  mmgr(FINISH_LOAD, 0)
  funcTbl = pModule()
  while TRUE
    textHome()
    for flagNum = 0 to NUM_GAME_FLAGS-1
      flagName = funcTbl=>flags_nameForNumber(flagNum)
      if getGameFlag(flagNum); ^$32 = $3F; fin // inverse mode
      puts(flagName)
      ^$32 = $FF
      printChar(' ')
    next
    puts("\nToggle:             ")
    ^$24 = 8
    flagName = readStr()
    if !^flagName; break; fin
    flagNum = funcTbl=>flags_numberForName(flagName)
    if flagNum < 0; beep; continue; fin
    setGameFlag(flagNum, !getGameFlag(flagNum))
  loop
  ^$c050
  mmgr(FREE_MEMORY, pModule)
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
def _cheatCmd(key)#1
  when key
    is 'T'; kbdTeleport(); break
    is $10; showPos(); break // ctrl-P
    is '>'; nextPortrait(); break
    is '<'; prevPortrait(); break
    is '!'; testCombat(); break
    is 'Y'; nextSky(); break
    is 'G'; nextGround(); break
    is '^'; editFlags(); break
    is '*'; testSoundGen(); break
    otherwise fatal("gmcmd")
  wend
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Abstract code for adding from a list of things (players, items, etc.)
def selectThing(isPlayers, nThings, prompt)#1
  word pModule, funcTbl, p_thing, n_thing, n2

  flipToPage1()
  textHome()
  ^$c051

  for n_thing = 1 to nThings
    p_thing = isPlayers ?? createStruct(MOD_GEN_PLAYERS, n_thing<<1) :: createItem(n_thing)
    printf2("%d: %s\n", n_thing, p_thing=>s_name)
    if (n_thing % 22) == 0 or n_thing == nThings
      mmgr(HEAP_COLLECT, 0)
      puts(prompt)
      n2 = parseDecWithDefault(readStr(), 0)
      if n2 >= 1 and n2 <= nThings
        n_thing = n2
        p_thing = isPlayers ?? createStruct(MOD_GEN_PLAYERS, n_thing<<1) :: createItem(n_thing)
        printf1("Adding '%s'\n", p_thing=>s_name)
        pause(800)
        break
      else
        textHome()
      fin
    elsif isPlayers
      // Players are big, so need collect each time
      mmgr(HEAP_COLLECT, 0)
    fin
  next

  ^$c050
  heapCollect()
  return n_thing <= nThings ?? n_thing :: -1
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Add an item cheat
def _addItem()#1
  word itemNum, pItem
  itemNum = selectThing(FALSE, NUM_ITEMS, "Add item #: ")
  if itemNum >= 0
    pItem = createItem(itemNum)
    if pItem->t_type == TYPE_FANCY_ITEM
      pItem=>w_count = pItem=>w_storeAmount
    fin
    giveItemToParty(pItem, @rawDisplayStr)
    pause(400)
  fin
  return 0
end

///////////////////////////////////////////////////////////////////////////////////////////////////
// Add a player cheat
def _addPlayer()#1
  word playerNum
  playerNum = selectThing(TRUE, NUM_PLAYERS, "Add player #: ")
  if playerNum >= 0
    addPlayerToParty(playerNum<<1, @rawDisplayStr)
    pause(400)
  fin
  return 0
end

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