local function D(d,c,J)for c,K in pairs(c)do if type(K)=="\116\097\098\108\101"then D(d,K,J..(c.."\046"))elseif type(K)=="\102\117\110\099\116\105\111\110"then table.insert(d,{J..c;K})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(K)..("\" \040"..(typeof(K)..("\041\059 \110\097\109\101\095\115\116\097\099\107 \061 \""..(J.."\""))))))end end end local function d(d)local c=LoadedWriteLibPacks[d]if c~=nil then return c end local J=require(d)local K={}D(K,J,"")table.sort(K,function(D,d)return D[1]<d[1]end)local r={}local b={}for D,d in ipairs(K)do r[D]=d[2]b[d[1]]=D end local f={r;b}LoadedWriteLibPacks[d]=f return f end local function c(D)local d={}if D~=""then for D in string.gmatch(D,"\091\094\037\046\093\043")do table.insert(d,D)end end return d end local function J(D,d)for D,d in ipairs(D.Children)do J(d,true)end local c=D.Id Replicas[c]=nil D._maid:Cleanup()if d~=true then if D.Parent~=nil then local d=D.Parent.Children table.remove(d,table.find(d,D))end end ChildListeners[c]=nil end local function K(D,d,c)local J=D._table_listeners for D=1,#d,1 do local c=J[1][d[D]]if c==nil then c={[1]={}}J[1][d[D]]=c end J=c end local K=J[c]if K==nil then K={}J[c]=K end return K end local function r(D)local d=D[1]local c=D[2]local J=d local K={J}for D=1,#c,1 do J=J[1][c[D]]table.insert(K,J)end for D=#K,2,-1 do local d=K[D]if next(d[1])~=nil then return end for D=2,6,1 do if d[D]~=nil then if#d[D]>0 then return end end end K[D-1][1][c[D-1]]=nil end end local function b(D,c)local J={}for D,d in pairs(D)do d[6]=tonumber(D)table.insert(J,d)end table.sort(J,function(D,d)return D[6]<d[6]end)local K={}c=c or{}for D,J in ipairs(J)do local r=J[6]local b=J[4]local f=nil local u=false if b~=0 then f=Replicas[b]if f==nil then u=true end end local k,z if J[5]~=nil then local D=d(J[5])k=D[1]z=D[2]end local i={Data=J[3],Id=r;Class=J[1];Tags=J[2];Parent=f;Children={},_write_lib=k;_write_lib_dictionary=z;_table_listeners={[1]={}},_function_listeners={};_raw_listeners={};_signal_listeners={},_maid=MadworkMaid.NewMaid()}setmetatable(i,Replica)if f~=nil then table.insert(f.Children,i)elseif u==true then local D=K[b]if D==nil then D={}K[b]=D end table.insert(D,i)end Replicas[r]=i table.insert(c,i)local l=K[r]if l~=nil then K[r]=nil for D,d in ipairs(l)do d.Parent=i table.insert(i.Children,d)end end end if next(K)~=nil then local D="\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 d,c in pairs(K)do D=D..("\091"..(tostring(d).."\093\058 \123"))for d,c in ipairs(c)do D=D..((d==1 and""or"\044 ")..c:Identify())end D=D.."\125\059 "end error(D)end return c end


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
