local statemachine = require("ai.statemachine")
local DL = require("design.DesignerLibrary")
local avoidance = require("behavior.avoidance")
local poilib = require("behavior.poi")
local unawarelib = require("awareness.unaware")
local passivelib = require("awareness.passive")
local locomotion = require("creature.locomotion")
local baldurdragon_poistates = require("baldurdragon00.POIStates")
local Brain = statemachine.StateMachine.New("BaldurDragon00")
local NOTHING = Brain:State("Nothing")
local INCOMBAT = Brain:State("InCombat")
local LEASHING = Brain:State("Leashing")
local UNAWARE = Brain:State("Unaware", unawarelib.Unaware) .. {sixthSenseDistance = 3}
local PASSIVE = Brain:State("Passive", passivelib.Passive) .. {hitsTakenUntilAggro = 1}
local NONHOSTILE = Brain:State("NonHostile")
local INAGGRO = Brain:Action("InAggro")
local goPlayerCreature, prevPositionIndex
local moveOptions = {}
moveOptions[1] = {MinDistance = 1, Chance = 1}
local moveOptionsComputed = {}
local avoidanceInputs = {
  Obstacle = {Radius = 0.5, Priority = 1},
  Agent = {
    Radius = 0.5,
    GoalRadius = 0.5,
    Priority = 1
  }
}
function Brain:OnBrainInit(ai, global, constants)
  goPlayerCreature = game.Player.FindPlayer()
end
local UsePOI = poilib.NewPOIState(Brain, "UsePOI")
poilib.AllowPOIFromStates(INCOMBAT, UNAWARE, PASSIVE, NONHOSTILE, INAGGRO, NOTHING)
baldurdragon_poistates.SetupPOIStates(UsePOI)
function UsePOI:OnUpdateAwareness(ai, global, constants)
  local update_this
  if global.aggroState == "UNAWARE" then
    update_this = UNAWARE
  elseif global.aggroState == "PASSIVE" then
    update_this = PASSIVE
  elseif global.aggroState == "NONHOSTILE" then
    update_this = NONHOSTILE
  end
  if update_this then
    update_this:Update(ai, global, constants)
  end
end
function NOTHING:Enter(ai, global, constants)
  DL.DebugPrint(ai, "NOTHING selected")
end
function INCOMBAT:Enter(ai, global, constants)
  DL.DebugPrint(ai, "INCOMBAT selected")
end
function INCOMBAT:Update(ai, global, constants)
  ai:SetCombatTarget(global.target)
  local fightPosResult, facingVector
  fightPosResult = ai:GetFightPosition()
  if fightPosResult then
    facingVector = (goPlayerCreature:GetWorldPosition() - ai:GetWorldPosition()):Normalized()
    if fightPosResult.PositionIndex ~= prevPositionIndex then
      global.fightConstants.MinDistanceToMove = DL.GetMoveOptionMinDistance(moveOptionsComputed)
      prevPositionIndex = fightPosResult.PositionIndex
    end
  end
  ai:SetFightIntent(global.fightConstants)
  if fightPosResult ~= nil then
    local actuatorData = {}
    local dTreeParams = {
      DecisionTree = global.DTree,
      AggressiveStandardRecoveryTime = 0,
      NonAggressiveStandardRecoveryTime = 1
    }
    actuatorData.Position = fightPosResult.Position
    actuatorData.ApproachSpeed = fightPosResult.SelectedSpeed
    actuatorData.Facing = facingVector
    actuatorData.StopDistance = global.navData.stopDistance
    actuatorData.StartDistance = global.navData.startDistance
    if (global.target:GetWorldPosition() - ai:GetWorldPosition()):Length() > 16 then
      actuatorData.Strafe = false
    else
      actuatorData.Strafe = global.navData.isStrafing
    end
    ai:RunDTree(dTreeParams)
    locomotion.SetActuator(ai, {
      Destination = actuatorData.Position,
      Facing = actuatorData.Facing,
      Strafe = actuatorData.Strafe,
      Speed = "Default",
      StopDistance = actuatorData.StopDistance,
      StartDistance = actuatorData.StartDistance
    })
    local avoidanceArgs = {
      AlwaysAvoid = not ai:IsInNavigationMove(),
      GoalPosition = actuatorData.Position
    }
    avoidance.SetAvoidanceInput(ai, avoidanceInputs, avoidanceArgs)
  end
end
function LEASHING:Enter(ai, global, constants)
end
function LEASHING:Update(ai, global, constants)
end
function UNAWARE:Update(ai, global, constants)
  unawarelib.Unaware.Update(self, ai, global, constants)
end
function PASSIVE:Update(ai, global, constants)
  passivelib.Passive.Update(self, ai, global, constants)
end
function INAGGRO:Enter(seq, ai, global, constants)
  DL.DebugPrint(ai, "INAGGRO selected")
  local delay = math.random()
  DL.DebugPrint(ai, "Starting timer for Aggro Event with value: " .. delay)
  seq:At(delay, function()
    DL.DebugPrint(ai, "Triggering Aggro Event")
    DL.SendAggroEvent(goPlayerCreature, ai)
  end)
  seq:At(delay + 0.1, function()
    global.aggroState = "INCOMBAT"
  end)
end
function Brain:SelectNextState(ai, global, constants)
  local ActiveTags = statemachine.ActiveTags()
  if poilib.IsPOIActive() then
    return UsePOI
  elseif global.aggroState == "INAGGRO" then
    return INAGGRO
  elseif global.aggroState == "INCOMBAT" then
    global.target = ai:FindTarget(global.targetParams)
    if global.target then
      if (ActiveTags.LeashFallback or ActiveTags.LeashRetreat) and global.useLeashing then
        return LEASHING
      else
        return INCOMBAT
      end
    end
    DL.DebugPrint(ai, "NOTHING selected because no target found")
    return NOTHING
  elseif global.aggroState == "UNAWARE" then
    return UNAWARE
  elseif global.aggroState == "PASSIVE" then
    return PASSIVE
  elseif global.aggroState == "NONHOSTILE" then
    return NONHOSTILE
  else
    return NOTHING
  end
end
return Brain
