-- hack-wish.lua
-- Allows for script-based wishing.
-- author Putnam
-- edited by expwnent

function getGenderString(gender)
 local genderStr
 if gender==0 then
  genderStr=string.char(12)
 elseif gender==1 then
  genderStr=string.char(11)
 else
  return ""
 end
 return string.char(40)..genderStr..string.char(41)
end

function getCreatureList()
 local crList={}
 for k,cr in ipairs(df.global.world.raws.creatures.alphabetic) do
  for kk,ca in ipairs(cr.caste) do
   local str=ca.caste_name[0]
   str=str..' '..getGenderString(ca.gender)
   table.insert(crList,{str,nil,ca})
  end
 end
 return crList
end

function getMatFilter(itemtype)
 local itemTypes={
  SEEDS=function(mat,parent,typ,idx)
   return mat.flags.SEED_MAT
  end,
  PLANT=function(mat,parent,typ,idx)
   return mat.flags.STRUCTURAL_PLANT_MAT
  end,
  LEAVES=function(mat,parent,typ,idx)
   return mat.flags.LEAF_MAT
  end,
  MEAT=function(mat,parent,typ,idx)
   return mat.flags.MEAT
  end,
  CHEESE=function(mat,parent,typ,idx)
   return (mat.flags.CHEESE_PLANT or mat.flags.CHEESE_CREATURE)
  end,
  LIQUID_MISC=function(mat,parent,typ,idx)
   return (mat.flags.LIQUID_MISC_PLANT or mat.flags.LIQUID_MISC_CREATURE or mat.flags.LIQUID_MISC_OTHER)
  end,
  POWDER_MISC=function(mat,parent,typ,idx)
   return (mat.flags.POWDER_MISC_PLANT or mat.flags.POWDER_MISC_CREATURE)
  end,
  DRINK=function(mat,parent,typ,idx)
   return (mat.flags.ALCOHOL_PLANT or mat.flags.ALCOHOL_CREATURE)
  end,
  GLOB=function(mat,parent,typ,idx)
   return (mat.flags.STOCKPILE_GLOB)
  end,
  WOOD=function(mat,parent,typ,idx)
   return (mat.flags.WOOD)
  end,
  THREAD=function(mat,parent,typ,idx)
   return (mat.flags.THREAD_PLANT)
  end,
  LEATHER=function(mat,parent,typ,idx)
   return (mat.flags.LEATHER)
  end
 }
 return itemTypes[df.item_type[itemtype]] or getRestrictiveMatFilter(itemtype)
end

function getRestrictiveMatFilter(itemType)
 if not args.veryRestrictive then return nil else
  local itemTypes={
   WEAPON=function(mat,parent,typ,idx)
    return (mat.flags.ITEMS_WEAPON or mat.flags.ITEMS_WEAPON_RANGED)
   end,
   AMMO=function(mat,parent,typ,idx)
    return (mat.flags.ITEMS_AMMO)
   end,
   ARMOR=function(mat,parent,typ,idx)
    return (mat.flags.ITEMS_ARMOR)
   end,
   SHOES,SHIELD,HELM,GLOVES=ARMOR,ARMOR,ARMOR,ARMOR,
   INSTRUMENT=function(mat,parent,typ,idx)
    return (mat.flags.ITEMS_HARD)
   end,
   GOBLET,FLASK,TOY,RING,CROWN,SCEPTER,FIGURINE,TOOL=INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,
   AMULET=function(mat,parent,typ,idx)
    return (mat.flags.ITEMS_SOFT or mat.flags.ITEMS_HARD)
   end,
   EARRING,BRACELET=AMULET,AMULET,
   ROCK=function(mat,parent,typ,idx)
    return (mat.flags.IS_STONE)
   end,
   BOULDER=ROCK,
   BAR=function(mat,parent,typ,idx)
    return (mat.flags.IS_METAL or mat.flags.SOAP or mat.id==COAL)
   end
  }
  return itemTypes[df.item_type[itemType]]
 end
end

function createItem(mat,itemType,quality,pos,description)
 local item=df[df.item_type.attrs[itemType[1]].classname]:new()
 item.id=df.global.item_next_id
 df.global.world.items.all:insert('#',item)
 df.global.item_next_id=df.global.item_next_id+1
 item:setSubtype(itemType[2])
 item:setMaterial(mat[1])
 item:setMaterialIndex(mat[2])
 if df.item_type[itemType[1]]=='EGG' then
  local creature=df.creature_raw.find(mat[1])
  local eggMat={}
  eggMat[1]=dfhack.matinfo.find(creature.creature_id..':EGGSHELL')
  if eggMat[1] then
   eggMat[2]=dfhack.matinfo.find(creature.creature_id..':EGG_WHITE')
   eggMat[3]=dfhack.matinfo.find(creature.creature_id..'EGG_YOLK')
   for k,v in ipairs(eggMat) do
    item.egg_materials.mat_type:insert('#',v.type)
    item.egg_materials.mat_index:insert('#',v.index)
   end
  else
   eggMat=dfhack.matinfo.find(creature.creature_id..':MUSCLE')
   item.egg_materials.mat_type:insert('#',eggMat.type)
   item.egg_materials.mat_index:insert('#',eggMat.index)
  end
 end
 item:categorize(true)
 item.flags.removed=true
 item:setSharpness(1,0)
 item:setQuality(quality-1)
 if df.item_type[itemType[1]]=='SLAB' then
  item.description=description
 end
 dfhack.items.moveToGround(item,{x=pos.x,y=pos.y,z=pos.z})
end

--TODO: should this be a function?
function qualityTable()
 return {{'None'},
  {'-Well-crafted-'},
  {'+Finely-crafted+'},
  {'*Superior*'},
  {string.char(240)..'Exceptional'..string.char(240)},
  {string.char(15)..'Masterwork'..string.char(15)}
 }
end

local script=require('gui/script')
local guimaterials=require('gui.materials')

function showItemPrompt(text,item_filter,hide_none)
 guimaterials.ItemTypeDialog{
  prompt=text,
  item_filter=item_filter,
  hide_none=hide_none,
  on_select=script.mkresume(true),
  on_cancel=script.mkresume(false),
  on_close=script.qresume(nil)
 }:show()
 
 return script.wait()
end

function showMaterialPrompt(title, prompt, filter, inorganic, creature, plant) --the one included with DFHack doesn't have a filter or the inorganic, creature, plant things available
 guimaterials.MaterialDialog{
  frame_title = title,
  prompt = prompt,
  mat_filter = filter,
  use_inorganic = inorganic,
  use_creature = creature,
  use_plant = plant,
  on_select = script.mkresume(true),
  on_cancel = script.mkresume(false),
  on_close = script.qresume(nil)
 }:show()
 
 return script.wait()
end

function usesCreature(itemtype)
 typesThatUseCreatures={REMAINS=true,FISH=true,FISH_RAW=true,VERMIN=true,PET=true,EGG=true,CORPSE=true,CORPSEPIECE=true}
 return typesThatUseCreatures[df.item_type[itemtype]]
end

function getCreatureRaceAndCaste(caste)
 return df.global.world.raws.creatures.list_creature[caste.index],df.global.world.raws.creatures.list_caste[caste.index]
end

function hackWish(posOrUnit)
 local pos = df.unit:is_instance(posOrUnit) and posOrUnit.pos or posOrUnit
 script.start(function()
  --local amountok, amount
  local matok,mattype,matindex,matFilter
  local itemok,itemtype,itemsubtype=showItemPrompt('What item do you want?',function(itype) return df.item_type[itype]~='CORPSE' and df.item_type[itype]~='FOOD' end ,true)
  if not args.notRestrictive then
   matFilter=getMatFilter(itemtype)
  end
  if not usesCreature(itemtype) then
   matok,mattype,matindex=showMaterialPrompt('Wish','And what material should it be made of?',matFilter)
  else
   local creatureok,useless,creatureTable=script.showListPrompt('Wish','What creature should it be?',COLOR_LIGHTGREEN,getCreatureList())
   mattype,matindex=getCreatureRaceAndCaste(creatureTable[3])
  end
  local qualityok,quality=script.showListPrompt('Wish','What quality should it be?',COLOR_LIGHTGREEN,qualityTable())
  local description
  if df.item_type[itemtype]=='SLAB' then
   local descriptionok
   descriptionok,description=script.showInputPrompt('Slab','What should the slab say?',COLOR_WHITE)
  end
  --repeat amountok,amount=script.showInputPrompt('Wish','How many do you want? (numbers only!)',COLOR_LIGHTGREEN) until tonumber(amount)
  if mattype and itemtype then
   --for i=1,tonumber(amount) do
   createItem({mattype,matindex},{itemtype,itemsubtype},quality,pos,description)
   --end
  end
 end)
end

scriptArgs={...}

args={}

for k,v in ipairs(scriptArgs) do
 v=v:lower()
 if v=='startup' then args.startup=true end
 if v=='all' then args.notRestrictive=true end
 if v=='restrictive' then args.veryRestrictive=true end
 if v=='unit' then args.unitNum=args[k+1] end
 if v=='x' then args.x=args[k+1] end
 if v=='y' then args.y=args[k+1] end
 if v=='z' then args.z=args[k+1] end
end

eventful=require('plugins.eventful')

function posIsValid(pos)
 return pos.x~=-30000 and pos or false
end

if not args.startup then 
 local posOrUnit=args.x and {x=args.x,y=args.y,z=args.z} or args.unitNum and df.unit.find(args.unitNum) or posIsValid(df.global.cursor) or dfhack.gui.getSelectedUnit(true)
 hackWish(posOrUnit)
else
 eventful.onReactionComplete.hackWishP=function(reaction,unit,input_items,input_reagents,output_items, call_native)
  if not reaction.code:find('DFHACK_WISH') then return nil end
  hackWish(unit)
 end
end