--!strict

local RunService = game:GetService("RunService")

local PostSimulation = RunService.Heartbeat

local OPT_INTERVAL_ATTRIBUTE = "Interval"
local OPT_INTERVAL = 3
local OPT_SWITCH_ATTRIBUTE = "Switch"
local OPT_SWITCH = false
local OPT_DEFAULT_MATERIAL = Enum.Material.SmoothPlastic
local OPT_SWITCH_MATERIAL = Enum.Material.ForceField

local Obstacle = {}

Obstacle.__index = Obstacle

function Obstacle.new(instance: Instance)
	local self = setmetatable({}, Obstacle)
	
	self._events = {}
	self._materialCache = {}
	self._lastTick = tick()
	
	self._instance = instance
	self._interval = instance:GetAttribute(OPT_INTERVAL_ATTRIBUTE) or OPT_INTERVAL
	self._switch = instance:GetAttribute(OPT_SWITCH_ATTRIBUTE) or OPT_SWITCH
	
	self:CachePartMaterials()
	
	table.insert(self._events, PostSimulation:Connect(function()
		local now = tick()
		
		if now - self._lastTick < self._interval then
			return
		end
		
		self._lastTick = now
		self:ToggleState()
	end))
	
	self:UpdateParts()
	
	return self
end

function Obstacle:GetAllParts(): {BasePart}
	local parts = {}
	
	if self._instance:IsA("BasePart") then
		table.insert(parts, self._instance::BasePart)
	end
	
	for _, child: BasePart in ipairs(self._instance:GetDescendants()) do
		if child:IsA("BasePart") then
			table.insert(parts, child)
		end
	end
	
	return parts
end

function Obstacle:CachePartMaterials()
	for _, part: BasePart in ipairs(self:GetAllParts()) do
		self._materialCache[part] = part.Material
	end
end

function Obstacle:GetSwitchState(): boolean
	return self._instance:GetAttribute(OPT_SWITCH_ATTRIBUTE) == true
end

function Obstacle:ToggleState()
	self._instance:SetAttribute(OPT_SWITCH_ATTRIBUTE, not self:GetSwitchState())
	self:UpdateParts()
end

function Obstacle:UpdateParts()
	for _, part in ipairs(self:GetAllParts()) do
		self:SetPartMaterial(part)
	end
end

function Obstacle:SetPartMaterial(part: BasePart)
	local switch = self:GetSwitchState()
	
	part.CanCollide = switch
	part.Material = switch and (self._materialCache[part] or OPT_DEFAULT_MATERIAL) or 
		OPT_SWITCH_MATERIAL
end

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

return Obstacle
