local g=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local w=g:WaitForChild("\077\111\100\117\108\101\115")local v=require(w.src.utils.Logger)local A=game:GetService("\080\108\097\121\101\114\115")local C=require(w.src.actions.rooms.setRoomCountDown)local Q=require(w.src.actions.rooms.setRoomStartTime)local t=require(w.src.actions.rooms.setRoomEndTime)local c=require(w.src.actions.rooms.resetRoom)local U=require(w.src.actions.toClient.clientStartGame)local B=require(w.src.thunks.playerFinishedRoom)local S=require(w.src.Transporter)local T=require(w.M)local R=require(w.src.TouchItem)local O=require(w.src.MazeGenerator)local W=require(w.src.GlobalConfig)local F=game.Workspace:WaitForChild("\080\108\097\099\101")local H=F:findFirstChild("\077\097\112\115")local function N(g)return g.finishTime==nil end local function s(g)return function(w)v:i("\083\116\097\114\116\105\110\103 \103\097\109\101 \102\111\114 \114\111\111\109\058"..g)local F=(w:getState()).rooms[g]if not H then v:w("\077\097\112\115 \102\111\108\100\101\114 \100\111\101\115 \110\111\116 \101\120\105\115\116\115\033")return end local s=H:findFirstChild(F.modelName)if not s then v:w("\077\097\112\115 \102\111\108\100\101\114  \105\115 \109\105\115\115\105\110\103 "..(F.modelName.." \111\098\106\101\099\116\033"))return end local X=s:FindFirstChild("\076\111\099\097\116\105\111\110\080\097\114\116")local f={width=F.config.width;height=F.config.height;wallMaterial=Enum.Material.Grass,groundMaterial=Enum.Material.Sand,onlyBlocks=false,addRandomModels=true;addStartAndFinish=true;addKillBlocks=true;addCeiling=false,partThickness=3,ceilingFloorThickness=1;location=X,blockHeight=20;blockWidth=25;randomStuffFolder="\076\105\103\104\116"}local I={width=F.config.width,height=F.config.height;wallMaterial=Enum.Material.Basalt;groundMaterial=Enum.Material.Mud,onlyBlocks=false,addRandomModels=true;addStartAndFinish=true,addKillBlocks=false;addCeiling=true;partThickness=5;ceilingFloorThickness=5,location=X,blockHeight=25;blockWidth=30,randomStuffFolder="\068\097\114\107"}local x={nil_1="\068\097\114\107";nil_2="\076\105\103\104\116"}local o=T.extend({},F.playerVotes,x)local n=T.countBy(o,function(g)return g end)local l=n.Dark>n.Light local j=l and I or f O:generate(j)local p=l and F.config.playTime*3 or F.config.playTime local L=X:findFirstChild("\070\105\110\105\115\104\080\108\097\099\101\104\111\108\100\101\114",true)if L then local A={}R.create(L,function(C)if not A[C]then v:d("\084\111\117\099\104\101\100 \070\105\110\105\115\104 \102\111\114 \112\108\097\121\101\114\058"..C.Name)A[C]=true w:dispatch(B(C,g))end end)else v:w("\070\105\110\105\115\104\080\108\097\099\101\104\111\108\100\101\114 \105\115 \109\105\115\115\105\110\103 \102\114\111\109 "..(g.." \109\097\112 \111\098\106\101\099\116\033"))end local u=F.playersWaiting w:dispatch(Q(g,os.time(),u))local function Y(g)return A:GetPlayerByUserId(g.id)end local V=s:findFirstChild("\083\112\097\119\110\080\108\097\099\101\104\111\108\100\101\114",true)if V then local v=T.map(u,Y)local function A(v)w:dispatch(U(v,g))end T.each(v,A)S:transportPlayers(v,V)else v:w("\083\112\097\119\110\080\108\097\099\101\104\111\108\100\101\114 \105\115 \109\105\115\115\105\110\103 \102\114\111\109 "..(g.." \109\097\112 \111\098\106\101\099\116\033"))end spawn(function()local A=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")w:dispatch(C(g,p,"\070\105\110\100 \101\120\105\116","\087\097\105\116"))delay(p,function()A:Fire(true)end)spawn(function()while true do wait(.1)local v=(w:getState()).rooms[g]local C=T.select(v.playersPlaying,N)if T.count(C)==0 then A:Fire(false)break end end end)local Q=A.event:Wait()if Q then v:d("\071\097\109\101 \119\105\108\108 \101\110\100\101\100 \119\105\116\104 \116\105\109\101\114 \102\111\114 \114\111\111\109 "..g)local A=w:getState()local C=A.rooms[g]local Q=T.select(C.playersPlaying,N)local t=T.map(Q,Y)S:transportByKillingPlayers(t)else v:d("\071\097\109\101 \119\105\108\108 \101\110\100 \119\105\116\104 \097\108\108 \112\108\097\121\101\114 \102\105\110\105\115\104\101\100 \102\111\114 \114\111\111\109 "..g)end w:dispatch(t(g,os.time()))O:clean(j)w:dispatch(C(g,W.afterFinishWaitTime,"\070\105\110\105\115\104\101\100 - \067\111\111\108\100\111\119\110"))wait(W.afterFinishWaitTime)w:dispatch(c(g))w:dispatch(C(g,nil,"\087\097\105\116\105\110\103 \112\108\097\121\101\114\115"))end)end end return s
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local Modules = ReplicatedStorage:WaitForChild('Modules')
local logger = require(Modules.src.utils.Logger)
local Players = game:GetService('Players')
local setRoomCountDown = require(Modules.src.actions.rooms.setRoomCountDown)
local setRoomStartTime = require(Modules.src.actions.rooms.setRoomStartTime)
local setRoomEndTime = require(Modules.src.actions.rooms.setRoomEndTime)
local resetRoom = require(Modules.src.actions.rooms.resetRoom)
local clientStartGame = require(Modules.src.actions.toClient.clientStartGame)
local playerFinishedRoom = require(Modules.src.thunks.playerFinishedRoom)

local Transporter = require(Modules.src.Transporter)
local M = require(Modules.M)
local TouchItem = require(Modules.src.TouchItem)
local MazeGenerator = require(Modules.src.MazeGenerator)

local GlobalConfig = require(Modules.src.GlobalConfig)

local Place = game.Workspace:WaitForChild('Place')
local MapsFolder = Place:findFirstChild('Maps')

local function isPlayerPlaying(player)
	return player.finishTime == nil
end

local function startGame(roomId)
	return function(store)
		logger:i('Starting game for room:' .. roomId)

		local room = store:getState().rooms[roomId]

		if not MapsFolder then
			logger:w('Maps folder does not exists!')
			return
		end

		local mapObj = MapsFolder:findFirstChild(room.modelName)

		if not mapObj then
			logger:w('Maps folder  is missing ' .. room.modelName .. ' object!')
			return
		end

		local location = mapObj:FindFirstChild('LocationPart')

		local defaultSettings = {
			width = room.config.width,
			height = room.config.height,
			wallMaterial = Enum.Material.Grass,
			groundMaterial = Enum.Material.Sand,
			onlyBlocks = false,
			addRandomModels = true,
			addStartAndFinish = true,
			addKillBlocks = true,
			addCeiling = false,
			partThickness = 3,
			ceilingFloorThickness = 1,
			location = location,
			blockHeight = 20,
			blockWidth = 25,
			randomStuffFolder = 'Light',
		}

		local darkSettings = {
			width = room.config.width,
			height = room.config.height,
			wallMaterial = Enum.Material.Basalt,
			groundMaterial = Enum.Material.Mud,
			onlyBlocks = false,
			addRandomModels = true,
			addStartAndFinish = true,
			addKillBlocks = false,
			addCeiling = true,
			partThickness = 5,
			ceilingFloorThickness = 5,
			location = location,
			blockHeight = 25,
			blockWidth = 30,
			randomStuffFolder = 'Dark',
		}

		local initialVotes = {
			nil_1 = 'Dark',
			nil_2 = 'Light',
		}
		local playerVotes = M.extend({}, room.playerVotes, initialVotes)
		local votes = M.countBy(playerVotes, function(vote)
			return vote
		end)

		local isDark = votes.Dark > votes.Light

		local settings = isDark and darkSettings or defaultSettings
		MazeGenerator:generate(settings)

		local playTime = isDark and room.config.playTime * 3 or room.config.playTime

		local finishPlaceholder = location:findFirstChild('FinishPlaceholder', true)

		if finishPlaceholder then
			local finishedPlayers = {}

			TouchItem.create(finishPlaceholder, function(player)
				--	self.api:endRoomGame(roomId)
				if not finishedPlayers[player] then
					logger:d('Touched Finish for player:' .. player.Name)
					finishedPlayers[player] = true
					store:dispatch(playerFinishedRoom(player, roomId))
				end
			end)
		else
			logger:w('FinishPlaceholder is missing from ' .. roomId .. ' map object!')
		end

		local playersWaiting = room.playersWaiting

		store:dispatch(setRoomStartTime(roomId, os.time(), playersWaiting))

		local function getPlayerInstance(player)
			return Players:GetPlayerByUserId(player.id)
		end

		local LevelSpawn = mapObj:findFirstChild('SpawnPlaceholder', true)

		if LevelSpawn then
			local playersWaitingInstances = M.map(playersWaiting, getPlayerInstance)

			local function sendToClient(player)
				store:dispatch(clientStartGame(player, roomId))
			end

			M.each(playersWaitingInstances, sendToClient)

			Transporter:transportPlayers(playersWaitingInstances, LevelSpawn)
			--[[	M.each(playersWaitingInstances, function(player)
				addPlayerDiedListener(store, roomId, player)
			end)]]
		else
			logger:w('SpawnPlaceholder is missing from ' .. roomId .. ' map object!')
		end

		spawn(function()
			local gameEndedEvent = Instance.new('BindableEvent')
			store:dispatch(setRoomCountDown(roomId, playTime, 'Find exit', 'Wait'))

			delay(playTime, function()
				gameEndedEvent:Fire(true)
			end)

			spawn(function()
				while true do
					wait(0.1)

					local room = store:getState().rooms[roomId]
					local playersPlaying = M.select(room.playersPlaying, isPlayerPlaying)

					if M.count(playersPlaying) == 0 then
						gameEndedEvent:Fire(false)
						break
					end
				end
			end)

			local gameWillEnd = gameEndedEvent.event:Wait()

			if gameWillEnd then
				logger:d('Game will ended with timer for room ' .. roomId)
				local state = store:getState()
				local room = state.rooms[roomId]
				local playersPlaying = M.select(room.playersPlaying, isPlayerPlaying)
				local playersPlayingInstances = M.map(playersPlaying, getPlayerInstance)

				Transporter:transportByKillingPlayers(playersPlayingInstances)
			else
				logger:d('Game will end with all player finished for room ' .. roomId)
			end

			store:dispatch(setRoomEndTime(roomId, os.time()))

			MazeGenerator:clean(settings)

			store:dispatch(
				setRoomCountDown(roomId, GlobalConfig.afterFinishWaitTime, 'Finished - Cooldown')
			)
			wait(GlobalConfig.afterFinishWaitTime)

			store:dispatch(resetRoom(roomId))
			store:dispatch(setRoomCountDown(roomId, nil, 'Waiting players'))
		end)
	end
end

return startGame