-- ----------------------------------------------------------------------------
local objectExcludes = 
{
	"insidePocket",
	"type", "dbgName",
	"groupParent", "groupChildren", 
	"showAsGroupInInventory", "showAsGroupInShoppingList",
	"pocket",
	"enabled", "over", "pressed",
}
-- ----------------------------------------------------------------------------
local goalExcludes = 
{
	"type", "dbgName",	
}
-- ----------------------------------------------------------------------------
local layoutExcludes = 
{
	-- debug
	"type", "dbgName",
	-- state	
	"kUnconstructed", "kUninitialized", "kHidden", "kInactive", "kActive",
	-- object properties
	"mHOs", "mIOs", "mPOIs", "mExits", "mGroups", "mProps", "mOrder", "mButtons", "mFireflies", "mSprites", "mParticles",
	-- exit properties
	"mHighlightedExit", "mHighlightedTime", "mHighlightedTimeStart",
	-- fading blackout sprite
	"mFade", "mFadeName",	
	-- layoutJigsaw
	"mDragging", "mDraggingGroup", "mDraggingPieces", "mDragX", "mDragY",
}
-- ----------------------------------------------------------------------------
-- serializer
-- See "Programming In Lua" chapter 12.1.2.
-- Also see forum thread:
--   http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=4960
-- ----------------------------------------------------------------------------
function basicSerialize (o)
  if type(o) == "nil" then
    return "nil"
  elseif type(o) == "number" or type(o) == "boolean" then
    return tostring(o)
  elseif type(o) == "string" then
    return string.format("%q", o)
  elseif type(o) == "table" then
    if o.type then
  	  if o.type == kObject then
  	    return o:name()
  	  elseif o.type == kLayout then
  	    return o:name()
  	  elseif o.type == kGoal then
  	    return o:name()
  	  else
  	    return type(o)
  	  end
  	end  
  else
  	return type(o)
  end
end -- basicSerialize 

--
-- Lua keywords might look OK to not be quoted as keys but must be.
-- So, we make a list of them.
--

lua_reserved_words = {}

for _, v in ipairs({
    "and", "break", "do", "else", "elseif", "end", "false", 
    "for", "function", "if", "in", "local", "nil", "not", "or", 
    "repeat", "return", "then", "true", "until", "while"
            }) do lua_reserved_words [v] = true end

-- ----------------------------------------------------------------------------
-- save one variable (calls itself recursively)
-- ----------------------------------------------------------------------------
local saved = {}

function save (name, value, out, exclude, indent, init)
  exclude = exclude or {}   -- initial value
  indent = indent or 0      -- start indenting at zero cols
  if init == nil then init = true end -- initialize a new table

  if type(value) == "number" or 
     type(value) == "string" or
     type(value) == "boolean" or
     type(value) == "nil" then
    table.insert (out, name .. " = " .. basicSerialize(value))
  elseif type(value) == "table" then
  
  	local convert = false
  	if value.type then
  	  if value.type == kObject then
  	    -- it's a CampfireObject
  	    exclude = objectExcludes
		convert = true
  	  end
  	  if (value.type == kLayout) then
  	    -- it's a Layout
  	    exclude = layoutExcludes
  	    convert = true
  	  end
  	  if (value.type == kGoal) then
  	    -- it's a Goal
  	    exclude = goalExcludes
  	    convert = true
  	  end
  	end
  	
  	local convertName = nil  	
  	if (convert) then
		convertName = value:name()  	
  	end
  	
  	if (convert and indent > 0) then
  	  table.insert (out, name .. " = " .. convertName)  -- use its variable name
  
    else
      if not value.__cpp and not convert then
        if saved[value] then
          table.insert (out, name .. " = " .. saved[value])  -- reference it!
          return
        elseif init then
          table.insert (out, name .. " = {}") -- create new!
          saved[value] = name
        end
      elseif value.__cpp then
        if not convert then
          if (value.name) then
          	if (value:name() ~= "") then
              -- gamenode / sprite
              table.insert(out, name .. " = child(\"" .. value:name() .. "\")")
          	else
              -- gamenode / sprite 
              -- but it doesn't have a name!
              print("WARNING: unnamed __cpp object. save/load will fail: " .. name)
              return              
          	end
          else
            print("WARNING: unknown __cpp object. save/load will fail: " .. name)
            return
          end
        end
      end
      for k,v in pairs(value) do      -- save its fields
        local fieldname 
        local keyname
        if type (k) == "string"
           and string.find (k, "^[_%a][_%a%d]*$") 
           and not lua_reserved_words [k] then
          keyname = k
          fieldname = string.format("%s.%s", name, keyname)
        else
          keyname = basicSerialize(k)
          fieldname  = string.format("%s[%s]", name, keyname)  
        end
        local excl = false
        for i, e in ipairs(exclude) do
        	if (keyname == e) then
        		excl = true
        		break
        	end
        end
        if (not excl) then
        	save(fieldname, v, out, {}, indent + 2)
        end
      end
    end
  end
end  -- save 

-- ----------------------------------------------------------------------------
-- Serialize a variable or nested set of tables:
-- ----------------------------------------------------------------------------
function serialize (what, v, exclude, init)
  v = v or _G [what]  -- default to "what" in global namespace
  exclude = exclude or {} -- initial value

  assert (type (what) == "string", 
          "Argument to serialize should be the *name* of a variable")
  assert (v ~= nil, "Variable '" .. what .. "' does not exist")

  local out = {}  -- output to this table
  save (what, v, out, exclude, 0, init)   -- do serialization
  return table.concat (out, "\r\n")  -- turn into a string
end -- serialize
-- ----------------------------------------------------------------------------
function luaSerialize(lua, o)
 
  assert(o)
  assert(o.name)
  assert(o.type)
  return serialize(o:name(), o)

end
-- ----------------------------------------------------------------------------
function luaSerializeReset()

  saved = {}

end
-- ----------------------------------------------------------------------------
function luaSerializeLevel()
  
  local result = ""
  
  result = result .. serialize("goalStack", goalStack, nil, false) .. "\r\n"
  result = result .. serialize("fireFlies", fireFlies, nil, false) .. "\r\n"
  result = result .. serialize("animationsLoad", animationsSave, nil, false) .. "\r\n"
  
  return result
  
end

