local function J(v,L,Y)for L,b in pairs(L)do if type(b)=="\116\097\098\108\101"then J(v,b,Y..(L.."\046"))elseif type(b)=="\102\117\110\099\116\105\111\110"then table.insert(v,{Y..L,b})else error("\091\082\101\112\108\105\099\097\067\111\110\116\114\111\108\108\101\114\093\058 \073\110\118\097\108\105\100 \119\114\105\116\101 \102\117\110\099\116\105\111\110 \118\097\108\117\101 \""..(tostring(b)..("\" \040"..(typeof(b)..("\041\059 \110\097\109\101\095\115\116\097\099\107 \061 \""..(Y.."\""))))))end end end local function v(v)local L=LoadedWriteLibPacks[v]if L~=nil then return L end local Y=require(v)local b={}J(b,Y,"")table.sort(b,function(J,v)return J[1]<v[1]end)local m={}local N={}for J,v in ipairs(b)do m[J]=v[2]N[v[1]]=J end local S={m,N}LoadedWriteLibPacks[v]=S return S end local function L(J)local v={}if J~=""then for J in string.gmatch(J,"\091\094\037\046\093\043")do table.insert(v,J)end end return v end local function Y(J,v)for J,v in ipairs(J.Children)do Y(v,true)end local L=J.Id Replicas[L]=nil J._maid:Cleanup()if v~=true then if J.Parent~=nil then local v=J.Parent.Children table.remove(v,table.find(v,J))end end ChildListeners[L]=nil end local function b(J,v,L)local Y=J._table_listeners for J=1,#v,1 do local L=Y[1][v[J]]if L==nil then L={[1]={}}Y[1][v[J]]=L end Y=L end local b=Y[L]if b==nil then b={}Y[L]=b end return b end local function m(J)local v=J[1]local L=J[2]local Y=v local b={Y}for J=1,#L,1 do Y=Y[1][L[J]]table.insert(b,Y)end for J=#b,2,-1 do local v=b[J]if next(v[1])~=nil then return end for J=2,6,1 do if v[J]~=nil then if#v[J]>0 then return end end end b[J-1][1][L[J-1]]=nil end end local function N(J,L)local Y={}for J,v in pairs(J)do v[6]=tonumber(J)table.insert(Y,v)end table.sort(Y,function(J,v)return J[6]<v[6]end)local b={}L=L or{}for J,Y in ipairs(Y)do local m=Y[6]local N=Y[4]local S=nil local R=false if N~=0 then S=Replicas[N]if S==nil then R=true end end local V,Q if Y[5]~=nil then local J=v(Y[5])V=J[1]Q=J[2]end local f={Data=Y[3];Id=m,Class=Y[1],Tags=Y[2],Parent=S;Children={},_write_lib=V,_write_lib_dictionary=Q,_table_listeners={[1]={}};_function_listeners={},_raw_listeners={};_signal_listeners={},_maid=MadworkMaid.NewMaid()}setmetatable(f,Replica)if S~=nil then table.insert(S.Children,f)elseif R==true then local J=b[N]if J==nil then J={}b[N]=J end table.insert(J,f)end Replicas[m]=f table.insert(L,f)local z=b[m]if z~=nil then b[m]=nil for J,v in ipairs(z)do v.Parent=f table.insert(f.Children,v)end end end if next(b)~=nil then local J="\091\082\101\112\108\105\099\097\083\101\114\118\105\099\101\093\058 \066\082\065\078\067\072 \082\069\080\076\073\067\065\084\073\079\078 \069\082\082\079\082 - \077\105\115\115\105\110\103 \112\097\114\101\110\116\115\058 "for v,L in pairs(b)do J=J..("\091"..(tostring(v).."\093\058 \123"))for v,L in ipairs(L)do J=J..((v==1 and""or"\044 ")..L:Identify())end J=J.."\125\059 "end error(J)end return L end
----- Private functions -----

local function GetWriteLibFunctionsRecursive(list_table, pointer, name_stack)
	for key, value in pairs(pointer) do
		if type(value) == "table" then
			GetWriteLibFunctionsRecursive(list_table, value, name_stack .. key .. ".")
		elseif type(value) == "function" then
			table.insert(list_table, { name_stack .. key, value })
		else
			error(
				'[ReplicaController]: Invalid write function value "'
					.. tostring(value)
					.. '" ('
					.. typeof(value)
					.. '); name_stack = "'
					.. name_stack
					.. '"'
			)
		end
	end
end

local function LoadWriteLib(write_lib_module)
	local get_write_lib_pack = LoadedWriteLibPacks[write_lib_module]
	if get_write_lib_pack ~= nil then
		return get_write_lib_pack
	end

	local write_lib_raw = require(write_lib_module)

	local function_list = {} -- func_id = {func_name, func}

	GetWriteLibFunctionsRecursive(function_list, write_lib_raw, "")
	table.sort(function_list, function(item1, item2)
		return item1[1] < item2[1] -- Sort functions by their names - this creates a consistent indexing on server and client-side
	end)

	local write_lib = {} -- {[func_id] = function, ...}
	local write_lib_dictionary = {} -- {["function_name"] = func_id, ...}

	for func_id, func_params in ipairs(function_list) do
		write_lib[func_id] = func_params[2]
		write_lib_dictionary[func_params[1]] = func_id
	end

	local write_lib_pack = { write_lib, write_lib_dictionary }

	LoadedWriteLibPacks[write_lib_module] = write_lib_pack

	return write_lib_pack
end

local function StringPathToArray(path)
	local path_array = {}
	if path ~= "" then
		for s in string.gmatch(path, "[^%.]+") do
			table.insert(path_array, s)
		end
	end
	return path_array
end

local function DestroyReplicaAndDescendantsRecursive(replica, not_first_in_stack)
	-- Scan children replicas:
	for _, child in ipairs(replica.Children) do
		DestroyReplicaAndDescendantsRecursive(child, true)
	end

	local id = replica.Id
	-- Clear replica entry:
	Replicas[id] = nil
	-- Cleanup:
	replica._maid:Cleanup()
	-- Clear from children table of top parent replica:
	if not_first_in_stack ~= true then
		if replica.Parent ~= nil then
			local children = replica.Parent.Children
			table.remove(children, table.find(children, replica))
		end
	end
	-- Clear child listeners:
	ChildListeners[id] = nil
end

local function CreateTableListenerPathIndex(replica, path_array, listener_type)
	-- Getting listener table:
	local listeners = replica._table_listeners
	-- Getting and or creating the structure nescessary to index the path for the listened key:
	for i = 1, #path_array do
		local key_listeners = listeners[1][path_array[i]]
		if key_listeners == nil then
			key_listeners = { [1] = {} }
			listeners[1][path_array[i]] = key_listeners
		end
		listeners = key_listeners
	end

	local listener_type_table = listeners[listener_type]
	if listener_type_table == nil then
		listener_type_table = {}
		listeners[listener_type] = listener_type_table
	end
	return listener_type_table
end

local function CleanTableListenerTable(disconnect_param)
	local table_listeners = disconnect_param[1]
	local path_array = disconnect_param[2]
	local pointer = table_listeners
	local pointer_stack = { pointer }
	for i = 1, #path_array do
		pointer = pointer[1][path_array[i]]
		table.insert(pointer_stack, pointer)
	end
	for i = #pointer_stack, 2, -1 do
		local listeners = pointer_stack[i]
		if next(listeners[1]) ~= nil then
			return -- Lower branches exist for this branch - this branch will not need further cleanup
		end
		for k = 2, 6 do
			if listeners[k] ~= nil then
				if #listeners[k] > 0 then
					return -- A listener exists - this branch will not need further cleanup
				end
			end
		end
		pointer_stack[i - 1][1][path_array[i - 1]] = nil -- Clearing listeners table for this branch
	end
end

local function CreateReplicaBranch(replica_entries, created_replicas) --> created_replicas: {replica, ...}
	-- Sorting replica entries:
	-- replica_entries = {[replica_id_string] = {replica_class, replica_tags, data_table, parent_id / 0, write_lib_module / nil}, ...}
	local sorted_replica_entries = {} -- {replica_class, replica_tags, data_table, parent_id / 0, write_lib_module / nil, replica_id}, ...}
	for replica_id_string, replica_entry in pairs(replica_entries) do
		replica_entry[6] = tonumber(replica_id_string)
		table.insert(sorted_replica_entries, replica_entry)
	end
	table.sort(sorted_replica_entries, function(a, b)
		return a[6] < b[6]
	end)
	local waiting_for_parent = {} -- [parent_replica_id] = {replica, ...}
	created_replicas = created_replicas or {}
	for _, replica_entry in ipairs(sorted_replica_entries) do
		local replica_id = replica_entry[6]
		-- Fetching replica parent:
		local parent_id = replica_entry[4]
		local parent = nil
		local wait_for_parent = false
		if parent_id ~= 0 then
			parent = Replicas[parent_id]
			if parent == nil then
				wait_for_parent = true
			end
		end
		-- Fetching replica write_lib:
		local write_lib, write_lib_dictionary
		if replica_entry[5] ~= nil then
			local write_lib_pack = LoadWriteLib(replica_entry[5])
			write_lib = write_lib_pack[1]
			write_lib_dictionary = write_lib_pack[2]
		end
		-- New Replica object table:
		local replica = {
			Data = replica_entry[3],
			Id = replica_id,
			Class = replica_entry[1],
			Tags = replica_entry[2],

			Parent = parent,
			Children = {},

			_write_lib = write_lib,
			_write_lib_dictionary = write_lib_dictionary,

			_table_listeners = { [1] = {} },
			_function_listeners = {},
			_raw_listeners = {},

			_signal_listeners = {},
			_maid = MadworkMaid.NewMaid(),
		}
		setmetatable(replica, Replica)
		-- Setting as child to parent:
		if parent ~= nil then
			table.insert(parent.Children, replica)
		elseif wait_for_parent == true then
			local wait_table = waiting_for_parent[parent_id]
			if wait_table == nil then
				wait_table = {}
				waiting_for_parent[parent_id] = wait_table
			end
			table.insert(wait_table, replica)
		end
		-- Adding replica to replica list:
		Replicas[replica_id] = replica
		table.insert(created_replicas, replica)
		-- Checking replicas waiting for parent:
		local children_waiting = waiting_for_parent[replica_id]
		if children_waiting ~= nil then
			waiting_for_parent[replica_id] = nil
			for _, child_replica in ipairs(children_waiting) do
				child_replica.Parent = replica
				table.insert(replica.Children, child_replica)
			end
		end
	end
	if next(waiting_for_parent) ~= nil then
		-- An error occured while replicating an replica branch.
		local error_string = "[ReplicaService]: BRANCH REPLICATION ERROR - Missing parents: "
		for parent_replica_id, child_replicas in pairs(waiting_for_parent) do
			error_string = error_string .. "[" .. tostring(parent_replica_id) .. "]: {"
			for k, replica in ipairs(child_replicas) do
				error_string = error_string .. (k == 1 and "" or ", ") .. replica:Identify()
			end
			error_string = error_string .. "}; "
		end
		error(error_string)
	end
	return created_replicas
end
