save_game = save_game or {}

function save_game.SaveLevelInfo(_file_name)
    if quest.global_played_level then
        threads_managment.InterruptClickScript()
        local xml_writer =  sf.misc.CXmlWriter() 
        local root_xml_element = sf.misc.CXmlElement ( xml_writer, "Save")
        local level_info = sf.misc.CXmlElement ( root_xml_element, "LevelInfo")
        level_info.Attr("current_scene", quest.global_current_main_scene.GetId().c_str() )
        quest.global_played_level.Save(level_info, true)
        level_info.Flush()
        local   inventory_info = sf.misc.CXmlElement ( root_xml_element, "Inventory")
            local invent_table = {}
            local current_object = quest.global_level_box.GetObjects()
            while not current_object.IsEnd() do
                table.insert(invent_table, current_object.Get().Item.GetId().c_str())
                current_object.Next()
            end
            local invent_size = #invent_table
            if invent_size > 0 then
                for i = 0, invent_size - 1 do
                    local item_info = sf.misc.CXmlElement ( inventory_info, "item")
                    item_info.Attr("object_id", invent_table[invent_size - i] )
                    item_info.Flush()
                end
            end
        inventory_info.Flush()

        local tasks_info = sf.misc.CXmlElement ( root_xml_element, "TasksInfo")
        task_list.Save(tasks_info)
        tasks_info.Flush()
        
        background_sounds.SaveToLuaVariables()
        
        local lua_variables = sf.misc.CXmlElement(root_xml_element, "LuaVariables")
        save_game._SaveLuaVariablesRec(quest.g_LuaVariables, lua_variables, "quest.g_LuaVariables")
        lua_variables.Flush()

        root_xml_element.Flush()
        xml_writer.SaveToFile(_file_name)
        sf.misc.SetNamedFullAccess(_file_name, false)
    end
end

-- Рекурсивно сохраняет таблицу луа переменных
function save_game._SaveLuaVariablesRec(_var, _root, _path)
    for id, val in pairs(_var) do
        local id_attr = type(id) == "number" and "i" or "s"
        
        local val_type = type(val)
        if val_type == "table" then
            -- сохраняем таблицу
            local table_root = sf.misc.CXmlElement(_root, "t")
            table_root.Attr(id_attr, tostring(id))
            save_game._SaveLuaVariablesRec(val, table_root, _path .. "." .. id)
            table_root.Flush()
        elseif val_type == "number" then
            -- сохраняем число
            local element = sf.misc.CXmlElement(_root, "n")
            element.Attr(id_attr, tostring(id))
            element.Attr("v", tostring(val))
            element.Flush()
        elseif val_type == "string" then
            -- сохраняем строку
            local element = sf.misc.CXmlElement(_root, "s")
            element.Attr(id_attr, tostring(id))
            element.Attr("v", val)
            element.Flush()
        else
            __assert(false, "В quest.g_LuaVariables сохранена переменная неверного типа.\nпеременная: "..
				_path.."."..id.."\nтип: "..__get_typeex(val), true)
        end
    end
end

function save_game.CopyObjectInfo(_dst_object, _src_object)
    _dst_object.SetObjectFlags(_src_object.GetObjectFlags())
    _dst_object.SetHint(_src_object.GetHint())
	_dst_object.SetUserData(_src_object.GetUserData())
	_dst_object.SetColor(_src_object.GetColor())
end

function save_game.CopyBaseObjectInfo(_dst_object, _src_object)
    assert(_dst_object.GetType() == _src_object.GetType())    
    local id = _dst_object.GetId().c_str()
    
    if _dst_object.GetType() == qe.CBaseSceneObject.GroupType then
        save_game.CopyObjectInfo(_dst_object, _src_object)
        local src_object =  __cast(_src_object, qe.CSceneGroup)
        local dst_object =  __cast(_dst_object, qe.CSceneGroup)
        local src_object_child = src_object.GetChilds()
        while not src_object_child.IsEnd() do
            local dst_child = dst_object.GetStateById(src_object_child.Get().GetId())
            if dst_child then
                save_game.CopyObjectInfo(dst_child, src_object_child.Get())
            end
            src_object_child.Next()
        end
    else
        save_game.CopyObjectInfo(_dst_object, _src_object)
    end
end

function save_game.Load(_load_file_name)
    local load_group = sf.core.CSettings()
    load_group.LoadXml(_load_file_name)
    local save_group = load_group.GetChildRef("Save", false)
    local loaded_level = nil
    local main_scene_id = nil
    if save_group then


        local level_info = save_group.GetChildRef("LevelInfo", false)
        if level_info then
            main_scene_id = level_info.GetValue(_T("current_scene"))
            local level_group = level_info.GetFirstChildRef()
            if level_group then
                loaded_level = qe.CLevel() 
                loaded_level.Load(level_group, true)
            end
        end                 
            
        if loaded_level then

            local original_level_scene = quest.global_played_level.GetScenes()
            while not original_level_scene.IsEnd() do
                local original_scene = original_level_scene.Get()
                local loaded_scene = loaded_level.GetSceneById(original_scene.GetId())
                local original_scene_child = original_scene.GetObjects()
       
                while not original_scene_child.IsEnd() do
                    local loaded_child = loaded_scene.IsContentObject(original_scene_child.Get().GetId())
                    if loaded_child ~= nil then
                        save_game.CopyBaseObjectInfo(original_scene_child.Get(), loaded_child)
                    end
                    original_scene_child.Next()
                end

                local original_scene_area = original_scene.GetAreas()
       
                while not original_scene_area.IsEnd() do
                    local loaded_area = loaded_scene.IsContentArea(original_scene_area.Get().GetId())
                    if loaded_area ~= nil then
                        save_game.CopyBaseObjectInfo(original_scene_area.Get(), loaded_area)
                    end
                    original_scene_area.Next()
                end

                original_level_scene.Next()
            end

            local inventory_info = save_group.GetChildRef("Inventory", false)
            if inventory_info then
                local item_group = inventory_info.GetFirstChildRef()
                local items_objects_list  = {}
                while item_group do
                    local item_id = item_group.GetValue(_T("object_id"))
                    inventory.shadow_take(item_id, true)
                    item_group = item_group.GetNextSiblingRef()
                end
            end
            local tasks_group = save_group.GetChildRef("TasksInfo", false)
            if tasks_group then
                task_list.Load(tasks_group)
            end
            
            local lua_variables = save_group.GetChild("LuaVariables", false)
            quest.g_LuaVariables = {}
            save_game._LoadLuaVariablesRec(lua_variables.GetFirstChildRef(), quest.g_LuaVariables)
            
            background_sounds.LoadFromLuaVariables()
        end
    end
    return main_scene_id
end

-- Рекурсивно загружает таблицу луа переменных
function save_game._LoadLuaVariablesRec(_var, _root)
    while true do
        if not _var then return end
    
        local value_type = _var.GetId().c_str()
        -- загружаем значение в зависимости от типа
        local value = nil
        if value_type == "n" then
            -- число
            value = tonumber(_var.GetValue("v").c_str())
            __assert(value)
        elseif value_type == "s" then
            -- строка
            value = _var.GetValue("v").c_str()
        elseif value_type == "t" then
            -- таблица
            value = {}
            save_game._LoadLuaVariablesRec(_var.GetFirstChildRef(), value)
        else
            __assert(false, "Invalid element in save: "..value_type, true)
        end
        
        -- загружаем id как число или как строку, в зависимости от атрибута
        local id = nil
        if _var.IsValue("i") then
            id = tonumber(_var.GetValue("i").c_str())
        else
            id = _var.GetValue("s").c_str()
        end
        __assert(id and id ~= "")
        
        _root[id] = value
        
        _var = _var.GetNextSiblingRef()
    end
end

