-- End of barrier visualization functions

local function _update(dt)
	if not _state then
		return
	end

	local time = _getAdjustedTime(_serverTime.Value)

	-- Update effects based on the calculated perimiter and the local player's proximity to it.
	local t = math.min(1, (time - _state.transition_time) / _state.transition_length)
	local stormPos = Util.lerp(_state.current_pos, _state.target_pos, t)
	local stormRadius = Util.lerp(_state.current_radius, _state.target_radius, t)

	-- Tween the storm position and radius to make things smooth
	local timeUntilTarget = _state.transition_length - (time - _state.transition_time)
	-- Tween position
	if stormPosTween ~= nil and stormPos == _state.target_pos then
		stormPosTween:Cancel()
		stormPosTween:Destroy()
		stormPosTween = nil
	end
	if stormPosTween == nil then
		if not stormPosSetToState then
			stormPosTweenValue.Value = stormPos
			stormPosSetToState = true
		end

		if stormPos ~= _state.target_pos then
			local tweenInfo = TweenInfo.new(timeUntilTarget, Enum.EasingStyle.Linear)
			local goal = {}
			goal.Value = _state.target_pos
			stormPosTween = TweenService:Create(stormPosTweenValue, tweenInfo, goal)
			stormPosTween:Play()
			stormPosSetToState = false
		end
	end
	-- Tween radius
	if stormRadiusTween ~= nil and stormRadius == _state.target_radius then
		stormRadiusTween:Cancel()
		stormRadiusTween:Destroy()
		stormRadiusTween = nil
	end
	if stormRadiusTween == nil then
		if not stormRadiusSetToState then
			stormRadiusTweenValue.Value = stormRadius
			stormRadiusSetToState = true
		end

		if stormRadius ~= _state.target_radius then
			local tweenInfo = TweenInfo.new(timeUntilTarget, Enum.EasingStyle.Linear)
			local goal = {}
			goal.Value = _state.target_radius
			stormRadiusTween = TweenService:Create(stormRadiusTweenValue, tweenInfo, goal)
			stormRadiusTween:Play()
			stormRadiusSetToState = false
		end
	end

	-- Minimap
	if stormPos == _state.target_pos and stormRadius == _state.target_radius and _state.nextPos and _state.nextRadius then
		-- Show next stage target as target indicator while waiting
		MinimapGui.updateStormIndicator(stormPos, stormRadius, _state.nextPos, _state.nextRadius)
	elseif stormPos == _state.target_pos and stormRadius == _state.target_radius and (not _state.nextPos or not _state.nextRadius) then
		-- Makes target essentially disappear on last stage when storm shrinks to nothing
		MinimapGui.updateStormIndicator(stormPos, stormRadius, stormPos, 1)
	else
		-- Move to current target
		MinimapGui.updateStormIndicator(stormPosTweenValue.Value, stormRadiusTweenValue.Value, _state.target_pos, _state.target_radius)
	end

	-- Calculate the distance to the perimeter for scaling other effects
	local character = Util.getClientFocus()
	if character and character.PrimaryPart then
		local playerPos = character.PrimaryPart.Position
		local playerPosXZPlane = Vector3.new(playerPos.X, 0, playerPos.Z)
		local stormPosDelta = stormPos - playerPosXZPlane

		-- Positive proximity is inside of the play area, negative is the storm
		local proximity = stormRadius - stormPosDelta.Magnitude
		local playerDir = (character.PrimaryPart.CFrame.LookVector * Vector3.new(1, 0, 1)).Unit
		local facingStorm = math.clamp(stormPosDelta.Unit:Dot(playerDir) * -1, 0, 1)

		-- Adjusting the lighting
		local stormLightingLevel = math.clamp((proximity - 100) * -0.01, 0, 1) * (facingStorm * 0.5 + 0.5)
		for k, v in pairs(_stormLighting) do
			Lighting[k] = Util.lerp(_baseLighting[k], v, stormLightingLevel)
		end

		-- Barrier visualization update
		for i, barrierPartInfo in ipairs(barrierPartInfos) do
			updateBarrierPartInfo(barrierPartInfo, playerPos, stormPosTweenValue.Value, stormRadiusTweenValue.Value, i)
		end
	end

end

local function onCharacterAdded(character)
	if #barrierPartsFolder:GetChildren() == 0 then
		-- Setup the storm barrier
		local partTemplate = ReplicatedStorage.Assets.Meshes:WaitForChild("barrierPartTemplate")
		partTemplate.Transparency = 1
		partTemplate.CanCollide = false
		partTemplate.Material = Enum.Material.ForceField
		partTemplate.TextureID = "rbxassetid://3970410602"
		partTemplate.Anchored = true
		partTemplate.CastShadow = false
		partTemplate.Color = BARRIERPART_COLOR

		for i = 1, NUMBER_OF_LARGEST_SUBDIVISIONS do
			local newPart = partTemplate:Clone()
			newPart.Name = "BarrierPart_" .. i
			newPart.Parent = barrierPartsFolder
			local stepSize = 360 / NUMBER_OF_LARGEST_SUBDIVISIONS
			barrierPartInfos[i] = getBarrierPartInfo(newPart, i * stepSize, (i + 1) * stepSize, 1, false, nil)
		end

		partTemplate:Destroy()
	end
end

Players.LocalPlayer.CharacterAdded:Connect(onCharacterAdded)
if Players.LocalPlayer.Character then
	onCharacterAdded(Players.LocalPlayer.Character)
end

Broadcast.registerHandler("BARRIER", _onStateUpdate)
RunService.RenderStepped:Connect(_update)

return nil