local t=game:GetService("\067\111\108\108\101\099\116\105\111\110\083\101\114\118\105\099\101")local f=script.Parent local h=require(script.Parent.Utils)local x=require(script.Parent.BounceTemplateUtils)local g={}g.DEPENDENCY_FOLDER_NAME="\110\111\100\101\095\109\111\100\117\108\101\115"g.ModuleReplicationTypes=h.readonly({CLIENT="\099\108\105\101\110\116";SERVER="\115\101\114\118\101\114",SHARED="\115\104\097\114\101\100",IGNORE="\105\103\110\111\114\101"})function g.createScriptInfo(t,f,x)assert(typeof(t)=="\073\110\115\116\097\110\099\101","\066\097\100 \105\110\115\116\097\110\099\101")assert(type(f)=="\115\116\114\105\110\103","\066\097\100 \110\097\109\101")assert(type(x)=="\115\116\114\105\110\103","\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")return h.readonly({name=f,replicationMode=x;instance=t})end function g.createScriptInfoLookup()return h.readonly({[g.ModuleReplicationTypes.SERVER]={},[g.ModuleReplicationTypes.CLIENT]={},[g.ModuleReplicationTypes.SHARED]={}})end function g.getScriptInfoLookupForMode(t,f)assert(type(t)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(f)=="\115\116\114\105\110\103","\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")return t[f]end function g.populateScriptInfoLookup(t,h,W)assert(typeof(t)=="\073\110\115\116\097\110\099\101","\066\097\100 \105\110\115\116\097\110\099\101")assert(type(h)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(W)=="\115\116\114\105\110\103","\066\097\100 \108\097\115\116\082\101\112\108\105\099\097\116\105\111\110\077\111\100\101")if t:IsA("\070\111\108\100\101\114")then local f=g.getFolderReplicationMode(t.Name,W)if f~=g.ModuleReplicationTypes.IGNORE then for t,W in pairs(t:GetChildren())do if not x.isBounceTemplate(W)then if W:IsA("\070\111\108\100\101\114")then g.populateScriptInfoLookup(W,h,f)elseif W:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then g.addToInfoMap(h,g.createScriptInfo(W,W.Name,f))end end end end elseif t:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then if not x.isBounceTemplate(t)then if t==f then g.addToInfoMap(h,g.createScriptInfo(t,"\108\111\097\100\101\114",W))else g.addToInfoMap(h,g.createScriptInfo(t,t.Name,W))end end elseif t:IsA("\079\098\106\101\099\116\086\097\108\117\101")then error("\079\098\106\101\099\116\086\097\108\117\101 \108\105\110\107\115 \097\114\101 \110\111\116 \115\117\112\112\111\114\116\101\100 \097\116 \116\104\105\115 \116\105\109\101 \102\111\114 \114\101\116\114\105\101\118\105\110\103 \105\110\108\105\110\101 \109\111\100\117\108\101 \115\099\114\105\112\116\115")end end local W={HoldingBindersServer=true;HoldingBindersClient=true;IKService=true;IKServiceClient=true}function g.isAvailableInShared(f)if t:HasTag(f.instance,"\076\105\110\107\084\111\083\104\097\114\101\100")then return true end return W[f.name]end function g.addToInfoMap(t,f)assert(type(t)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(f)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111")local h=assert(f.replicationMode,"\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")local x=assert(t[h],"\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")g.addToInfoMapForMode(x,f)if h==g.ModuleReplicationTypes.SHARED then g.addToInfoMapForMode(t[g.ModuleReplicationTypes.SERVER],f)g.addToInfoMapForMode(t[g.ModuleReplicationTypes.CLIENT],f)elseif g.isAvailableInShared(f)then g.addToInfoMapForMode(t[g.ModuleReplicationTypes.SHARED],f)end end function g.addToInfoMapForMode(t,f)if t[f.name]then warn(("\068\117\112\108\105\099\097\116\101 \109\111\100\117\108\101 \037\113 \105\110 \115\097\109\101 \112\097\099\107\097\103\101 \117\110\100\101\114 \115\097\109\101 \114\101\112\108\105\099\097\116\105\111\110 \115\099\111\112\101\046 \079\110\108\121 \117\115\105\110\103 \102\105\114\115\116 \111\110\101\046 \n- \037\113\n- \037\113"):format(f.name,f.instance:GetFullName(),t[f.name]:GetFullName()))return end t[f.name]=f end function g.getFolderReplicationMode(t,f)assert(type(t)=="\115\116\114\105\110\103","\066\097\100 \102\111\108\100\101\114\078\097\109\101")assert(type(f)=="\115\116\114\105\110\103","\066\097\100 \108\097\115\116\082\101\112\108\105\099\097\116\105\111\110\077\111\100\101")if t=="\083\104\097\114\101\100"then return g.ModuleReplicationTypes.SHARED elseif t=="\067\108\105\101\110\116"then return g.ModuleReplicationTypes.CLIENT elseif t=="\083\101\114\118\101\114"then return g.ModuleReplicationTypes.SERVER elseif t==g.DEPENDENCY_FOLDER_NAME then return g.ModuleReplicationTypes.IGNORE else return f end end return g


local CollectionService = game:GetService("CollectionService")

local loader = script.Parent
local Utils = require(script.Parent.Utils)
local BounceTemplateUtils = require(script.Parent.BounceTemplateUtils)

local ScriptInfoUtils = {}

ScriptInfoUtils.DEPENDENCY_FOLDER_NAME = "node_modules";
ScriptInfoUtils.ModuleReplicationTypes = Utils.readonly({
	CLIENT = "client";
	SERVER = "server";
	SHARED = "shared";
	IGNORE = "ignore";
})

function ScriptInfoUtils.createScriptInfo(instance, name, replicationMode)
	assert(typeof(instance) == "Instance", "Bad instance")
	assert(type(name) == "string", "Bad name")
	assert(type(replicationMode) == "string", "Bad replicationMode")

	return Utils.readonly({
		name = name;
		replicationMode = replicationMode;
		instance = instance;
	})
end

function ScriptInfoUtils.createScriptInfoLookup()
	-- Server/client also contain shared entries
	return Utils.readonly({
		[ScriptInfoUtils.ModuleReplicationTypes.SERVER] = {}; -- [string name] = scriptInfo
		[ScriptInfoUtils.ModuleReplicationTypes.CLIENT] = {};
		[ScriptInfoUtils.ModuleReplicationTypes.SHARED] = {};
	})
end

function ScriptInfoUtils.getScriptInfoLookupForMode(scriptInfoLookup, replicationMode)
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(replicationMode) == "string", "Bad replicationMode")

	return scriptInfoLookup[replicationMode]
end

function ScriptInfoUtils.populateScriptInfoLookup(instance, scriptInfoLookup, lastReplicationMode)
	assert(typeof(instance) == "Instance", "Bad instance")
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(lastReplicationMode) == "string", "Bad lastReplicationMode")

	if instance:IsA("Folder") then
		local replicationMode = ScriptInfoUtils.getFolderReplicationMode(instance.Name, lastReplicationMode)
		if replicationMode ~= ScriptInfoUtils.ModuleReplicationTypes.IGNORE then
			for _, item in pairs(instance:GetChildren()) do
				if not BounceTemplateUtils.isBounceTemplate(item) then
					if item:IsA("Folder") then
						ScriptInfoUtils.populateScriptInfoLookup(item, scriptInfoLookup, replicationMode)
					elseif item:IsA("ModuleScript") then
						ScriptInfoUtils.addToInfoMap(scriptInfoLookup,
							ScriptInfoUtils.createScriptInfo(item, item.Name, replicationMode))
					end
				end
			end
		end
	elseif instance:IsA("ModuleScript") then
		if not BounceTemplateUtils.isBounceTemplate(instance) then
			if instance == loader then
				-- STRICT hack to support this module script as "loader" over "Nevermore" in replicated scenario
				ScriptInfoUtils.addToInfoMap(scriptInfoLookup,
					ScriptInfoUtils.createScriptInfo(instance, "loader", lastReplicationMode))
			else
				ScriptInfoUtils.addToInfoMap(scriptInfoLookup,
					ScriptInfoUtils.createScriptInfo(instance, instance.Name, lastReplicationMode))
			end
		end
	elseif instance:IsA("ObjectValue") then
		error("ObjectValue links are not supported at this time for retrieving inline module scripts")
	end
end

local AVAILABLE_IN_SHARED = {
	["HoldingBindersServer"] = true;
	["HoldingBindersClient"] = true;
	["IKService"] = true;
	["IKServiceClient"] = true;
}

function ScriptInfoUtils.isAvailableInShared(scriptInfo)
	if CollectionService:HasTag(scriptInfo.instance, "LinkToShared") then
		return true
	end

	-- Hack because we can't tag things in Rojo yet
	return AVAILABLE_IN_SHARED[scriptInfo.name]
end

function ScriptInfoUtils.addToInfoMap(scriptInfoLookup, scriptInfo)
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(scriptInfo) == "table", "Bad scriptInfo")

	local replicationMode = assert(scriptInfo.replicationMode, "Bad replicationMode")
	local replicationMap = assert(scriptInfoLookup[replicationMode], "Bad replicationMode")

	ScriptInfoUtils.addToInfoMapForMode(replicationMap, scriptInfo)

	if replicationMode == ScriptInfoUtils.ModuleReplicationTypes.SHARED then
		ScriptInfoUtils.addToInfoMapForMode(
			scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.SERVER], scriptInfo)
		ScriptInfoUtils.addToInfoMapForMode(
			scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.CLIENT], scriptInfo)
	elseif ScriptInfoUtils.isAvailableInShared(scriptInfo) then
		ScriptInfoUtils.addToInfoMapForMode(
			scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.SHARED], scriptInfo)
	end
end

function ScriptInfoUtils.addToInfoMapForMode(replicationMap, scriptInfo)
	if replicationMap[scriptInfo.name] then
		warn(("Duplicate module %q in same package under same replication scope. Only using first one. \n- %q\n- %q")
			:format(scriptInfo.name,
				scriptInfo.instance:GetFullName(),
				replicationMap[scriptInfo.name]:GetFullName()))
		return
	end

	replicationMap[scriptInfo.name] = scriptInfo
end

function ScriptInfoUtils.getFolderReplicationMode(folderName, lastReplicationMode)
	assert(type(folderName) == "string", "Bad folderName")
	assert(type(lastReplicationMode) == "string", "Bad lastReplicationMode")

	if folderName == "Shared" then
		return ScriptInfoUtils.ModuleReplicationTypes.SHARED
	elseif folderName == "Client" then
		return ScriptInfoUtils.ModuleReplicationTypes.CLIENT
	elseif folderName == "Server" then
		return ScriptInfoUtils.ModuleReplicationTypes.SERVER
	elseif folderName == ScriptInfoUtils.DEPENDENCY_FOLDER_NAME then
		return ScriptInfoUtils.ModuleReplicationTypes.IGNORE
	else
		return lastReplicationMode
	end
end

return ScriptInfoUtils
