local class = require("core.class")
local statemachine = require("ai.statemachine")
local locomotion = require("creature.locomotion")
local thunk = require("core.thunk")
local poilib = require("behavior.poi")
local unaware = require("awareness.unaware")
local followpointcloud = require("ai.followpointcloud")
local navCurve = require("behavior.navCurve")
local navbranchlogic = require("creature.navbranchlogic")
local traverselink = require("creature.traverselink")
traverselink.InstallHooks()
local CoreBrain = class.Class("CoreBrain")
local Brain = statemachine.StateMachine.New("CoreSimpleBrain")
local state_machine_list = {Brain}
local STATE_NonHostile = Brain:State("NonHostile")
local STATE_Unaware = Brain:State("Unaware", unaware.Unaware)
local STATE_SplineNav = Brain:State("SplineNav")
local STATE_PlayerNav = Brain:State("PlayerNav")
local STATE_UsePOI = poilib.NewPOIState(Brain, "UsePOI")
poilib.AllowPOIFromStates(STATE_NonHostile, STATE_Unaware, STATE_PlayerNav, STATE_SplineNav)
local player
function CoreBrain:init()
  print("navbranchlogic", navbranchlogic)
end
function CoreBrain:OnAIPostSpawn(ai, global, constants, optionalTable)
end
function CoreBrain:OnAICreateLuaState(ai, global, constants)
  player = game.Player.FindPlayer()
  constants.isSimple = true
  locomotion.Install()
  locomotion.CreateDrivers(ai)
  global.navData = {}
  global.navData.navSpeedWalk = ai:LookupFloatConstant("NAV_SPEED_WALK") or 1.6
  global.navData.navSpeedJog = ai:LookupFloatConstant("NAV_SPEED_JOG") or 3.5
  global.navData.navSpeedRun = ai:LookupFloatConstant("NAV_SPEED_RUN") or 4.5
  global.navData.navSpeedSprint = ai:LookupFloatConstant("NAV_SPEED_SPRINT") or 6
  self:SetPoiStates(ai)
  if StartConfig ~= nil then
    global.aggroState = StartConfig.AggroState
  else
    global.aggroState = "NonHostile"
  end
  ai:SetNavBank(global.navBank)
  if ai:GetBlackboard() == nil then
    ai:SetBlackboardSize(16)
  end
  self:StartUpdate(ai, global, constants)
end
function CoreBrain:Update(ai, global, constants)
  statemachine.UpdateAll(state_machine_list, ai, global, constants)
end
function CoreBrain:CreatureUpdate(ai, global, constants)
  if engine.IsDebug() then
    local debugTable = statemachine.GetDebugTable(state_machine_list, ai)
    if debugTable then
      self:DrawDebugTable(debugTable, global)
    end
  end
end
function CoreBrain:SetPoiStates(ai)
  local poiStateName = string.format("%s.POIStates", ai:GetName())
  if package.preload[poiStateName] == nil then
    return
  end
  local setupPOI = require(poiStateName).SetupPOIStates
  assert(type(setupPOI) == "function", "Invalid POIStates, make sure it follows the standard setup. See Henry if you need help.")
  setupPOI(STATE_UsePOI)
  local name = ai:GetName()
  name = string.upper(name:gsub("%d", ""))
  poilib.SetCharacterType(STATE_UsePOI, name)
end
function CoreBrain:StartUpdate(ai, global, constants)
  statemachine.StartAll(ai, state_machine_list, ai, global, constants)
end
local Brain_Core = CoreBrain.New()
local OnAIUpdate = function(ai)
  CoreBrain:Update(ai, _G.global, _G.constants)
end
local OnAIPostSpawn = function(ai)
  CoreBrain:OnAIPostSpawn(ai, _G.global, _G.constants)
end
local OnAICreateLuaState = function(ai)
  CoreBrain:OnAICreateLuaState(ai, _G.global, _G.constants)
end
local OnCreatureUpdate = function(ai)
  CoreBrain:CreatureUpdate(ai, _G.global, _G.constants)
end
thunk.Install("OnAIUpdate", OnAIUpdate)
thunk.Install("OnAIPostSpawn", OnAIPostSpawn)
thunk.Install("OnAICreateLuaState", OnAICreateLuaState)
thunk.Install("OnCreatureUpdate", OnCreatureUpdate)
function Brain:OnBrainInit(ai, global, constants)
  print("init brain")
  constants.focusDuration = 0
end
function Brain:SelectNextState(ai, global, constants)
  if global.POIInfo.useThisPOI and ai.OwnedPOI and not global.POIInfo.approach then
    local curve = ai:GetNavCurve()
    if curve and global.POIInfo.useThisPOI.Type == "KeepEvaluatingPath" then
      return STATE_SplineNav
    end
  end
  if poilib.IsPOIActive() then
    return STATE_UsePOI
  end
  local curve
  if ai.GetNavCurve then
    curve = ai:GetNavCurve()
  else
    curve = ai:GetActuator().Curve
  end
  if curve then
    return STATE_SplineNav
  end
  if global.aggroState == "FollowPlayer" then
    return STATE_PlayerNav
  end
  if global.aggroState == "Unaware" then
    return STATE_Unaware
  end
  return STATE_NonHostile
end
function STATE_SplineNav:Enter(ai, global, constants)
  if global.leadTheWayParams.TargetCreature ~= nil then
    ai:SetFocus(global.leadTheWayParams.TargetCreature)
  end
end
function STATE_SplineNav:Update(ai, global, constants)
  local curve = ai:GetNavCurve()
  navCurve.LeadTheWay(ai, curve, global.leadTheWayParams)
end
function STATE_SplineNav:Exit(ai, global, constants)
  ai:ClearFocus()
  ai:ClearMaxSpeedOverride()
end
function _G.SubmitNavCurve(ai, data)
  locomotion.SubmitNavCurve(_G.constants, data)
end
function STATE_PlayerNav:OnBrainInit(ai, global, constants)
  global.followPoints = followpointcloud.FollowPointRequester.New(ai, "PointCloudParameters")
  global.motionType = "UseFollowPoint"
end
function STATE_PlayerNav:Enter(ai, global, constants)
  global.followPoints:SetTarget(player)
  global.followPoints:Start()
  self.desiredDirectionDelta = 0
end
function STATE_PlayerNav:Update(ai, global, constants)
  if global.motionType == "UseFollowPoint" then
    local finalPos = global.followPoints.intent_state.pos
    if finalPos == nil then
      finalPos = player:GetWorldPosition()
    end
    local totalDistance = finalPos:Distance(ai:GetWorldPosition())
    local playerSpeed = player:GetVelocity():Length()
    local speed = 6
    print(speed)
    global.followPoints:SetTweak("PointCloudParameters_FrontLead")
    if 18 < totalDistance or 4 < playerSpeed then
      speed = global.navData.navSpeedSprint
    elseif 12 < totalDistance or 3 < playerSpeed then
      speed = global.navData.navSpeedRun
    elseif 7 < totalDistance or 2.5 < playerSpeed then
      speed = global.navData.navSpeedJog
    else
      speed = global.navData.navSpeedWalk
    end
    global.followPlayerParams.Speed = speed
    followpointcloud.Follow(ai, global.followPoints, global.followPlayerParams)
    if totalDistance < 2.5 then
      global.motionType = "Idle"
      ai:ModifyCreatureMotion({})
    end
  elseif global.motionType == "UseFollowPoint_GetInFront" then
    local speed = 1.3
    global.followPoints:SetTweak("PointCloudParameters_FrontLead")
    global.followPlayerParams.Speed = speed
    followpointcloud.Follow(ai, global.followPoints, global.followPlayerParams)
  elseif global.motionType == "Idle" then
    if 7 < game.AIUtil.Distance(ai, player) then
      global.motionType = "UseFollowPoint"
    end
    locomotion.SetActuator(ai, {
      Destination = ai.WorldPosition,
      Facing = ai:GetWorldForward(),
      Speed = 0
    })
  end
end
function STATE_PlayerNav:Exit(ai, global, constants)
  global.followPoints:Stop()
end
function STATE_NonHostile:Enter(ai, global, constants)
end
function STATE_UsePOI:OnUpdateAwareness(ai, global, constants)
  local update_this
  if global.aggroState == "NonHostile" then
    update_this = STATE_NonHostile
  end
  if update_this then
    update_this:Update(ai, global, constants)
  end
end
function LuaHook_SetFollowPlayer(ai)
  _G.global.aggroState = "FollowPlayer"
end
function LuaHook_SetNonHostile(ai)
  _G.global.aggroState = "NonHostile"
end
function CoreBrain:DrawDebugTable(debugTable, global)
  if debugTable then
    table.insert(debugTable, {
      "Current POI:",
      global.currentPOI
    })
    engine.DrawDebugTable(debugTable)
  end
end
return Brain_Core
