-- checks when ground is close enough to disable skydiving or if a player fell off the map
local function makeSkydiveConnection()
	local connection = RunService.Heartbeat:Connect(function()
		if player.Character == nil then
			return
		end

		if player.Character.PrimaryPart == nil then
			return
		end

		if Skydive:isSkydiving() then
			local raycastParams = RaycastParams.new()
			raycastParams.FilterDescendantsInstances = { Environment, ServerEnvironment, workspace.Terrain }
			raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
			local raycastResult = workspace:Raycast(
				player.Character.PrimaryPart.Position,
				Vector3.new(0, -5, 0),
				raycastParams
			)

			if raycastResult and raycastResult.Instance and raycastResult.Instance.Transparency == 0 then
				Skydive:stop()
			elseif raycastResult and raycastResult.Instance and not raycastResult.Instance:IsA("BasePart") then
				Skydive:stop()
			end
		end

		if player.Character.PrimaryPart.Position.Y < TELEPORT_BACK_HEIGHT then
			teleportPlayerToSkydive()

			if not Skydive:isSkydiving() then
				Skydive:start()
			end
		end
	end)
	return connection
end

Schema.OnSetup = function()
	Environment = GetCurrentSceneEnvironment()
	ServerEnvironment = GetCurrentServerEnvironmentFromClient()

	FirstNeonParts = CollectionService:GetTagged("Scene2NeonPart")
	SecondNeonParts = CollectionService:GetTagged("SecondNeonParts")
	enableBouncyPads()
	
	SkydiveOutOfBoundsConnection = makeSkydiveConnection()

	-- Display a message to the client to inform the user about flashing lights.
	messageTakeoverModule.ShowMessage("Warning\n\nThis scene contains bright flashing lights and strobing effects.", 4)
	
	teleportPlayerToSkydive()
	Skydive:start()
	local camera = workspace.CurrentCamera
	local vector = Vector3.new(0, -1, 0)

	camera.CameraType = Enum.CameraType.Custom

	camera.CFrame = CFrame.new(camera.CFrame.p, camera.CFrame.Position + vector * 100)
end

Schema.OnRun = function()
	local MainAudio = Schema:audio({
		StartTime = 0,
		SoundId = "rbxassetid://7023650590",
	})

	RunFirstNeonParts(Schema, MainAudio, FirstNeonParts)

	local spinDiscoBall = Schema:interval({
		Frequency = 0,
		OnInterval = function()
			Environment.DiscoBall.PrimaryPart.CFrame *= CFrame.Angles(0, math.rad(0.1), 0)
		end,

		SyncToAudio = {
			Audio = MainAudio,
			StartAtAudioTime = 0,
			EndAtAudioTime = 250,
		},
	})

	local horizontalLasersRoot = Environment.Lasers_Horizontal.PrimaryPart
	local verticalLasersRoot = Environment.Lasers_Vertical.PrimaryPart

	local spinLasers = Schema:interval({
		Frequency = 0,

		OnInterval = function()
			horizontalLasersRoot.CFrame *= CFrame.Angles(0, math.rad(0.1), 0)
			verticalLasersRoot.CFrame *= CFrame.Angles(0, math.rad(-0.1), 0)
		end,

		SyncToAudio = {
			Audio = MainAudio,
			StartAtAudioTime = 0,
			EndAtAudioTime = 61,
		},
	})

	local spinLasersInReverse = Schema:interval({
		Frequency = 0,

		OnInterval = function()
			horizontalLasersRoot.CFrame *= CFrame.Angles(math.rad(1), math.rad(1), math.rad(1))
			verticalLasersRoot.CFrame *= CFrame.Angles(math.rad(-1), math.rad(-1), math.rad(1))
		end,

		SyncToAudio = {
			Audio = MainAudio,
			StartAtAudioTime = 61.88,
			EndAtAudioTime = 91,
		},
	})

	local spinLasersAgain = Schema:interval({
		Frequency = 0,

		OnStart = function()
			horizontalLasersRoot.CFrame = CFrame.new(horizontalLasersRoot.Position) * CFrame.Angles(0, 0, 0)
			verticalLasersRoot.CFrame = CFrame.new(verticalLasersRoot.Position) * CFrame.Angles(0, 0, 0)
		end,
		OnInterval = function()
			horizontalLasersRoot.CFrame *= CFrame.Angles(0, math.rad(0.1), 0)
			verticalLasersRoot.CFrame *= CFrame.Angles(0, math.rad(-0.1), 0)
		end,

		SyncToAudio = {
			Audio = MainAudio,
			StartAtAudioTime = 91.68,
			EndAtAudioTime = 122.8,
		},
	})

	local spinLasersInReverseAgain = Schema:interval({
		Frequency = 0,
		OnInterval = function()
			horizontalLasersRoot.CFrame *= CFrame.Angles(
				math.rad(math.sin(os.clock()) * -2),
				math.rad(1),
				math.rad(math.sin(os.clock()) * 2)
			)
			verticalLasersRoot.CFrame *= CFrame.Angles(
				math.rad(math.sin(os.clock()) * 2),
				math.rad(1),
				math.rad(math.sin(os.clock()) * -2)
			)
		end,
		SyncToAudio = {
			Audio = MainAudio,
			StartAtAudioTime = 123.165,
			EndAtAudioTime = 169,
		},
	})

	for _, laserPointer in pairs(Environment.LaserPointers:GetChildren()) do
		local spinLaserPointers = Schema:interval({
			Frequency = 0,
			OnInterval = function()
				laserPointer.Root.CFrame *= CFrame.Angles(math.rad(0.1), math.rad(0.1), math.rad(0.1))
			end,
			SyncToAudio = {
				Audio = MainAudio,
				StartAtAudioTime = 0,
				EndAtAudioTime = 61.44,
			},
		})

		local spinLaserPointersFaster = Schema:interval({
			Frequency = 0,
			OnInterval = function()
				laserPointer.Root.CFrame *= CFrame.Angles(math.rad(1), math.rad(1), math.rad(1))
			end,
			SyncToAudio = {
				Audio = MainAudio,
				StartAtAudioTime = 61.88,
				EndAtAudioTime = 91.68,
			},
		})

		Schema:schedule({
			OnStart = function()
				local CFRAME = CFrame.lookAt(laserPointer.Root.Position, Vector3.new(0, 100, 0))
				laserPointer.Root.CFrame = CFRAME * CFrame.Angles(math.rad(-90), 0, 0)
			end,
			SyncToAudio = {
				Audio = MainAudio,
				StartAtAudioTimes = { 91.7 },
			},
		})

		Schema:interval({
			Frequency = 0,
			OnInterval = function()
				laserPointer.Root.CFrame *= CFrame.Angles(0, math.cos(math.sin(os.clock()) * 3), 0)
			end,
			SyncToAudio = {
				Audio = MainAudio,
				StartAtAudioTime = 123.165,
				EndAtAudioTime = 169,
			},
		})
	end

	local lowerBloomThreshold = Schema:tween({
		Tween = {
			Object = game.Lighting.Bloom,
			Info = TweenInfo.new(61.88, Enum.EasingStyle.Sine, Enum.EasingDirection.In, 0, false),
			Properties = {
				Threshold = 0.75,
			},
		},
		SyncToAudio = {
			Audio = MainAudio,
			StartAtAudioTimes = { 0 },
		},
	})

	local increaseBloomSize = Schema:tween({
		Tween = {
			Object = game.Lighting.Bloom,
			Info = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.In, 0, false),
			Properties = {
				Size = 56,
			},
		},
		SyncToAudio = {
			Audio = MainAudio,
			StartAtAudioTimes = { 61.44 },
		},
	})

	ThirdBeatDrop(Schema, MainAudio, Environment, FirstNeonParts, SecondNeonParts)

	Cooldown(Schema, MainAudio, Environment, FirstNeonParts, SecondNeonParts)
end

Schema.OnEndScene = function()
	if SkydiveOutOfBoundsConnection then
		SkydiveOutOfBoundsConnection:Disconnect()
		SkydiveOutOfBoundsConnection = nil
	end
	Skydive:stop()
end

return Schema