local profile = require("core.profile")
local thunk = require("core.thunk")
local RegisterWaypointCallback = function(ai, global, constants)
  thunk.Install("SetNewDestinations", function(go, finalPoints, defaultParameters)
    global.LTW_Destinations = finalPoints
    global.LTW_DestinationIndex = 1
    global.LTW_DefaultParameters = defaultParameters
    global.leadTheWay.Path = nil
    global.leadTheWay.PathTimer = 0
    global.leadTheWay.Velocity = 0
    global.leadTheWay.VelocityTimer = 0
    global.leadTheWay.DistancePrediction = 0
    global.leadTheWay.LookBackTimer = 10
    global.leadTheWay.LookBackAvailable = false
    global.leadTheWay.CurrentPathLength = 10000
    global.leadTheWay.FinalPoint = nil
    global.leadTheWay.FirstPath = nil
    global.leadTheWay.FirstPathLength = nil
    global.leadTheWay.Facing = engine.Vector.New(0, 0, 1)
  end)
  thunk.Install("SetNewDestinationsSpline", function(go, spline, defaultParameters)
    global.LTW_Spline = spline
    global.LTW_DefaultParameters = defaultParameters
    global.leadTheWay = {}
    global.destinationPoint = nil
    global.leadTheWay.spline = spline
    global.leadTheWay.defaultParameters = defaultParameters
    global.leadTheWay.currentProgression = 0
    global.leadTheWay.Path = nil
    global.leadTheWay.PathTimer = 0
    global.leadTheWay.Velocity = 0
    global.leadTheWay.VelocityTimer = 0
    global.leadTheWay.DistancePrediction = 0
    global.leadTheWay.LookBackTimer = 10
    global.leadTheWay.LookBackAvailable = false
    global.leadTheWay.CurrentPathLength = 10000
    global.leadTheWay.FinalPoint = nil
    global.leadTheWay.FirstPath = nil
    global.leadTheWay.FirstPathLength = nil
    global.leadTheWay.Facing = engine.Vector.New(0, 0, 1)
  end)
  thunk.Install("ClearPosition", function(go)
    global.LTW_Destinations = nil
    global.LTW_Spline = nil
    global.leadTheWay.spline = nil
  end)
end
local CalculateLeadTheWayPositionSpline = function(ai, global, constants, player, motionParams)
  if global.leadTheWay.spline ~= nil then
    local progression = global.leadTheWay.spline:FindProgression(player.WorldPosition)
    local advancedProgression = global.leadTheWay.spline:AdvanceProgression(progression, 10, 50)
    if advancedProgression > global.leadTheWay.currentProgression then
      global.leadTheWay.currentProgression = advancedProgression
    end
    local potentialprogress = global.leadTheWay.currentProgression
    if global.bAllTheWay then
      potentialprogress = 1
    end
    local curvePt, _, _, _ = global.leadTheWay.spline:GetData(potentialprogress)
    motionParams.Strafe = true
    motionParams.ApproachSpeed = 4
    motionParams.StopDistance = 0.75
    motionParams.Position = curvePt
    engine.DrawFillSphere(curvePt, 1, 16715535)
    return curvePt, motionParams
  end
  return nil, motionParams
end
local CalculateLeadTheWayPosition = function(ai, global, constants, player, motionParams)
  local distanceToGoToNextPoint = global.LTW_DefaultParameters.DefaultDistanceToActivateNextPoint
  if distanceToGoToNextPoint and distanceToGoToNextPoint >= global.LTW_Destinations[global.LTW_DestinationIndex].WorldPosition:Distance(ai.WorldPosition) and #global.LTW_Destinations > global.LTW_DestinationIndex then
    global.LTW_DestinationIndex = global.LTW_DestinationIndex + 1
    global.leadTheWay.FirstPath = nil
    global.leadTheWay.CurrentPathLength = 10000
  end
  local pathData = {}
  pathData.StartLocation = game.NavMesh.ClosestLocation(player:GetWorldPosition(), player)
  pathData.EndLocation = game.NavMesh.ClosestLocation(global.LTW_Destinations[global.LTW_DestinationIndex].WorldPosition)
  local path = game.NavMesh.FindPath(pathData)
  local pathLength = path.PathLength
  local usePath = false
  if global.leadTheWay.FirstPath == nil then
    global.leadTheWay.FirstPath = path
    global.leadTheWay.FirstPathLength = pathLength
  end
  if pathLength <= global.leadTheWay.CurrentPathLength then
    global.leadTheWay.CurrentPathLength = pathLength
    usePath = true
  end
  game.NavMesh.DrawPath(global.leadTheWay.FirstPath)
  if usePath == true then
    local offset = global.LTW_DefaultParameters.LeadDistance
    local pointOnPath
    if pathLength < offset then
      pointOnPath = game.NavMesh.PointOnPath(path, 1)
    else
      pointOnPath = game.NavMesh.PointOnPath(path, offset / global.leadTheWay.CurrentPathLength)
    end
    global.leadTheWay.tempStorage = pointOnPath
    local pointradius = 3
    if pointradius < pointOnPath.Position:Distance(ai.WorldPosition) or global.leadTheWay.FinalPoint == nil then
      global.leadTheWay.FinalPoint = pointOnPath.Position
    end
  end
  global.leadTheWay.Velocity = player:GetVelocity():Length()
  if global.leadTheWay.Velocity < global.LTW_DefaultParameters.DefaultSpeed then
    global.leadTheWay.Velocity = global.LTW_DefaultParameters.DefaultSpeed
  end
  local finalPosition = global.leadTheWay.FinalPoint
  if ai.WorldPosition:Distance(finalPosition) < 2 then
    global.leadTheWay.Velocity = global.leadTheWay.Velocity * 0.75
  end
  if global.LTW_Destinations[global.LTW_DestinationIndex] ~= nil then
    global.leadTheWay.Facing = (global.leadTheWay.tempStorage.Position - ai.WorldPosition):Normalized()
  end
  motionParams.Strafe = false
  motionParams.ApproachSpeed = global.leadTheWay.Velocity
  motionParams.StopDistance = 0.75
  motionParams.Position = global.leadTheWay.FinalPoint
  return finalPosition, motionParams
end
return profile.WrapLibrary({
  RegisterWaypointCallback = RegisterWaypointCallback,
  CalculateLeadTheWayPosition = CalculateLeadTheWayPosition,
  CalculateLeadTheWayPositionSpline = CalculateLeadTheWayPositionSpline
})
