-- Serialize the table "t".
function Serialize(t)
	local ret = ""
	local queue = { }
	local already_queued = { }

	-- Convert a value to an identifier.  If we encounter a table that we've never seen before,
	-- it's an anonymous table and we'll create a name for it; for example, in t = { [ {10} ] = 1 },
	-- "{10}" has no name.
	local next_id = 1
	local function convert_to_identifier( v, name )
		-- print("convert_to_identifier: " .. (name or "nil"))
		if type(v) == "string" then
			return string.format("%q", v)
		elseif type(v) == "nil" then
			return "nil"
		elseif type(v) == "boolean" then
			if v then return "true" end
			return "false"
		elseif type(v) == "number" then
			return string.format("%i", v)
		elseif type(v) == "table" then
			if already_queued[v] then
				return already_queued[v]
			end

			-- Create the table.  If we have no name, give it one; be sure to make it local.
			if not name then
				name = "tab" .. next_id
				next_id = next_id + 1
				ret = ret .. "local " .. name .. " = { }\n"
			else
				-- The name is probably something like "x[1][2][3]", so don't emit "local".
				ret = ret .. name .. " = { }\n"
			end

			for i, tab in pairs(v) do
				local to_fill = { ["name"] = name .. "[" .. convert_to_identifier(i) .. "]", with = tab }
				table.insert( queue, to_fill )
			end

			already_queued[v] = name
			return name
		else
			return '"UNSUPPORTED TYPE (' .. type(v) .. ')"', true
		end
	end

	local top_name = convert_to_identifier( t )
 
	while table.getn(queue) > 0 do
		local to_fill = table.remove( queue, 1 )
		local str = convert_to_identifier( to_fill.with, to_fill.name )
		-- Assign the result.  If to_fill.with is a non-anonymous table, we just created
		-- it ("ret[1] = { }"); don't redundantly write "ret[1] = ret[1]".
		if to_fill.name ~= str then
			ret = ret .. to_fill.name .. " = " .. str .. "\n"
		end
	end
	ret = ret .. "return " .. top_name
	return ret
end

-- Recursively deep-copy a table.
function DeepCopy(From, To, already_copied)
	if not To then To = {} end

	already_copied = already_copied or { }
	already_copied[From] = To

	for a, b in pairs(From) do
		local aCopy, bCopy
		if type(a) ~= "table" then
			aCopy = a
		elseif already_copied[a] then
			aCopy = already_copied[a]
		else
			aCopy = {}
			DeepCopy( a, aCopy, already_copied )
		end

		if type(b) ~= "table" then
			bCopy = b
		elseif already_copied[b] then
			bCopy = already_copied[b]
		else
			bCopy = {}
			DeepCopy( b, bCopy, already_copied )
		end

		To[aCopy] = bCopy
	end

	return To
end

-- (c) 2005 Glenn Maynard
-- All rights reserved.
-- 
-- Permission is hereby granted, free of charge, to any person obtaining a
-- copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, and/or sell copies of the Software, and to permit persons to
-- whom the Software is furnished to do so, provided that the above
-- copyright notice(s) and this permission notice appear in all copies of
-- the Software and that both the above copyright notice(s) and this
-- permission notice appear in supporting documentation.
-- 
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
-- THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
-- INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
-- OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
-- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-- PERFORMANCE OF THIS SOFTWARE.

