-- Class that provides body orientation functionality to a given rig

local OrientableBody = {}
OrientableBody.__index = OrientableBody

local function waitForDescendantWhichIsA(parent: Instance, name: string, className: string)
	for _, descendant in ipairs(parent:GetDescendants()) do
		if descendant.Name == name and descendant:IsA(className) then
			return descendant
		end
	end
	while true do
		local descendant = parent.DescendantAdded:Wait()
		if descendant.Name == name and descendant:IsA(className) then
			return descendant
		end
	end
end

function OrientableBody.new(character: Model)
	local self = {}
	self.character = character

	task.spawn(function()
		self.neck = waitForDescendantWhichIsA(character, "Neck", "Motor6D")
		self.waist = waitForDescendantWhichIsA(character, "Waist", "Motor6D")
	end)

	self.motor = Otter.createGroupMotor({
		horizontalAngle = 0,
		verticalAngle = 0,
	})
	self.motor:onStep(function(values)
		local waistWeight = config.getValues().waistOrientationWeight
		local neckWeight = 1 - waistWeight
		self:applyAngle(self.neck, values.horizontalAngle * neckWeight, values.verticalAngle * neckWeight)
		self:applyAngle(self.waist, values.horizontalAngle * waistWeight, values.verticalAngle * waistWeight)
	end)

	return setmetatable(self, OrientableBody)
end

function OrientableBody:applyAngle(joint, horizontalAngle, verticalAngle)
	if not joint then
		return
	end

	local horizontalRotation = CFrame.Angles(0, horizontalAngle, 0)
	local verticalRotation = CFrame.Angles(verticalAngle, 0, 0)
	joint.C0 = CFrame.new(joint.C0.Position) * horizontalRotation * verticalRotation
end
