--Dupes a part from the template.


local function MakeFromTemplate(template: BasePart, currentCacheParent: Instance): BasePart
	local part: BasePart = template:Clone()
	-- ^ Ignore W000 type mismatch between Instance and BasePart. False alert.

	part.CFrame = CF_REALLY_FAR_AWAY
	part.Anchored = true
	part.Parent = currentCacheParent
	return part
end

function PartCacheStatic.new(orgTemplate: BasePart, numPrecreatedParts: number?, currentCacheParent: Instance?): PartCache
	local newNumPrecreatedParts: number = numPrecreatedParts or 5
	local newCurrentCacheParent: Instance = currentCacheParent or workspace

	--PrecreatedParts value.
	--Same thing. Ensure it's a number, ensure it's not negative, warn if it's really huge or 0.
	assert(numPrecreatedParts > 0, "PrecreatedParts can not be negative!")
	assertwarn(numPrecreatedParts ~= 0, "PrecreatedParts is 0! This may have adverse effects when initially using the cache.")
	assertwarn(orgTemplate.Archivable, "The template's Archivable property has been set to false, which prevents it from being cloned. It will temporarily be set to true.")

	local oldArchivable = orgTemplate.Archivable
	orgTemplate.Archivable = true
	local newTemplate: BasePart = orgTemplate:Clone()
	-- ^ Ignore W000 type mismatch between Instance and BasePart. False alert.

	orgTemplate.Archivable = oldArchivable
	local template: BasePart = newTemplate

	local trails = {}
	for _, v in pairs(template:GetChildren()) do
		if (v:IsA("Trail") or v:IsA("Beam") or v:IsA("ParticleEmitter")) and v.Enabled then
			table.insert(trails, #trails+1, v.Name)
			v.Enabled = false
		end
	end

	local object: PartCache = {
		Open = {},
		InUse = {},
		CurrentCacheParent = newCurrentCacheParent,
		Template = template,
		ExpansionSize = 10,
		Trails = trails
	}
	setmetatable(object, PartCacheStatic)

	-- Below: Ignore type mismatch nil | number and the nil | Instance mismatch on the table.insert line.
	for _ = 1, newNumPrecreatedParts do
		table.insert(object.Open, MakeFromTemplate(template, object.CurrentCacheParent))
	end

	local function getFullNameRelative(main, child): string
		local fullName: string = child:GetFullName()
		local pos: number = string.find(fullName, main.Name)
		local start: number = pos + #main.Name + 1
		local rest: string = string.sub(fullName, start, -1)
		return rest
	end

	object.Template.Parent = nil
	
	for _, changed in ipairs(table.pack(orgTemplate, unpack(orgTemplate:GetDescendants()))) do
		local path = changed ~= orgTemplate and getFullNameRelative(orgTemplate, changed) or nil
		local function getTarget(v)
			local target: Instance = v
			if path ~= nil then
				for _, name: string in ipairs(string.split(path, ".")) do
					target = target[name]
				end
			end
			return target
		end
		pcall(function()
			changed.Changed:Connect(function(property)
				if property == "Parent" then return end
				
				for _, v: Instance in ipairs(object.Open) do
					local target: Instance = getTarget(v)
					target[property] = changed[property]
				end
				for _, v: Instance in ipairs(object.InUse) do
					local target: Instance = getTarget(v)
					target[property] = changed[property]
				end
			end)
		end)
	end
	
	return object
	-- ^ Ignore mismatch here too
end