--lua/syndrome-util.lua
--author expwnent
--some utilities for adding syndromes to units

local _ENV = mkmodule("syndrome-util")
local utils = require("utils")

function findUnitSyndrome(unit,syn_id)
 for index,syndrome in ipairs(unit.syndromes.active) do
  if syndrome['type'] == syn_id then
   return syndrome
  end
 end
 return nil
end

--usage: syndromeUtil.ResetPolicy.DoNothing, syndromeUtil.ResetPolicy.ResetDuration, etc
ResetPolicy = ResetPolicy or utils.invert({
 "DoNothing",
 "ResetDuration",
 "AddDuration",
 "NewInstance"
})

function eraseSyndromeData(unit,syndromeId,oldestFirst)
 for i = (oldestFirst and 0) or #unit.body.wounds-1,(oldestFirst and #unit.body.wounds-1) or 0,(oldestFirst and 1) or -1 do
  if unit.body.wounds[i].syndrome_id == syndromeId then
   unit.body.wounds:erase(i)
   break
  end
 end
end

function eraseSyndrome(unit,syndromeId,oldestFirst)
 local i1
 local iN
 local d
 if oldestFirst then
  i1 = 0
  iN = #unit.syndromes.active-1
  d = 1
 else
  i1 = #unit.syndromes.active-1
  iN = 0
  d = -1
 end
 local syndromes = unit.syndromes.active
 for i=i1,iN,d do
  if syndromes[i]['type'] == syndromeId then
   eraseSyndromeData(unit,syndromeId,oldestFirst)
   syndromes:erase(i)
   return true
  end
 end
 return false
end

local function eraseSyndromeClassHelper(unit,synclass)
 for i,unitSyndrome in ipairs(unit.syndromes.active) do
  local syndrome = df.syndrome.find(unitSyndrome.type)
  for _,class in ipairs(syndrome.syn_class) do
   if class.value == synclass then
    eraseSyndromeData(unit,syndrome.id)
    unit.syndromes.active:erase(i)
    return true
   end
  end
 end
 return false
end

function eraseSyndromeClass(unit,synclass)
 local count=0
 while eraseSyndromeClassHelper(unit,synclass) do
  count = count+1
 end
 return count
end

function eraseSyndromes(unit,syndromeId)
 local count=0
 while eraseSyndrome(unit,syndromeId,true) do
  count = count+1
 end
 return count
end

--target is a df.unit, syndrome is a df.syndrome, resetPolicy is one of syndromeUtil.ResetPolicy
--if the target has an instance of the syndrome already, the reset policy takes effect
--returns true if the unit did not have the syndrome before calling and false otherwise
function infectWithSyndrome(target,syndrome,resetPolicy)
 local oldSyndrome = findUnitSyndrome(target,syndrome.id)
 if oldSyndrome == nil or resetPolicy == nil or resetPolicy == ResetPolicy.NewInstance then
  local unitSyndrome = df.unit_syndrome:new()
  unitSyndrome.type = syndrome.id
  unitSyndrome.year = df.global.cur_year
  unitSyndrome.year_time = df.global.cur_year_tick
  unitSyndrome.ticks = 0
  unitSyndrome.wound_id = -1
  for k,v in ipairs(syndrome.ce) do
   local symptom = df.active_creature_interaction_effectst:new()
   symptom.quantity = 0
   symptom.delay = 0
   symptom.ticks = 0
   symptom.flags.active = true
   unitSyndrome.symptoms:insert("#",symptom)
  end
  target.syndromes.active:insert("#",unitSyndrome)
 elseif resetPolicy == ResetPolicy.DoNothing then
 elseif resetPolicy == ResetPolicy.ResetDuration then
  for k,symptom in ipairs(oldSyndrome.symptoms) do
   symptom.ticks = 0
  end
  oldSyndrome.ticks = 0
 elseif resetPolicy == ResetPolicy.AddDuration then
  for k,symptom in ipairs(oldSyndrome.symptoms) do
   --really it's syndrome.ce[k].end, but lua doesn't like that because keywords
   if syndrome.ce[k]["end"] ~= -1 then
    symptom.ticks = symptom.ticks - syndrome.ce[k]["end"]
   end
  end
 else qerror("Bad reset policy: " .. resetPolicy)
 end
 return (oldSyndrome == nil)
end

function isValidTarget(unit,syndrome)
 --mostly copied from itemsyndrome, which is based on autoSyndrome
 if
      #syndrome.syn_affected_class==0
  and #syndrome.syn_affected_creature==0
  and #syndrome.syn_affected_caste==0
  and #syndrome.syn_immune_class==0
  and #syndrome.syn_immune_creature==0
  and #syndrome.syn_immune_caste==0
 then
  return true
 end
 local affected = false
 local unitRaws = df.creature_raw.find(unit.race)
 local casteRaws = unitRaws.caste[unit.caste]
 local unitRaceName = unitRaws.creature_id
 local unitCasteName = casteRaws.caste_id
 local unitClasses = casteRaws.creature_class
 for _,unitClass in ipairs(unitClasses) do
  for _,syndromeClass in ipairs(syndrome.syn_affected_class) do
   if unitClass.value==syndromeClass.value then
    affected = true
   end
  end
 end
 for caste,creature in ipairs(syndrome.syn_affected_creature) do
  local affectedCreature = creature.value
  local affectedCaste = syndrome.syn_affected_caste[caste].value
  if affectedCreature == unitRaceName and (affectedCaste == unitCasteName or affectedCaste == "ALL") then
   affected = true
  end
 end
 for _,unitClass in ipairs(unitClasses) do
  for _,syndromeClass in ipairs(syndrome.syn_immune_class) do
   if unitClass.value == syndromeClass.value then
    affected = false
   end
  end
 end
 for caste,creature in ipairs(syndrome.syn_immune_creature) do
  local immuneCreature = creature.value
  local immuneCaste = syndrome.syn_immune_caste[caste].value
  if immuneCreature == unitRaceName and (immuneCaste == unitCasteName or immuneCaste == "ALL") then
   affected = false
  end
 end
 return affected
end

function infectWithSyndromeIfValidTarget(target,syndrome,resetPolicy)
 if isValidTarget(target,syndrome) then
  infectWithSyndrome(target,syndrome,resetPolicy)
  return true
 else
  return false
 end
end

return _ENV
