-- Modules / Directories --
local Dependencies = script.Dependencies
local Maid = require(Dependencies.Maid)
local InverseKinematics = require(Dependencies.InverseKinematics)
local IK_Parts = Dependencies.IK_Parts

local LegController = {}
LegController.__index = LegController

function LegController.new(Character : Model, Configuration : any)
	local self = setmetatable({}, LegController)
	
	self.Maid = Maid.new()
		
	local Humanoid = Character:WaitForChild("Humanoid")
	
	local LeftHip_Joint = Character:FindFirstChild(Humanoid.RigType == Enum.HumanoidRigType.R6 and "Left Hip" or "LeftHip", true)
	local RightHip_Joint = Character:FindFirstChild(Humanoid.RigType == Enum.HumanoidRigType.R6 and "Right Hip" or "RightHip", true)
	
	local LeftHip_C0 = LeftHip_Joint.C0
	local RightHip_C0 = RightHip_Joint.C0
	
	local Char_IK = nil
	if Humanoid.RigType == Enum.HumanoidRigType.R6 then
		Char_IK = InverseKinematics.New(Character)
	end
	
	local IK_RayParams = RaycastParams.new()
	IK_RayParams.FilterType = Enum.RaycastFilterType.Exclude
	IK_RayParams.FilterDescendantsInstances = {Character}
	local CurrentIK_Parts = {}
	
	for Index, Attachment in pairs(IK_Parts:GetChildren()) do
		local Attachment = Attachment:Clone()
		Attachment.Parent = Humanoid.RootPart
		CurrentIK_Parts[Attachment.Name] = Attachment
	end
	
	-- Connections --
	self.Maid:GiveTask(RunService.RenderStepped:Connect(function(DeltaTime : number)
		local Normalized_DeltaTime = DeltaTime * 60
		local Root_Velocity = Vector3.new(1, 0, 1) * Humanoid.RootPart.Velocity
		
		local LeftHip_C0, RightHip_C0 = LeftHip_C0, RightHip_C0
		if Char_IK and CurrentIK_Parts.LeftHip and CurrentIK_Parts.LeftFoot and CurrentIK_Parts.RightHip and CurrentIK_Parts.RightFoot then
			local LeftFoot_RayDir = CurrentIK_Parts.LeftFoot.WorldCFrame.Position - CurrentIK_Parts.LeftHip.WorldCFrame.Position
			local LeftFoot_Raycast = workspace:Raycast(CurrentIK_Parts.LeftHip.WorldCFrame.Position, LeftFoot_RayDir, IK_RayParams)
			
			local RightFoot_RayDir = CurrentIK_Parts.RightFoot.WorldCFrame.Position - CurrentIK_Parts.RightHip.WorldCFrame.Position
			local RightFoot_Raycast = workspace:Raycast(CurrentIK_Parts.RightHip.WorldCFrame.Position, RightFoot_RayDir, IK_RayParams)
			
			if LeftFoot_Raycast and LeftFoot_Raycast.Material ~= Enum.Material.Water and LeftFoot_Raycast.Instance.CanCollide then
				LeftHip_C0 = Char_IK:LegIK("Left", LeftFoot_Raycast.Position)
			end
			if RightFoot_Raycast and RightFoot_Raycast.Material ~= Enum.Material.Water and RightFoot_Raycast.Instance.CanCollide then
				RightHip_C0 = Char_IK:LegIK("Right", RightFoot_Raycast.Position)
			end
		end
		
		local MinActivation_Speed, Max_TiltAngle = Configuration and Configuration.Tilt.MinActivation_Speed or 1.5, Configuration and Configuration.Tilt.MaxAngle or 35
		
		local Tilt, TiltAngle = 0, 0
		local Dont_Inverse = Humanoid.RootPart.CFrame.LookVector:Dot(Humanoid.MoveDirection) < -0.1
		if Root_Velocity.Magnitude > MinActivation_Speed then --Checking if the Root_Velocity is above MinActivation_Speed
			Tilt = Humanoid.RootPart.CFrame.RightVector:Dot(Root_Velocity.unit)
			Tilt = Dont_Inverse and Tilt or -Tilt
		end
		
		TiltAngle = math.rad(Humanoid:GetState() == Enum.HumanoidStateType.Running and Tilt * Max_TiltAngle or 0)
		
		-- Moving joints --
		local Lerp = Root_Velocity.Magnitude < 2.5 and 0.285 or 0.1
		LeftHip_Joint.C0 = LeftHip_Joint.C0:Lerp(LeftHip_C0 * CFrame.Angles(0, TiltAngle, 0), Lerp * Normalized_DeltaTime)
		RightHip_Joint.C0 = RightHip_Joint.C0:Lerp(RightHip_C0 * CFrame.Angles(0, TiltAngle, 0), Lerp * Normalized_DeltaTime)
	end))
	
	return self
end

function LegController:Destroy()
	self.Maid:DoCleaning()
	
	table.clear(self)
	setmetatable(self, nil)
	
	return
end

return LegController