-- Serializer.lua

local Serializer = {}

local HttpService = game:GetService("HttpService")
local ScriptGenerator = require(script.Parent.ScriptGenerator)
local RobloxApiDump = require(script.Parent.RobloxApiDump)

-- Function to serialize a value
function Serializer.serializeValue(value, instanceMap, serializedData, instancesQueue)
	local valueType = typeof(value)

	-- Handle unsupported types
	local unsupportedTypes = {
		["function"] = true,
		["userdata"] = true,
		["RBXScriptConnection"] = true,
		["RBXScriptSignal"] = true,
		["thread"] = true,
	}

	if unsupportedTypes[valueType] then
		return nil
	end

	-- Handle specific types
	if valueType == "EnumItem" then
		return { __type = "EnumItem", Value = tostring(value) }
	elseif valueType == "BrickColor" then
		return { __type = "BrickColor", Number = value.Number }
	elseif valueType == "Color3" then
		return { __type = "Color3", R = value.R, G = value.G, B = value.B }
	elseif valueType == "Vector3" then
		return { __type = "Vector3", X = value.X, Y = value.Y, Z = value.Z }
	elseif valueType == "Vector2" then
		return { __type = "Vector2", X = value.X, Y = value.Y }
	elseif valueType == "CFrame" then
		return { __type = "CFrame", Components = { value:GetComponents() } }
	elseif valueType == "UDim2" then
		return { __type = "UDim2", XScale = value.X.Scale, XOffset = value.X.Offset, YScale = value.Y.Scale, YOffset = value.Y.Offset }
	elseif valueType == "UDim" then
		return { __type = "UDim", Scale = value.Scale, Offset = value.Offset }
	elseif valueType == "NumberRange" then
		return { __type = "NumberRange", Min = value.Min, Max = value.Max }
	elseif valueType == "NumberSequence" then
		local keypoints = {}
		for _, keypoint in ipairs(value.Keypoints) do
			table.insert(keypoints, { Time = keypoint.Time, Value = keypoint.Value, Envelope = keypoint.Envelope })
		end
		return { __type = "NumberSequence", Keypoints = keypoints }
	elseif valueType == "ColorSequence" then
		local keypoints = {}
		for _, keypoint in ipairs(value.Keypoints) do
			table.insert(keypoints, {
				Time = keypoint.Time,
				Value = { R = keypoint.Value.R, G = keypoint.Value.G, B = keypoint.Value.B },
			})
		end
		return { __type = "ColorSequence", Keypoints = keypoints }
	elseif valueType == "Rect" then
		return { __type = "Rect", Min = { X = value.Min.X, Y = value.Min.Y }, Max = { X = value.Max.X, Y = value.Max.Y } }
	elseif valueType == "PhysicalProperties" then
		return {
			__type = "PhysicalProperties",
			Density = value.Density,
			Friction = value.Friction,
			Elasticity = value.Elasticity,
			FrictionWeight = value.FrictionWeight,
			ElasticityWeight = value.ElasticityWeight,
		}
	elseif valueType == "Font" then
		return {
			__type = "Font",
			Family = value.Family,
			Weight = tostring(value.Weight),
			Style = tostring(value.Style),
		}
	elseif valueType == "Faces" then
		-- Serialize Faces as a table of booleans
		return {
			__type = "Faces",
			Top = value.Top,
			Bottom = value.Bottom,
			Left = value.Left,
			Right = value.Right,
			Front = value.Front,
			Back = value.Back,
		}
	elseif valueType == "Axes" then
		-- Serialize Axes as a table of booleans
		return {
			__type = "Axes",
			X = value.X,
			Y = value.Y,
			Z = value.Z,
		}
	elseif valueType == "Instance" then
		-- Handle instance references
		if instanceMap[value] then
			-- Reference to a serialized instance
			return { __type = "Instance", __ref = instanceMap[value] }
		else
			-- The instance has not been serialized yet
			-- We need to serialize it now
			local success, err = pcall(function()
				Serializer.serializeInstance(value, instanceMap, serializedData, instancesQueue)
			end)
			if success then
				return { __type = "Instance", __ref = instanceMap[value] }
			else
				-- Could not serialize the instance; return nil or handle appropriately
				warn("Failed to serialize instance reference:", tostring(value), "-", err)
				return nil
			end
		end
	else
		-- For basic types like string, number, boolean, Content, etc.
		return value
	end
end

-- Function to serialize an instance
function Serializer.serializeInstance(instance, instanceMap, serializedData, instancesQueue)
	-- Early exit if instance is already processed
	if instanceMap[instance] then
		return
	end

	-- Exclude instances that should not be serialized
	if instance == game or instance:IsA("CoreScript") then
		return
	end

	local success, className = pcall(function()
		return instance.ClassName
	end)

	if not success then
		warn("Failed to get ClassName for instance:", tostring(instance))
		return
	end

	-- Generate a unique ID for the instance
	local instanceId = HttpService:GenerateGUID(false)
	instanceMap[instance] = instanceId

	local serialized = {
		ClassName = className,
		__id = instanceId,
		Properties = {},
		Attributes = {},
	}

	-- Special handling for MeshPart and UnionOperation
	if className == "MeshPart" or className == "UnionOperation" then
		-- Create a new Part to replace the MeshPart or UnionOperation
		local newPart = Instance.new("Part")

		-- Copy basic properties from the original instance
		newPart.Name = instance.Name
		newPart.Color = instance.Color
		newPart.Material = instance.Material
		newPart.Transparency = instance.Transparency
		newPart.Reflectance = instance.Reflectance
		newPart.Anchored = instance.Anchored
		newPart.CanCollide = instance.CanCollide
		newPart.CastShadow = instance.CastShadow
		newPart.Locked = instance.Locked
		newPart.Massless = instance.Massless
		newPart.CFrame = instance.CFrame

		-- Adjust the Part's Size and create the SpecialMesh
		newPart.Size = instance.Size * 0.5  -- Scale down the Part's Size by half

		local specialMesh = Instance.new("SpecialMesh")
		specialMesh.MeshType = Enum.MeshType.FileMesh

		if className == "MeshPart" then
			specialMesh.MeshId = instance.MeshId
			specialMesh.TextureId = instance.TextureID
		elseif className == "UnionOperation" then
			specialMesh.MeshId = "rbxassetid://" .. tostring(instance.AssetId)
			-- UnionOperations don't have TextureID
		end

		-- Set the SpecialMesh's Scale to (1, 1, 1)
		specialMesh.Scale = Vector3.new(1, 1, 1)
		specialMesh.Parent = newPart

		-- Map the original instance to the new Part's ID in the instanceMap
		instanceMap[instance] = instanceId
		-- Map the new Part instance to the same ID (in case of children referencing it)
		instanceMap[newPart] = instanceId

		-- Serialize the new Part
		serialized.ClassName = "Part"

		-- Serialize properties of the new Part
		local properties = Serializer.getSerializableProperties(newPart)
		for _, propertyData in ipairs(properties) do
			local propertyName = propertyData.Name

			if propertyName ~= "Parent" then
				local successProp, value = pcall(function()
					return newPart[propertyName]
				end)
				if successProp then
					local serializedValue = Serializer.serializeValue(value, instanceMap, serializedData, instancesQueue)
					if serializedValue ~= nil then
						serialized.Properties[propertyName] = serializedValue
					else
						warn("Property value could not be serialized:", propertyName, "on instance:", newPart:GetFullName())
					end
				else
					warn("Failed to get property:", propertyName, "on instance:", tostring(newPart))
				end
			end
		end

		-- Serialize attributes from the original instance
		local successAttr, attributes = pcall(function()
			return instance:GetAttributes()
		end)
		if successAttr and attributes then
			for attributeName, attributeValue in pairs(attributes) do
				local serializedAttributeValue = Serializer.serializeValue(attributeValue, instanceMap, serializedData, instancesQueue)
				if serializedAttributeValue ~= nil then
					serialized.Attributes[attributeName] = serializedAttributeValue
				end
			end
		else
			warn("Failed to get attributes for instance:", tostring(instance))
		end

		-- Store ParentId to reconstruct the hierarchy
		local successParent, parent = pcall(function()
			return instance.Parent
		end)
		if successParent and parent then
			if instanceMap[parent] then
				-- Parent is a serialized instance
				serialized.ParentId = instanceMap[parent]
			else
				-- Parent is an external instance or service
				if parent == game then
					serialized.ParentIsGame = true
				else
					-- Attempt to get service if parent is a service
					local successService, parentService = pcall(game.GetService, game, parent.ClassName)
					if successService and parentService == parent then
						serialized.ParentIsService = true
						serialized.ParentClassName = parent.ClassName
					else
						serialized.ParentName = parent.Name
						serialized.ParentClassName = parent.ClassName
					end
				end
			end
		else
			-- Instance has no parent; default to game during deserialization
			serialized.ParentIsGame = true
		end

		-- Add serialized data to the list
		table.insert(serializedData, serialized)

		-- Enqueue the SpecialMesh for serialization
		-- Map the SpecialMesh to a new ID
		local meshInstanceId = HttpService:GenerateGUID(false)
		instanceMap[specialMesh] = meshInstanceId

		-- Serialize the SpecialMesh
		local serializedMesh = {
			ClassName = "SpecialMesh",
			__id = meshInstanceId,
			Properties = {},
			Attributes = {},
		}

		-- Serialize properties of the SpecialMesh
		local meshProperties = Serializer.getSerializableProperties(specialMesh)
		for _, propertyData in ipairs(meshProperties) do
			local propertyName = propertyData.Name

			if propertyName ~= "Parent" then
				local successProp, value = pcall(function()
					return specialMesh[propertyName]
				end)
				if successProp then
					local serializedValue = Serializer.serializeValue(value, instanceMap, serializedData, instancesQueue)
					if serializedValue ~= nil then
						serializedMesh.Properties[propertyName] = serializedValue
					else
						warn("Property value could not be serialized:", propertyName, "on instance:", specialMesh:GetFullName())
					end
				else
					warn("Failed to get property:", propertyName, "on instance:", tostring(specialMesh))
				end
			end
		end

		-- Set ParentId of the SpecialMesh to the new Part
		serializedMesh.ParentId = instanceId

		-- Add serialized data of SpecialMesh to the list
		table.insert(serializedData, serializedMesh)

		-- Handle SurfaceAppearance
		local surfaceAppearance = instance:FindFirstChildOfClass("SurfaceAppearance")
		if surfaceAppearance then
			-- SurfaceAppearance cannot be parented to a Part
			-- We'll try to approximate using Textures
			-- Create six Textures (one for each face)
			local faces = {
				Enum.NormalId.Front,
				Enum.NormalId.Back,
				Enum.NormalId.Top,
				Enum.NormalId.Bottom,
				Enum.NormalId.Left,
				Enum.NormalId.Right,
			}
			for _, face in ipairs(faces) do
				local texture = Instance.new("Texture")
				texture.Name = surfaceAppearance.Name .. "_" .. tostring(face)
				texture.Face = face
				-- Set texture properties (assuming ColorMap as Texture)
				texture.Texture = surfaceAppearance.ColorMap
				texture.Transparency = surfaceAppearance.AlphaMode == Enum.AlphaMode.Overlay and surfaceAppearance.Opacity or 0
				texture.Parent = newPart

				-- Serialize the Texture
				local textureInstanceId = HttpService:GenerateGUID(false)
				instanceMap[texture] = textureInstanceId

				local serializedTexture = {
					ClassName = "Texture",
					__id = textureInstanceId,
					Properties = {},
					Attributes = {},
					ParentId = instanceId,
				}

				local textureProperties = Serializer.getSerializableProperties(texture)
				for _, propertyData in ipairs(textureProperties) do
					local propertyName = propertyData.Name
					if propertyName ~= "Parent" then
						local successProp, value = pcall(function()
							return texture[propertyName]
						end)
						if successProp then
							local serializedValue = Serializer.serializeValue(value, instanceMap, serializedData, instancesQueue)
							if serializedValue ~= nil then
								serializedTexture.Properties[propertyName] = serializedValue
							else
								warn("Property value could not be serialized:", propertyName, "on instance:", texture:GetFullName())
							end
						else
							warn("Failed to get property:", propertyName, "on instance:", tostring(texture))
						end
					end
				end

				-- Add serialized data of Texture to the list
				table.insert(serializedData, serializedTexture)
			end
		end

		-- Enqueue other children of the original instance
		local canAccess, children = pcall(function()
			return instance:GetChildren()
		end)
		if canAccess and children then
			for _, child in ipairs(children) do
				if not instanceMap[child] then
					if not child:IsA("SurfaceAppearance") then
						-- Enqueue the child for serialization
						table.insert(instancesQueue, child)
					end
				end
			end
		end

	else
		-- Regular instance serialization

		local properties = Serializer.getSerializableProperties(instance)
		for _, propertyData in ipairs(properties) do
			local propertyName = propertyData.Name

			if propertyName ~= "Parent" then
				local successProp, value = pcall(function()
					return instance[propertyName]
				end)
				if successProp then
					local serializedValue = Serializer.serializeValue(value, instanceMap, serializedData, instancesQueue)
					if serializedValue ~= nil then
						serialized.Properties[propertyName] = serializedValue
					else
						-- Handle serialization failure of property value
						warn("Property value could not be serialized:", propertyName, "on instance:", instance:GetFullName())
					end
				else
					-- Handle property get errors gracefully
					warn("Failed to get property:", propertyName, "on instance:", tostring(instance))
				end
			end
		end

		-- Serialize attributes
		local successAttr, attributes = pcall(function()
			return instance:GetAttributes()
		end)
		if successAttr and attributes then
			for attributeName, attributeValue in pairs(attributes) do
				local serializedAttributeValue = Serializer.serializeValue(attributeValue, instanceMap, serializedData, instancesQueue)
				if serializedAttributeValue ~= nil then
					serialized.Attributes[attributeName] = serializedAttributeValue
				end
			end
		else
			warn("Failed to get attributes for instance:", tostring(instance))
		end

		-- Store ParentId to reconstruct the hierarchy
		local successParent, parent = pcall(function()
			return instance.Parent
		end)
		if successParent and parent then
			if instanceMap[parent] then
				-- Parent is a serialized instance
				serialized.ParentId = instanceMap[parent]
			else
				-- Parent is an external instance or service
				if parent == game then
					serialized.ParentIsGame = true
				else
					-- Attempt to get service if parent is a service
					local successService, parentService = pcall(game.GetService, game, parent.ClassName)
					if successService and parentService == parent then
						serialized.ParentIsService = true
						serialized.ParentClassName = parent.ClassName
					else
						serialized.ParentName = parent.Name
						serialized.ParentClassName = parent.ClassName
					end
				end
			end
		else
			-- Instance has no parent; default to game during deserialization
			serialized.ParentIsGame = true
		end

		-- Add serialized data to the list
		table.insert(serializedData, serialized)

		-- Add immediate children to the queue to process, if any
		local canAccess, children = pcall(function()
			return instance:GetChildren()
		end)
		if canAccess and children then
			for _, child in ipairs(children) do
				if not instanceMap[child] then
					table.insert(instancesQueue, child)
				end
			end
		end
	end
end

-- Function to get serializable properties of an instance
function Serializer.getSerializableProperties(instance)
	local properties = {}
	local className = instance.ClassName
	local classData = RobloxApiDump.Classes[className]

	-- Define collectProperties function
	local function collectProperties(clsData)
		if not clsData then
			-- No class data provided
			return
		end

		-- Recursively collect properties from superclass
		if clsData.Superclass and RobloxApiDump.Classes[clsData.Superclass] then
			collectProperties(RobloxApiDump.Classes[clsData.Superclass])
		end

		-- Process properties
		if clsData.Properties then
			for _, propertyData in ipairs(clsData.Properties) do
				local propertyName = propertyData.Name

				-- Filter out properties based on security and attributes
				local isNotBrowsable = false
				local deprecated = false
				local attributes = propertyData.Attributes or {}

				for _, attribute in ipairs(attributes) do
					if attribute == "NotBrowsable" then
						isNotBrowsable = true
					elseif attribute == "Deprecated" then
						deprecated = true
					end
				end

				-- Include properties that are readable at script security level
				local canRead = propertyData.Security and (propertyData.Security.Read == "None" or propertyData.Security.Read == "PluginSecurity")
				if canRead and not isNotBrowsable and not deprecated then
					table.insert(properties, propertyData)
				end
			end
		end
	end

	if classData then
		collectProperties(classData)
	else
		-- If class data is not available, use default properties
		properties = {
			{ Name = "Name", Security = { Read = "None" } },
			{ Name = "Archivable", Security = { Read = "None" } },
			-- Add other common properties if needed
		}
	end

	return properties
end

-- Function to generate reconstruction scripts
function Serializer.generateReconstructionScripts(serializedData, maxScriptSize)
	-- Use the ScriptGenerator to generate the scripts
	local scripts = ScriptGenerator.generateReconstructionScripts(serializedData, maxScriptSize)
	return scripts
end

-- Function to handle missing instances during serialization
function Serializer.handleMissingInstances(missingInstances, instanceMap, serializedData, instancesQueue)
	for _, instance in ipairs(missingInstances) do
		if not instanceMap[instance] then
			table.insert(instancesQueue, instance)
		end
	end
end

return Serializer Serializer module , -- ScriptGenerator.lua

local ScriptGenerator = {}
local HttpService = game:GetService("HttpService")

function ScriptGenerator.getScriptTemplate()
	local scriptTemplate = [[
-- Reconstruction Script - Part CHUNK_INDEX of TOTAL_CHUNKS

local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")

_G.instanceRegistry = _G.instanceRegistry or {}
_G.parentDataMap = _G.parentDataMap or {}
local instanceRegistry = _G.instanceRegistry
local parentDataMap = _G.parentDataMap

local propertiesToSkip = {
        ["ClassName"] = true,
        ["Parent"] = true,
        ["Archivable"] = true,
}

local function recreateValue(value)
        if type(value) == "table" and value.__type then
                local valueType = value.__type
                if valueType == "EnumItem" then
                        local enumFullName = value.Value
                        local enumTypeName, enumName = enumFullName:match("Enum%.(%w+)%.(.+)")
                        if enumTypeName and enumName then
                                return Enum[enumTypeName][enumName]
                        else
                                warn("Failed to reconstruct EnumItem:", value.Value)
                                return nil
                        end
                elseif valueType == "BrickColor" then
                        return BrickColor.new(value.Number)
                elseif valueType == "Color3" then
                        return Color3.new(value.R, value.G, value.B)
                elseif valueType == "Vector3" then
                        return Vector3.new(value.X, value.Y, value.Z)
                elseif valueType == "Vector2" then
                        return Vector2.new(value.X, value.Y)
                elseif valueType == "CFrame" then
                        return CFrame.new(unpack(value.Components))
                elseif valueType == "UDim2" then
                        return UDim2.new(value.XScale, value.XOffset, value.YScale, value.YOffset)
                elseif valueType == "UDim" then
                        return UDim.new(value.Scale, value.Offset)
                elseif valueType == "NumberRange" then
                        return NumberRange.new(value.Min, value.Max)
                elseif valueType == "NumberSequence" then
                        local keypoints = {}
                        for _, kp in ipairs(value.Keypoints) do
                                table.insert(keypoints, NumberSequenceKeypoint.new(kp.Time, kp.Value, kp.Envelope))
                        end
                        return NumberSequence.new(keypoints)
                elseif valueType == "ColorSequence" then
                        local keypoints = {}
                        for _, kp in ipairs(value.Keypoints) do
                                local color = Color3.new(kp.Value.R, kp.Value.G, kp.Value.B)
                                table.insert(keypoints, ColorSequenceKeypoint.new(kp.Time, color))
                        end
                        return ColorSequence.new(keypoints)
                elseif valueType == "Rect" then
                        return Rect.new(value.Min.X, value.Min.Y, value.Max.X, value.Max.Y)
                elseif valueType == "PhysicalProperties" then
                        return PhysicalProperties.new(value.Density, value.Friction, value.Elasticity, value.FrictionWeight, value.ElasticityWeight)
                elseif valueType == "Font" then
                        return Font.new(value.Family, Enum.FontWeight[value.Weight], Enum.FontStyle[value.Style])
                elseif valueType == "Faces" then
                        -- Reconstruct Faces from table of booleans
                        local faces = {}
                        for face, included in pairs(value) do
                                if included then
                                        table.insert(faces, Enum.NormalId[face])
                                end
                        end
                        return Faces.new(unpack(faces))
                elseif valueType == "Axes" then
                        -- Reconstruct Axes from table of booleans
                        local axes = {}
                        for axis, included in pairs(value) do
                                if included then
                                        table.insert(axes, Enum.Axis[axis])
                                end
                        end
                        return Axes.new(unpack(axes))
                elseif valueType == "Instance" then
                        return instanceRegistry[value.__ref]
                else
                        return nil
                end
        else
                return value
        end
end

local function deserializeInstance(data)
        local instance

        -- Handle non-creatable classes
        local nonCreatableClasses = {
                Workspace = function() return game:GetService("Workspace") end,
                Terrain = function() return workspace.Terrain end,
                Lighting = function() return game:GetService("Lighting") end,
                Players = function() return game:GetService("Players") end,
                ReplicatedFirst = function() return game:GetService("ReplicatedFirst") end,
                ReplicatedStorage = function() return game:GetService("ReplicatedStorage") end,
                ServerScriptService = function() return game:GetService("ServerScriptService") end,
                ServerStorage = function() return game:GetService("ServerStorage") end,
                StarterGui = function() return game:GetService("StarterGui") end,
                StarterPack = function() return game:GetService("StarterPack") end,
                StarterPlayer = function() return game:GetService("StarterPlayer") end,
                -- Add other non-creatable classes as needed
        }

        local getInstanceFunction = nonCreatableClasses[data.ClassName]
        if getInstanceFunction then
                instance = getInstanceFunction()
        else
                local success, result = pcall(function()
                        return Instance.new(data.ClassName)
                end)
                if success then
                        instance = result
                else
                        warn("Failed to create instance of class:", data.ClassName, "-", result)
                        return nil
                end
        end

        instanceRegistry[data.__id] = instance

        -- Set properties (excluding Parent)
        for property, value in pairs(data.Properties or {}) do
                if not propertiesToSkip[property] then
                        local recreatedValue = recreateValue(value)
                        local successProp, errorMsg = pcall(function()
                                instance[property] = recreatedValue
                        end)
                        if not successProp then
                                warn("Failed to set property:", property, "on instance:", instance.Name, "-", errorMsg)
                        end
                end
        end

        -- Set attributes
        if data.Attributes then
                for attribute, value in pairs(data.Attributes) do
                        local recreatedValue = recreateValue(value)
                        local successAttr, errorMsg = pcall(function()
                                instance:SetAttribute(attribute, recreatedValue)
                        end)
                        if not successAttr then
                                warn("Failed to set attribute:", attribute, "on instance:", instance.Name, "-", errorMsg)
                        end
                end
        end

        -- Store parent data for later parenting
        parentDataMap[instance] = data
end

local function attemptToSetParents()
        local unresolved = {}
        for instance, data in pairs(parentDataMap) do
                local parentInstance = nil
                local canSetParent = true

                if data.ParentId then
                        parentInstance = instanceRegistry[data.ParentId]
                        if parentInstance then
                                -- Avoid setting Parent of non-parentable instances
                                if instance == workspace.Terrain or instance:IsA("Terrain") then
                                        canSetParent = false
                                end
                                if canSetParent then
                                        instance.Parent = parentInstance
                                else
                                        unresolved[instance] = data
                                end
                        else
                                unresolved[instance] = data  -- Parent not yet available
                        end
                elseif data.ParentIsGame then
                        if instance == workspace.Terrain or instance:IsA("Terrain") then
                                canSetParent = false
                        end
                        if canSetParent then
                                instance.Parent = game
                        end
                elseif data.ParentIsService and data.ParentClassName then
                        parentInstance = game:GetService(data.ParentClassName)
                        if parentInstance then
                                -- Avoid setting Parent of non-parentable instances
                                if instance == workspace.Terrain or instance:IsA("Terrain") then
                                        canSetParent = false
                                end
                                if canSetParent then
                                        instance.Parent = parentInstance
                                end
                        else
                                warn("Service not found:", data.ParentClassName)
                        end
                elseif data.ParentName and data.ParentClassName then
                        local service = game:GetService(data.ParentClassName)
                        if service then
                                parentInstance = service:FindFirstChild(data.ParentName)
                                if parentInstance then
                                        -- Avoid setting Parent of non-parentable instances
                                        if instance == workspace.Terrain or instance:IsA("Terrain") then
                                                canSetParent = false
                                        end
                                        if canSetParent then
                                                instance.Parent = parentInstance
                                        else
                                                unresolved[instance] = data
                                        end
                                else
                                        unresolved[instance] = data  -- Parent not found yet
                                end
                        else
                                warn("Service not found:", data.ParentClassName)
                        end
                else
                        -- Default parent
                        if instance == workspace.Terrain or instance:IsA("Terrain") then
                                canSetParent = false
                        end
                        if canSetParent then
                                instance.Parent = workspace
                        end
                end
        end
        parentDataMap = unresolved
end

-- Data string assignment
DATA_STRING_ASSIGNMENT

local data = HttpService:JSONDecode(dataString)

-- Deserialize instances
local totalInstances = #data
local currentIndex = 1

local function deserializeBatch()
        local batchSize = 100  -- Adjust as needed
        local endIndex = math.min(currentIndex + batchSize - 1, totalInstances)
        for i = currentIndex, endIndex do
                local instanceData = data[i]
                deserializeInstance(instanceData)
        end
        currentIndex = endIndex + 1
        if currentIndex <= totalInstances then
                RunService.Heartbeat:Wait()
                deserializeBatch()
        else
                -- All instances created, now set parents
                attemptToSetParents()
                -- Retry setting parents for unresolved instances
                for attempt = 1, 10 do
                        if next(parentDataMap) then
                                RunService.Heartbeat:Wait()
                                attemptToSetParents()
                        else
                                break
                        end
                end
                if next(parentDataMap) then
                        for instance, data in pairs(parentDataMap) do
                                warn("Unresolved parent for instance:", instance.Name)
                                -- Avoid setting Parent of non-parentable instances
                                if instance ~= workspace.Terrain and not instance:IsA("Terrain") then
                                        instance.Parent = workspace  -- Assign to workspace as fallback
                                end
                        end
                end

                -- Clean up shared data
                _G.instanceRegistry = nil
                _G.parentDataMap = nil
                print("Deserialization complete.")

                -- Signal that deserialization is complete
                local reconstructionCompleteSignal = script.Parent:FindFirstChild("ReconstructionComplete")
                if reconstructionCompleteSignal then
                        reconstructionCompleteSignal.Value = true
                end
        end
end

-- Start deserialization
deserializeBatch()
]]

	return scriptTemplate
end

function ScriptGenerator.generateScript(chunkIndex, totalChunks, dataChunk)
	local scriptTemplate = ScriptGenerator.getScriptTemplate()

	-- Prepare the data string assignment
	local dataStringAssignment = 'local dataString = ' .. string.format('%q', dataChunk)
	-- Escape any '%' in dataStringAssignment
	dataStringAssignment = dataStringAssignment:gsub("%%", "%%%%")

	-- Replace placeholders in the script template
	scriptTemplate = scriptTemplate:gsub("CHUNK_INDEX", tostring(chunkIndex))
	scriptTemplate = scriptTemplate:gsub("TOTAL_CHUNKS", tostring(totalChunks))
	scriptTemplate = scriptTemplate:gsub("DATA_STRING_ASSIGNMENT", dataStringAssignment)

	return scriptTemplate
end

function ScriptGenerator.generateReconstructionScripts(serializedData, maxScriptSize)
	local scripts = {}

	local dataSet = serializedData

	local dataChunks = {}
	local totalInstances = #dataSet
	local index = 1

	-- Get the script template for size estimation
	local emptyDataString = 'local dataString = ""'
	local scriptTemplateWithoutData = ScriptGenerator.getScriptTemplate()
	-- Replace placeholders with sample values
	scriptTemplateWithoutData = scriptTemplateWithoutData:gsub("DATA_STRING_ASSIGNMENT", emptyDataString)
	scriptTemplateWithoutData = scriptTemplateWithoutData:gsub("CHUNK_INDEX", "1")
	scriptTemplateWithoutData = scriptTemplateWithoutData:gsub("TOTAL_CHUNKS", "1")

	-- Estimate base script size
	local baseScriptSize = #scriptTemplateWithoutData

	local dataChunk = {}
	local dataSize = 0

	while index <= totalInstances do
		local instanceData = dataSet[index]
		local instanceDataString = HttpService:JSONEncode(instanceData)
		local instanceDataSize = #instanceDataString

		local tempDataChunk = { table.unpack(dataChunk) }
		table.insert(tempDataChunk, instanceData)
		local dataStringAssignmentSize = #('local dataString = ' .. string.format('%q', HttpService:JSONEncode(tempDataChunk)))

		local estimatedScriptSize = baseScriptSize - #('local dataString = ""') + dataStringAssignmentSize

		if estimatedScriptSize > maxScriptSize then
			if #dataChunk == 0 then
				-- Single instance too large to fit into a script, skip it
				warn("Instance at index " .. index .. " is too large to fit into a script and will be skipped.")
				index = index + 1
			else
				-- Finalize current dataChunk
				table.insert(dataChunks, dataChunk)
				-- Reset dataChunk and dataSize
				dataChunk = {}
				dataSize = 0
			end
		else
			-- Add instanceData to dataChunk
			dataChunk = tempDataChunk
			dataSize = dataSize + instanceDataSize
			index = index + 1
		end
	end

	-- Add any remaining dataChunk
	if #dataChunk > 0 then
		table.insert(dataChunks, dataChunk)
	end

	local totalChunks = #dataChunks

	-- Now generate scripts
	for chunkIndex, dataChunk in ipairs(dataChunks) do
		local dataString = HttpService:JSONEncode(dataChunk)
		local scriptSource = ScriptGenerator.generateScript(chunkIndex, totalChunks, dataString)
		table.insert(scripts, { Source = scriptSource })
	end

	return scripts
end

return ScriptGenerator scriptgenerator moudle  -- MainPluginScript.lua

local plugin = plugin

local UI = require(script.Parent.UI)
local Serializer = require(script.Parent.Serializer)
local Rechecker = require(script.Parent.Rechecker)

-- Create the UI
local uiElements = UI.createWidget(plugin)

-- Handle the button click to open/close the widget
uiElements.button.Click:Connect(function()
	uiElements.widget.Enabled = not uiElements.widget.Enabled
end)

-- Function to collect selected objects from the UI
local function getSelectedObjects()
	local selectedObjects = {}
	for _, obj in ipairs(uiElements.objectList or {}) do
		local checkboxInfo = uiElements.objectCheckboxes[obj]
		if checkboxInfo and checkboxInfo.isSelected() then
			table.insert(selectedObjects, obj)
		end
	end
	return selectedObjects
end

-- Function to collect all game objects for serialization
local function getAllGameObjects()
	local allObjects = {}

	local servicesToInclude = {
		"Workspace",
		"Lighting",
		"ReplicatedFirst",
		"ReplicatedStorage",
		"ServerScriptService",
		"ServerStorage",
		"StarterGui",
		"StarterPack",
		"StarterPlayer",
		-- Add other services as needed
	}

	for _, serviceName in ipairs(servicesToInclude) do
		local success, service = pcall(function()
			return game:GetService(serviceName)
		end)
		if success and service then
			table.insert(allObjects, service)
		else
			warn("Failed to get service:", serviceName)
		end
	end

	return allObjects
end

-- Function to handle serialization
local function onSerializeButtonClicked()
	local isAllGameConversionEnabled = uiElements.isAllGameConversionEnabled()

	local objectsToSerialize = {}

	if isAllGameConversionEnabled then
		-- Get all objects in the game
		objectsToSerialize = getAllGameObjects()
	else
		-- Get selected objects
		objectsToSerialize = getSelectedObjects()
		if #objectsToSerialize == 0 then
			warn("No objects selected for serialization.")
			uiElements.progressLabel.Text = "No objects selected for serialization."
			return
		end
	end

	print("Number of initial objects to serialize:", #objectsToSerialize)

	-- Prepare the serialization queue
	local instancesQueue = {}
	for _, obj in ipairs(objectsToSerialize) do
		table.insert(instancesQueue, obj)
	end

	local instanceMap = {}        -- Map to store instance references
	local serializedData = {}     -- List to keep serialized data
	local skippedInstances = 0    -- Count of instances that were skipped due to errors
	local totalObjectsProcessed = 0  -- We will increment this as we process instances
	local totalObjectsToProcess = #instancesQueue

	uiElements.progressLabel.Text = "Serialization in progress..."
	uiElements.progressBar.Size = UDim2.new(0, 0, 1, 0)  -- Reset progress bar

	local RunService = game:GetService("RunService")
	local startTime = tick()

	-- Function to proceed to generating reconstruction scripts
	local function proceedToGenerateScripts()
		if #serializedData == 0 then
			warn("No data was serialized. Serialization aborted.")
			uiElements.progressLabel.Text = "Serialization aborted. No data was serialized."
			return
		end

		-- Generate reconstruction scripts (multiple scripts if necessary)
		local maxScriptSize = 199000  -- Adjust as needed (Roblox's script size limit is around 200,000 characters)
		local reconstructionScriptsData = Serializer.generateReconstructionScripts(serializedData, maxScriptSize)

		-- Create the reconstruction scripts within the Plugin's parent (e.g., ServerScriptService)
		local scriptsFolder = Instance.new("Folder")
		scriptsFolder.Name = "ReconstructionScripts"
		scriptsFolder.Parent = game:GetService("ServerScriptService")  -- Placing in ServerScriptService to run on server

		-- Create a BoolValue to signal when reconstruction is complete
		local reconstructionCompleteSignal = Instance.new("BoolValue")
		reconstructionCompleteSignal.Name = "ReconstructionComplete"
		reconstructionCompleteSignal.Value = false
		reconstructionCompleteSignal.Parent = scriptsFolder

		-- Create and parent each reconstruction script
		for i, scriptData in ipairs(reconstructionScriptsData) do
			local reconstructionScript = Instance.new("Script")
			reconstructionScript.Name = "ReconstructionScript_" .. tostring(i)
			reconstructionScript.Source = scriptData.Source
			reconstructionScript.Disabled = false  -- Enable script
			reconstructionScript.Parent = scriptsFolder
		end

		-- Wait for deserialization to complete
		local deserializationStartTime = tick()
		local maxWaitTime = 600  -- Maximum wait time in seconds (adjust as needed)
		local deserializationComplete = false

		while not deserializationComplete and (tick() - deserializationStartTime) < maxWaitTime do
			RunService.Heartbeat:Wait()
			deserializationComplete = reconstructionCompleteSignal.Value
		end

		if deserializationComplete then
			print("Deserialization complete. Game is ready to load.")
		else
			warn("Deserialization did not complete within the expected time.")
		end

		-- Display time taken
		local endTime = tick()
		local duration = endTime - startTime
		local minutes = math.floor(duration / 60)
		local seconds = duration % 60
		uiElements.progressLabel.Text = string.format("Serialization complete in %d minutes, %.2f seconds.", minutes, seconds)
		uiElements.progressBar.Size = UDim2.new(1, 0, 1, 0)  -- Fill progress bar

		-- Notify user that serialization is complete
		print("Serialization complete. Reconstruction scripts have been generated.")
		if skippedInstances > 0 then
			print(skippedInstances .. " instances were skipped due to errors.")
		end
	end

	-- Serialize instances
	local function serializeNextInstance(onComplete)
		if #instancesQueue == 0 then
			-- Serialization complete
			if onComplete then
				onComplete()
			end
		else
			local instancesPerFrame = 100  -- Adjust this number as needed
			local count = 0

			while count < instancesPerFrame and #instancesQueue > 0 do
				count = count + 1
				local instance = table.remove(instancesQueue, 1)

				-- Serialize the instance
				local success, errorMsg = pcall(function()
					Serializer.serializeInstance(instance, instanceMap, serializedData, instancesQueue)
				end)

				if not success then
					skippedInstances = skippedInstances + 1
					warn("Failed to serialize instance: " .. tostring(instance:GetFullName()) .. ". Error: " .. tostring(errorMsg))
				end

				totalObjectsProcessed = totalObjectsProcessed + 1

				-- Update progress in UI
				local progressPercentage = math.floor((totalObjectsProcessed / totalObjectsToProcess) * 100)
				uiElements.progressLabel.Text = string.format("Serializing... %d%% complete", progressPercentage)
				uiElements.progressBar.Size = UDim2.new(totalObjectsProcessed / totalObjectsToProcess, 0, 1, 0)
			end

			-- Schedule next batch after yielding
			RunService.Heartbeat:Wait()
			serializeNextInstance(onComplete)
		end
	end

	-- Start the serialization process
	serializeNextInstance(function()
		-- After serialization is complete
		-- Perform recheck to find any missing instances
		local missingInstances = Rechecker.verifySerialization(objectsToSerialize, instanceMap)
		if #missingInstances > 0 then
			warn("Some instances were not serialized. Attempting to serialize missing instances.")

			-- Attempt to serialize missing instances
			Serializer.handleMissingInstances(missingInstances, instanceMap, serializedData, instancesQueue)

			-- Re-run serialization if there are instances left in the queue
			if #instancesQueue > 0 then
				totalObjectsToProcess = totalObjectsToProcess + #instancesQueue
				serializeNextInstance(function()
					-- After re-serialization, check again for missing instances
					missingInstances = Rechecker.verifySerialization(objectsToSerialize, instanceMap)
					if #missingInstances > 0 then
						warn("Could not serialize all instances after retries.")
					else
						print("All instances have been successfully serialized after retries.")
					end

					-- Proceed to generate reconstruction scripts
					proceedToGenerateScripts()
				end)
			else
				-- Proceed to generate reconstruction scripts even if some instances are missing
				proceedToGenerateScripts()
			end
		else
			print("All instances have been successfully serialized.")
			-- Proceed to generate reconstruction scripts
			proceedToGenerateScripts()
		end
	end)
end

-- Connect the serialize button in the UI to the function
uiElements.serializeButton.MouseButton1Click:Connect(onSerializeButtonClicked) mainpluginscript the issue is that when i join the game the converted meshparts into special meshes, 2 issues underlying: not all meshparts [specialmeshes converten] show and second some special meshes have a big grey block part surrounding it  and the special mesh inside and that big grey block part u can walk inside it, inside it is the converten mesh there fix  and make sure that big grey block part doesnt appear, code like a libra , balancing everything