local LD = require("design.LevelDesignLibrary")
local debugobject = require("core.debugobject")
local color = require("core.color")
local STATE_INIT = 0
local STATE_BROADCAST = 1
local STATE_INTERACT = 2
local STATE_PUPPET_DEAD = 3
local STATE_DONE = 4
local STATE_DISABLE = 5
local STATE_INTERRUPTED = 6
local thisObj, thisLevel, player, son
local current_state = STATE_INIT
local puppeteer, puppet
local timer = 0
local puppeteerTable = {}
local startEnabled, approachSpeed, startEventsDelay, arrivalDistance, interactCompleteEvent, onArrivalEvent, interactInterruptedEvent, onEngagedEvent, movBranch, creatureName, poiAdName
local interruptRestartTime = 8
local participantNums = 0
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  startEnabled = startEnabled
  approachSpeed = approachSpeed
  startEventsDelay = startEventsDelay
  arrivalDistance = arrivalDistance
  interactCompleteEvent = interactCompleteEvent
  interactInterruptedEvent = interactInterruptedEvent
  onEngagedEvent = onEngagedEvent
  movBranch = movBranch
  creatureName = creatureName
  poiAdName = poiAdName
  if obj:FindLuaTableAttribute("Event_InteractComplete") ~= nil then
    interactCompleteEvent = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("Event_InteractComplete"))
  end
  if obj:FindLuaTableAttribute("Event_OnArrival") ~= nil then
    onArrivalEvent = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("Event_OnArrival"))
  end
  if obj:FindLuaTableAttribute("startEventsDelay") ~= nil then
    startEventsDelay = obj:FindLuaTableAttribute("startEventsDelay")
  end
  if obj:FindLuaTableAttribute("Event_InteractInterrupted") ~= nil then
    interactInterruptedEvent = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("Event_InteractInterrupted"))
  end
  if obj:FindLuaTableAttribute("Event_OnEngaged") ~= nil then
    onEngagedEvent = LD.ExtractCallbacksForEvent(level, obj, obj:FindLuaTableAttribute("Event_OnEngaged"))
  end
  if obj:FindLuaTableAttribute("approachSpeed") ~= nil then
    approachSpeed = obj:FindLuaTableAttribute("approachSpeed")
  end
  if obj:FindLuaTableAttribute("arrivalDistance") ~= nil then
    arrivalDistance = obj:FindLuaTableAttribute("arrivalDistance")
  end
  if obj:FindLuaTableAttribute("movBranch") ~= nil then
    movBranch = obj:FindLuaTableAttribute("movBranch")
  end
  if obj:FindLuaTableAttribute("creatureName") ~= nil then
    creatureName = obj:FindLuaTableAttribute("creatureName")
  end
  if obj:FindLuaTableAttribute("poiAdName") ~= nil then
    poiAdName = obj:FindLuaTableAttribute("poiAdName")
  end
  if obj:FindLuaTableAttribute("startEnabled") ~= nil then
    startEnabled = obj:FindLuaTableAttribute("startEnabled")
  end
  if obj:FindLuaTableAttribute("interruptRestartTime") ~= nil then
    interruptRestartTime = obj:FindLuaTableAttribute("interruptRestartTime")
  end
  if startEnabled == false then
    current_state = STATE_DISABLE
  end
  local self_debug = debugobject.Create(thisObj)
  self_debug.poiState = current_state
  self_debug.bVFS_ShowDebug = engine.VFSBool.New("Show AI Interact State")
  InitPuppeteer()
end
function OnUpdate(level, obj)
  UpdateState()
  if engine.IsDebug() then
    local self_debug = debugobject.Get(thisObj)
    if self_debug.bVFS_ShowDebug.value == true then
      DrawDebug()
    end
  end
end
function GetValidPuppet()
  for creature in game.Creature.IterateAllCreatures() do
    if creature:GetName() == creatureName then
      local distance = creature:GetWorldPosition()
      if distance:Distance(thisObj:GetWorldPosition()) < 20 then
        return creature
      end
    end
  end
  return nil
end
function IsPuppetDead()
  if puppet ~= nil and puppet:GetHitPoints() <= 0 then
    return true
  end
  return false
end
function InitPuppeteer()
  for _ = 1, 1 do
    local pup = {}
    pup.currentState = STATE_INIT
    pup.poi = nil
    table.insert(puppeteerTable, pup)
  end
end
function UpdateState()
  if current_state == STATE_INIT then
    if puppeteer ~= nil then
      puppeteer:Clear()
      puppeteer = nil
    end
    puppeteer = game.Puppeteer.NewEngagement(thisObj, "Enemy Interact: " .. thisObj.Parent:GetName())
    puppeteer:Advertise(GetCreaturePOIAdvertise())
    puppeteer:SetEvent(poiAdName)
    puppeteer:OnEvent(OnHitReaction, "kEHitReaction")
    puppeteer:OnEngaged(function()
      local creature = puppeteer.Puppet
      if creature:IsDoingSyncMove() then
        print("|||| Skipping puppeteer, creature is N-sync!")
        OnHitReaction()
        return
      else
        GotoPOI()
      end
    end)
    current_state = STATE_BROADCAST
  elseif current_state == STATE_INTERACT then
    CheckEnemyDeath()
  elseif current_state == STATE_INTERRUPTED then
    timer = timer + thisLevel:GetUnitTime()
    if timer > interruptRestartTime then
      timer = 0.5
      current_state = STATE_INIT
    end
  elseif current_state == STATE_PUPPET_DEAD then
    timer = timer + thisLevel:GetUnitTime()
    if timer > interruptRestartTime then
      timer = 0
      current_state = STATE_INIT
    end
  end
end
function GotoPOI(Pup)
  if puppeteer ~= nil then
    puppet = puppeteer.Puppet
    local run_speed = 1
    if approachSpeed == "Run" then
      run_speed = 4
    end
    arrivalDistance = math.random(4.5, 6)
    puppeteer:Clear()
    puppeteer:OnEvent(OnHitReaction, "kEHitReaction")
    puppeteer:Approach({
      pos = thisObj:GetWorldPosition(),
      dir = thisObj:GetWorldForward(),
      speed = run_speed,
      stop = false
    })
    puppeteer:OnArrival(OnArrivedPOI, {
      pos = thisObj:GetWorldPosition(),
      radius = arrivalDistance
    })
    current_state = STATE_INTERACT
  end
end
function OnArrivedPOI(Pup)
  if puppeteer ~= nil then
    puppeteer:Clear()
    puppet:Warp(puppet:GetWorldPosition(), thisObj:GetWorldPosition() - puppet:GetWorldPosition())
    if movBranch ~= "BRA_Null" then
      puppeteer:StartBranch(movBranch)
    end
    puppeteer:OnEvent(OnHitReaction, "kEHitReaction")
    if onArrivalEvent ~= nil and next(onArrivalEvent) ~= nil then
      LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onArrivalEvent, "On Arrival")
    end
    puppeteer:OnComplete(InteractionComplete)
  end
end
function CheckOverrideInteractionComplete()
  local objRef = LookForInteractionComplete(thisObj)
  if objRef ~= nil then
    objRef.LuaObjectScript.OnInteractionComplete(thisObj, puppeteer, puppet)
  end
end
function LookForInteractionComplete(obj)
  if obj == nil then
    return nil
  end
  if obj.LuaObjectScript and obj.LuaObjectScript._G.OnInteractionComplete then
    return obj
  end
  local temp_obj
  if obj.Parent then
    temp_obj = LookForInteractionComplete(obj.Parent)
    if temp_obj ~= nil then
      return temp_obj
    end
  end
  return nil
end
function InteractionComplete()
  ClearPuppeteer()
  current_state = STATE_INIT
  if interactCompleteEvent ~= nil and next(interactCompleteEvent) ~= nil then
    LD.CallFunctionAfterDelay(function()
      LD.ExecuteCallbacksForEvent(thisLevel, thisObj, interactCompleteEvent, "Interact Complete")
    end, startEventsDelay)
  end
end
function CheckEnemyDeath()
  if IsPuppetDead() then
    ClearPuppeteer()
    current_state = STATE_PUPPET_DEAD
    if interactInterruptedEvent ~= nil and next(interactInterruptedEvent) ~= nil then
      LD.ExecuteCallbacksForEvent(thisLevel, thisObj, interactInterruptedEvent, "On Interrupted(Death)")
    end
    return true
  end
  return false
end
function OnHitReaction(Pup)
  print("enemy taking hit while going to the puppeteer")
  ClearPuppeteer()
  current_state = STATE_INTERRUPTED
  if interactInterruptedEvent ~= nil and next(interactInterruptedEvent) ~= nil then
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, interactInterruptedEvent, "On Interrupted")
  end
end
function ClearPuppeteer()
  if puppeteer ~= nil then
    puppeteer:Clear()
    puppeteer:DetachPuppet()
    puppeteer = nil
  end
end
function GetCreaturePOIAdvertise()
  local creatuerAd = string.gsub(creatureName, "%d+", "")
  creatuerAd = string.upper(creatuerAd)
  creatuerAd = creatuerAd .. ":" .. poiAdName
  return creatuerAd
end
function OnSaveCheckpoint(level, obj)
  return {}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
end
function Enable()
  if current_state == STATE_INTERACT or current_state == STATE_INTERRUPTED then
    return
  end
  current_state = STATE_INIT
end
function Disable()
  ClearPuppeteer()
  current_state = STATE_DISABLE
end
local GetStateString = function(pState)
  if pState == STATE_INIT then
    return "Init"
  end
  if pState == STATE_BROADCAST then
    return "Broadcast"
  end
  if pState == STATE_INTERACT then
    return "Interact"
  end
  if pState == STATE_PUPPET_DEAD then
    return "Dead"
  end
  if pState == STATE_DONE then
    return "Done"
  end
  if pState == STATE_DISABLE then
    return "Disable"
  end
  if pState == STATE_INTERRUPTED then
    return "Interrupted"
  end
end
function DrawDebug()
  local pos = thisObj:GetWorldPosition()
  pos.y = pos.y + 1
  engine.DrawTextInWorld(pos, "POI_state: " .. GetStateString(current_state), color.lime)
end
