--!strict

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local fmt = string.format

local OPT_AXIS_ATTRIBUTE = "Axis"
local OPT_AXIS = "Z"
local OPT_DEGREES_ATTRIBUTE = "Degrees"
local OPT_DEGREES = 1

local ERR_NOT_MODEL = "Rotate expects a Model, but got %q (%s)"

local Obstacle = {}

Obstacle.__index = Obstacle

function Obstacle.new(model: Model)
	assert(model:IsA("Model"), fmt(ERR_NOT_MODEL, model:GetFullName(), model.ClassName))
	
	local self = setmetatable({}, Obstacle)
	self._events = {}
	
	self._model = model
	self._distance = model:GetModelSize().Magnitude * 2
	self._rotation = 0
	
	self._axis = model:GetAttribute(OPT_AXIS_ATTRIBUTE) or OPT_AXIS
	self._degrees = model:GetAttribute(OPT_DEGREES_ATTRIBUTE) or OPT_DEGREES
	self._initCFrame = model:GetPrimaryPartCFrame()
	
	local bodyPosition = Instance.new("BodyPosition")
	local bodyGyro = Instance.new("BodyGyro")
	
	bodyPosition.P = 1e5
	bodyPosition.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	bodyPosition.Position = self._model:GetPrimaryPartCFrame().Position
	bodyPosition.Parent = model.PrimaryPart
	
	bodyGyro.P = 1e4
	bodyGyro.MaxTorque = Vector3.new(1e5, 1e5, 1e5)
	bodyGyro.Parent = model.PrimaryPart
	
	self._bodyPosition = bodyPosition
	self._bodyGyro = bodyGyro
	
	-- TODO: Defer render to global handler
	table.insert(self._events, RunService.Heartbeat:Connect(function()
		self:RenderUpdate()
	end))
	
	return self
end

function Obstacle:GetRotationCFrame(): CFrame
	(self::any)._rotation += self._degrees::number % 360
	
	local degrees = math.rad(self._rotation)
	
	if self._axis == "X" then
		return CFrame.Angles(degrees, 0, 0)
	elseif self._axis == "Y" then
		return CFrame.Angles(0, degrees, 0)
	end
	
	return CFrame.Angles(0, 0, degrees)
end

function Obstacle:RenderUpdate()
	local angles = self:GetRotationCFrame()
	self._bodyGyro.CFrame = self._initCFrame * angles
end

function Obstacle:Destroy()
	for _, event: RBXScriptConnection in ipairs(self._events) do
		event:Disconnect()
	end
end

return Obstacle
