local profile = require("core.profile")
local GetWorldJointPosition = function(objectName, jointName)
  return GameObjects[objectName]:GetWorldJointPosition(GameObjectJoints[objectName][jointName])
end
local GetWorldJointForward = function(objectName, jointName)
  return GameObjects[objectName]:GetWorldJointForward(GameObjectJoints[objectName][jointName])
end
local SetFacingTowardObject = function(gameObject01, gameObject02, args)
  local ignoreYAxis = true
  local invertFacing = false
  if args then
    if args.ignoreYAxis then
      ignoreYAxis = args.ignoreYAxis
    end
    if args.invertFacing then
      invertFacing = args.invertFacing
    end
  end
  local obj02_pos = gameObject02:GetWorldPosition()
  local obj02_x = obj02_pos.x
  local obj02_z = obj02_pos.z
  local obj02_y
  if ignoreYAxis then
    obj02_y = gameObject01:GetWorldPosition().y
  else
    obj02_y = obj02_pos.y
  end
  if invertFacing then
    gameObject01:SetWorldFacing(engine.Vector.New(obj02_x, obj02_y, obj02_z) - gameObject01.WorldPosition)
  else
    gameObject01:SetWorldFacing(gameObject01.WorldPosition - engine.Vector.New(obj02_x, obj02_y, obj02_z))
  end
end
local SetJointFacingTowardObject = function(gameObject01, gameObject02, jointIndex, callback, args)
  if not gameObject01.SetJointDirection then
    engine.warning("SetJointFacintTowardObject is not supported in this code build - get latest and do a feature check!")
    return
  end
  local ignoreYAxis = true
  local invertFacing = false
  if args then
    if args.ignoreYAxis then
      ignoreYAxis = args.ignoreYAxis
    end
    if args.invertFacing then
      invertFacing = args.invertFacing
    end
  end
  local obj02_pos = gameObject02:GetWorldPosition()
  local obj02_x = obj02_pos.x
  local obj02_z = obj02_pos.z
  local obj02_y
  if ignoreYAxis then
    obj02_y = gameObject01:GetWorldPosition().y
  else
    obj02_y = obj02_pos.y
  end
  if invertFacing then
    gameObject01:SetJointDirection(jointIndex, engine.Vector.New(obj02_x, obj02_y, obj02_z) - gameObject01.WorldPosition, callback)
  else
    gameObject01:SetJointDirection(jointIndex, gameObject01.WorldPosition - engine.Vector.New(obj02_x, obj02_y, obj02_z), callback)
  end
end
local GetPosInLocalSpaceOfObject = function(pos, obj)
  local diff = obj.WorldPosition - pos
  local posRelativeX = diff:Dot(obj:GetWorldLeft())
  local posRelativeY = diff:Dot(obj:GetWorldUp())
  local posRelativeZ = diff:Dot(obj:GetWorldForward())
  return engine.Vector.New(posRelativeX, posRelativeY, posRelativeZ)
end
local ProjectPositionOntoObject = function(pos, obj)
  local projectedXVector = -1 * (obj:GetWorldLeft():Normalized() * pos.x)
  local projectedYVector = -1 * (obj:GetWorldUp():Normalized() * pos.y)
  local projectedZVector = -1 * (obj:GetWorldForward():Normalized() * pos.z)
  return obj.WorldPosition + projectedXVector + projectedYVector + projectedZVector
end
local GetDistanceBetweenTwoObjects = function(go1, go2)
  return game.AIUtil.Distance(go1, go2)
end
local GetPositionBetweenTwoObjects = function(go1, go2, percent)
  percent = percent or 0.5
  if 1 < percent then
    percent = 1
  elseif percent < 0 then
    percent = 0
  end
  local magnitude = game.AIUtil.Distance(go1, go2) * percent
  local position = (go2.WorldPosition - go1.WorldPosition):Normalized() * magnitude + go1.WorldPosition
  return position
end
local IsPositionInFrontOfObject = function(pos, go)
  if math.acos(go:GetWorldForward():Dot((pos - go.WorldPosition):Normalized())) > math.pi / 2 then
    return false
  end
  return true
end
local SnapObjectToFloor = function(gameObject, posOffset)
  if gameObject ~= nil then
    local distanceToCheck = 40
    local checkLoc = gameObject.WorldPosition - engine.Vector.New(0, distanceToCheck, 0)
    local hit = game.World.RaycastCollision(gameObject.WorldPosition, checkLoc, {
      SourceGameObject = gameObject,
      EntityType = game.CollisionType.New("kInvisibleBarrier", "kEnvironment"),
      FindAnything = true
    })
    if not hit then
      return
    elseif hit then
      if not posOffset then
        gameObject:SetWorldPosition(hit.Position)
      else
        gameObject:SetWorldPosition(hit.Position + posOffset)
      end
    end
  end
end
return profile.WrapLibrary({
  GetWorldJointPosition = GetWorldJointPosition,
  GetWorldJointForward = GetWorldJointForward,
  SetFacingTowardObject = SetFacingTowardObject,
  SetJointFacingTowardObject = SetJointFacingTowardObject,
  GetPosInLocalSpaceOfObject = GetPosInLocalSpaceOfObject,
  ProjectPositionOntoObject = ProjectPositionOntoObject,
  GetDistanceBetweenTwoObjects = GetDistanceBetweenTwoObjects,
  GetPositionBetweenTwoObjects = GetPositionBetweenTwoObjects,
  IsPositionInFrontOfObject = IsPositionInFrontOfObject,
  SnapObjectToFloor = SnapObjectToFloor
})
