-- Initial settings for ScreenshotHud
if ScreenshotHud then
	ScreenshotHud.OverlayFont = Enum.Font.GothamMedium
	ScreenshotHud.ExperienceNameOverlayEnabled = true
	ScreenshotHud.UsernameOverlayEnabled = false
	ScreenshotHud.CloseWhenScreenshotTaken = false

	-- Placing the close button off-screen since we don't use it
	ScreenshotHud.CloseButtonPosition = UDim2.fromScale(-1, -1)
end

local initialSpawn = true
local mountedTree
local connections = {}
local disconnectActions: (() -> ())?

local function handleActionActivation()
	local cleanupMap: { [any]: () -> () } = {}
	local conns: { [string]: RBXScriptConnection } = {}

	local function cleanupAction(action: UserActions.Action)
		local cleanup = cleanupMap[action]
		if cleanup then
			cleanup()
			cleanupMap[action] = nil
		end
	end

	conns.activated = events.actionActivated:Connect(function(action: UserActions.Action)
		if action.parent then
			local state: store.State = store:getState()

			-- Each parent action can only have one sub-action active at a time, so
			-- this will deactivate any other active sub-actions for the current
			-- parent
			local subAction = UserActions.getActiveSubAction(state.actions, action.parent)

			if subAction then
				cleanupAction(subAction)
			end
		end

		if action.onActivated then
			local cleanup = action.onActivated(action, store)
			if cleanup then
				cleanupMap[action] = cleanup
			end
		end
	end)

	conns.deactivated = events.actionDeactivated:Connect(function(action: UserActions.Action)
		cleanupAction(action)
	end)

	conns.closed = events.selfieModeClosed:Connect(function()
		for _, cleanup in pairs(cleanupMap) do
			cleanup()
		end

		cleanupMap = {}
	end)

	local function disconnect()
		for _, conn in pairs(conns) do
			conn:Disconnect()
		end
	end

	return disconnect
end

local function mount(playerGui: BasePlayerGui)
	local ref = Roact.createRef()
	local guiController = ExperienceComponents.GuiController.new({ ref })

	local function onSelfieModeToggled(inSelfieMode: boolean)
		if inSelfieMode then
			local player = Players.LocalPlayer
			local camera = workspace.CurrentCamera

			if player and camera then
				focusCharacter(player, camera)
			end

			guiController:disable()
			guiController:setMobileTouchGuiEnabled(true)
		else
			guiController:enable()
		end

		if ScreenshotHud then
			ScreenshotHud.Visible = inSelfieMode
		end
	end

	local root = Roact.createElement("ScreenGui", {
		ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
		ResetOnSpawn = false,
		[Roact.Ref] = ref,
	}, {
		App = Roact.createElement(RoactRodux.StoreProvider, {
			store = store,
		}, {
			Roact.createElement(App, {
				onSelfieModeToggled = onSelfieModeToggled,
				screenshotHud = ScreenshotHud,
			}),
		}),
	})

	local element = Roact.mount(root, playerGui, "SelfieMode")

	return element
end

return function(playerGui: BasePlayerGui)
	playerGui = if playerGui then playerGui else Players.LocalPlayer:WaitForChild("PlayerGui")

	local function setEnabled(isEnabled: boolean)
		assert(RunService:IsClient(), "SelfieMode.setEnabled must be called on the client")
		assert(t.boolean(isEnabled), "Bad argument #1 to SelfieMode.setEnabled: expected a boolean")

		if isEnabled then
			-- Mount SelfieModeUI to PlayerGui
			mountedTree = mount(playerGui)
			disconnectActions = handleActionActivation()

			-- Setting up connections
			connections.characterAddedConnection = Players.LocalPlayer.CharacterAdded:Connect(function()
				if not initialSpawn then
					store:dispatch(selfieModeClosed())
					setEnabled(false)
					task.wait()
					setEnabled(true)
				end
				initialSpawn = false
			end)
		else
			-- Unmount SelfieModeUI from PlayerGui
			if mountedTree then
				Roact.unmount(mountedTree)
				mountedTree = nil
			end

			-- Disconnecting connections
			for _, connection in pairs(connections) do
				connection:Disconnect()
			end

			if disconnectActions then
				disconnectActions()
			end
		end
	end

	return setEnabled
end