local h={}h.ClassName="\084\104\114\111\116\116\108\101\100\070\117\110\099\116\105\111\110"h.__index=h function h.new(k,V,O)local z=setmetatable({},h)z._nextCallTimeStamp=0 z._timeout=k or error("\078\111 \116\105\109\101\111\117\116\073\110\083\101\099\111\110\100\115")z._func=V or error("\078\111 \102\117\110\099")z._trailingValue=nil z._callLeading=true z._callTrailing=true z:_configureOrError(O)return z end function h.Call(h,...)if h._trailingValue then h._trailingValue=table.pack(...)elseif h._nextCallTimeStamp<=tick()then if h._callLeading or h._callLeadingFirstTime then h._callLeadingFirstTime=false h._nextCallTimeStamp=tick()+h._timeout h._func(...)elseif h._callTrailing then h._trailingValue=table.pack(...)task.delay(h._timeout,function()if h.Destroy then h:_dispatch()end end)else error("\091\084\104\114\111\116\116\108\101\100\070\117\110\099\116\105\111\110\046\067\108\101\097\110\117\112\093 - \084\114\097\105\108\105\110\103 \097\110\100 \108\101\097\100\105\110\103 \097\114\101 \098\111\116\104 \100\105\115\097\098\108\101\100")end elseif h._callLeading or h._callTrailing or h._callLeadingFirstTime then h._callLeadingFirstTime=false local k=h._nextCallTimeStamp-tick()h._trailingValue=table.pack(...)task.delay(k,function()if h.Destroy then h:_dispatch()end end)end end function h._dispatch(h)h._nextCallTimeStamp=tick()+h._timeout local k=h._trailingValue if k then h._trailingValue=nil h._func(unpack(k,1,k.n))end end function h._configureOrError(k,h)if h==nil then return end assert(type(h)=="\116\097\098\108\101","\066\097\100 \116\104\114\111\116\116\108\101\067\111\110\102\105\103")for h,V in pairs(h)do assert(type(V)=="\098\111\111\108\101\097\110","\066\097\100 \116\104\114\111\116\116\108\101\067\111\110\102\105\103 \101\110\116\114\121")if h=="\108\101\097\100\105\110\103"then k._callLeading=V elseif h=="\116\114\097\105\108\105\110\103"then k._callTrailing=V elseif h=="\108\101\097\100\105\110\103\070\105\114\115\116\084\105\109\101\079\110\108\121"then k._callLeadingFirstTime=V else error(("\066\097\100 \107\101\121 \037\113 \105\110 \099\111\110\102\105\103"):format(tostring(h)))end end assert(k._callLeading or k._callTrailing,"\067\097\110\110\111\116 \099\111\110\102\105\103\117\114\101 \098\111\116\104 \108\101\097\100\105\110\103 \097\110\100 \116\114\097\105\108\105\110\103 \100\105\115\097\098\108\101\100")end function h.Destroy(h)h._trailingValue=nil h._func=nil setmetatable(h,nil)end return h
--[=[
	Throttles execution of a functon. Does both leading, and following
	@class ThrottledFunction
]=]

local ThrottledFunction = {}
ThrottledFunction.ClassName = "ThrottledFunction"
ThrottledFunction.__index = ThrottledFunction

function ThrottledFunction.new(timeoutInSeconds, func, config)
	local self = setmetatable({}, ThrottledFunction)

	self._nextCallTimeStamp = 0
	self._timeout = timeoutInSeconds or error("No timeoutInSeconds")
	self._func = func or error("No func")

	self._trailingValue = nil

	self._callLeading = true
	self._callTrailing = true

	self:_configureOrError(config)

	return self
end

function ThrottledFunction:Call(...)
	if self._trailingValue then
		-- Update the next value to be dispatched
		self._trailingValue = table.pack(...)
	elseif self._nextCallTimeStamp <= tick() then
		if self._callLeading or self._callLeadingFirstTime then
			self._callLeadingFirstTime = false
			-- Dispatch immediately
			self._nextCallTimeStamp = tick() + self._timeout
			self._func(...)
		elseif self._callTrailing then
			-- Schedule for trailing at exactly timeout
			self._trailingValue = table.pack(...)
			task.delay(self._timeout, function()
				if self.Destroy then
					self:_dispatch()
				end
			end)
		else
			error("[ThrottledFunction.Cleanup] - Trailing and leading are both disabled")
		end
	elseif self._callLeading or self._callTrailing or self._callLeadingFirstTime then
		self._callLeadingFirstTime = false
		-- As long as either leading or trailing are set to true, we are good
		local remainingTime = self._nextCallTimeStamp - tick()
		self._trailingValue = table.pack(...)

		task.delay(remainingTime, function()
			if self.Destroy then
				self:_dispatch()
			end
		end)
	end
end

function ThrottledFunction:_dispatch()
	self._nextCallTimeStamp = tick() + self._timeout

	local trailingValue = self._trailingValue
	if trailingValue then
		-- Clear before call so we are in valid state!
		self._trailingValue = nil
		self._func(unpack(trailingValue, 1, trailingValue.n))
	end
end

function ThrottledFunction:_configureOrError(throttleConfig)
	if throttleConfig == nil then
		return
	end

	assert(type(throttleConfig) == "table", "Bad throttleConfig")

	for key, value in pairs(throttleConfig) do
		assert(type(value) == "boolean", "Bad throttleConfig entry")

		if key == "leading" then
			self._callLeading = value
		elseif key == "trailing" then
			self._callTrailing = value
		elseif key == "leadingFirstTimeOnly" then
			self._callLeadingFirstTime = value
		else
			error(("Bad key %q in config"):format(tostring(key)))
		end
	end

	assert(self._callLeading or self._callTrailing, "Cannot configure both leading and trailing disabled")
end

function ThrottledFunction:Destroy()
	self._trailingValue = nil
	self._func = nil
	setmetatable(self, nil)
end

return ThrottledFunction
