dofile('data/Lua/WaterEffect.lua')
class 'ProfileManager'

SUCCESS = 0
ERROR_PROFILE_EXISTS = 1
ERROR_MAX_PLAYERS_REACHED = 2

function OnScenePreLoad(screen)
    OnSceneLoad(screen)
    play_field.game_state = game_state
    game_state.unregistered_zoom_zones = {}
    
	--TODO Uncomment for loading    
	local pm = GetProfileManager()
	local currentProfile = pm:GetCurrentProfile();
	
	if currentProfile then
		local fs = GetGameApp():IsFullScreen()
		if currentProfile.fullscreen ~= fs then
			currentProfile.fullscreen = fs
		end
	end
	
	pm:SaveProfiles()
	currentProfile.game_state_ = {}
	pm:LoadProfiles()
	currentProfile = pm:GetCurrentProfile()
	local currentLevel = play_field:GetLevelScreen();
	
	if currentProfile.game_state_.is_tutorial_must_be_shown then
	    currentProfile.cigars_found[1] = false
	    panel.RefreshMetaGameCaption()
	end
		
	if (play_field:GetName() == currentProfile:GetLevelToLoad() and not currentProfile.game_state_.is_tutorial_must_be_shown) then
		currentLevel:ClearTasks();
		for i, v in ipairs(currentProfile.actual_tasks_) do
			currentLevel:AddTask(v)
		end
		
		for i, v in ipairs(currentProfile.found_objects_) do
			if play_field:HasObject(v) then
				play_field:DeleteObject(v)
			end
		end
		
		game_state.do_not_load_mini_game = false
		
		local ImposeLayers
		local ImposeInventoryZones
		ImposeLayers = function(original, save_data)
            for i, v in pairs(original) do
                for j, w in pairs(save_data) do
                    if i == j then
                        if type(v) ~= 'table' then
                            original[i] = w
                        else
                            if i == 'inventory_zones' then
                                ImposeInventoryZones(v, w)
                            elseif i == 'unregistered_zoom_zones' then
                                for l, y in pairs(w) do
                                    table.insert(v, y)
                                end
                            else
                                ImposeLayers(v, w)
                            end
                        end
                    end
                end
            end            
		end
		
		ImposeInventoryZones = function(original, save_data)
		    for i, v in pairs(original) do
                for j, w in pairs(save_data) do
                    if i == j then
                        v.item_1_found = w.item_1_found
                        v.item_2_found = w.item_2_found
                        v.item_3_found = w.item_3_found
                        v.items_to_use = w.items_to_use
                        if w.locked then
                            v.Lock()
                        else
                            v.Unlock()
                        end
                    end
                end
            end
	    end
		game_state.loaded = true
		
		ImposeLayers(game_state, currentProfile.game_state_)
		
		for i, v in ipairs(game_state.unregistered_zoom_zones) do
		    play_field:IgnoreOnTestPoint(v)
		end
		
		local on_win_called = false
		game_state.OnWin = function()
		    on_win_called = true
		    OnWin()
		end
        
        if not currentProfile:CallsFunction('OnWin') then
            local functionsToCall = {}
            
            for i, v in ipairs(currentProfile.functions_to_call_on_start_) do
                table.insert(functionsToCall, v)
            end
            
            for i, v in ipairs(functionsToCall) do
                game_state[v]()
            end
            
        else
            game_state.OnWin()
        end
        
        if not on_win_called and not play_field:GetLevelScreen():HasTaskObjects() then
            OnAllItemsFound()
        end
		
		if currentProfile.hint_delay_ ~= 0 then
			panel:SetHintDelay(currentProfile.hint_delay_)
			cigar_button:Stop()
			cigar_button:SetVisible(false)
			panel:SetCigarFiring()
		end
	end
end


function RemoveObject(object_name)    
    play_field:DeleteObject(object_name)
    GetProfileManager():GetCurrentProfile():AddFoundObject(object_name)
end

function OnSceneStateChanged(screen)    
	local pm = GetProfileManager()
	local currentLevel = screen:GetLevelScreen()
	local currentProfile = pm:GetCurrentProfile();
	currentProfile:SetActualTasks(currentLevel:GetTasks());
	currentProfile:SetLevelToLoad(play_field:GetName());
	currentProfile:SetGameState(play_field.game_state)
	currentProfile.hint_delay_ = panel:GetHintDelay()
	--currentProfile:SetActualObjects(play_field:GetObjects());
	pm:SaveProfiles();
end

function GetProfileManager()
	if profile_manager == nil then
		profile_manager = ProfileManager()
	end
	return profile_manager
end

function ProfileManager:__init()
    self.profiles_ = {}
	self.players_file_ = ""
	self.storage_path_ = ""
	self.current_profile_index_ = -1
	self.show_error_message_ = false
end

function ProfileManager:SetErrorMessageEnabled(value)
    self.show_error_message_ = value
end

function ProfileManager:IsErrorMessageEnabled()
    return self.show_error_message_;
end

function ProfileManager:SetStoragePath(storage_path)
	self.storage_path_ = storage_path
end

function ProfileManager:SetPlayersFile(players_file)
	self.players_file_ = self.storage_path_..players_file
end

function ProfileManager:GetProfileCount()
    local size = 0;
    for i, v in pairs(self.profiles_) do
		if v  then
			size = size + 1;
		end
	end
	return size;
end

function ProfileManager:AddProfile(profile_name)
	local exists = false
	for i, v in pairs(self.profiles_) do
		if v and string.upper(v:GetName()) == string.upper(profile_name) then
			exists = true
			break
		end
	end
	
	if exists then
		return ERROR_PROFILE_EXISTS;
	end

	local possible_save_files = {}
	
	possible_save_files[1] = self.storage_path_.."data\\Map\\1.xml";
	possible_save_files[2] = self.storage_path_.."data\\Map\\2.xml";
	possible_save_files[3] = self.storage_path_.."data\\Map\\3.xml";
	possible_save_files[4] = self.storage_path_.."data\\Map\\4.xml";
	possible_save_files[5] = self.storage_path_.."data\\Map\\5.xml";
	--[[possible_save_files[6] = self.storage_path_.."data\\Map\\6.xml";
	possible_save_files[7] = self.storage_path_.."data\\Map\\7.xml";
	possible_save_files[8] = self.storage_path_.."data\\Map\\8.xml";]]

	--Is Exist Free Name?
	local save_file = ""
	
	for i, v in ipairs(possible_save_files) do
		local found = false
		for j, w in pairs(self.profiles_) do
			if w and w:GetSavePath() == v then
			    found = true
			    break
			end
		end
		
		if not found then
			save_file = v
			break
		end
	end
	
	if save_file ~= "" then
		self.profiles_[table.getn(self.profiles_) + 1] = Profile(profile_name, save_file)		
		return SUCCESS;
	end

	return ERROR_MAX_PLAYERS_REACHED;
end

function ProfileManager:DeleteProfile(profile_name)
	local exists = false
	local index = 0
	for i, v in pairs(self.profiles_) do
		if v and v:GetName() == profile_name then
			exists = true
			index = i
			break
		end
	end
	
	if not exists then
		return false;
	end

	self.profiles_[index] = nil

	return true;
end

function ProfileManager:SetCurrentProfile(profile_name)
	for i, v in pairs(self.profiles_) do
		if v and v:GetName() == profile_name then
		    -- reset selet all profile
		    for j, u in pairs(self.profiles_) do
		        if u then
			        u:SetActive(false);
		        end
	        end

            -- select current profile
			self.profiles_[i]:SetActive(true)
			OnProfileActivated()
			return true;
		end
	end
	return false;	
end

function ProfileManager:GetCurrentProfile()		
	for i, v in pairs(self.profiles_) do
		if v and v:IsActive() then
			return v
		end
	end
	
	return nil
end

function ProfileManager:GetFirstProfile()
    for i, v in pairs(self.profiles_) do
        self.current_profile_index_ = i
        return self.profiles_[self.current_profile_index_];
    end
end

function ProfileManager:GetNextProfile()
    local use_as_current = false
    for i, v in pairs(self.profiles_) do
        if use_as_current then
            self.current_profile_index_ = i
            return v
        end
        if i == self.current_profile_index_ then
            use_as_current = true
        end
    end
    return nil
--[[
	self.current_profile_index_ = self.current_profile_index_ + 1
	local profile = self.profiles_[self.current_profile_index_]
	while profile and self.current_profile_index_ < table.getn(self.profiles_) do
		self.current_profile_index_ = self.current_profile_index_ + 1
		profile = self.profiles_[self.current_profile_index_]
	end
	return profile]]
end

function ProfileManager:SaveProfiles()
	local doc = XMLDocument("Root");
	
	local root = doc:GetRootElement();
	
	local profiles = XMLElement("Profiles")

	for i, v in pairs(self.profiles_) do
		local elem = XMLElement("Profile")
		local profile = v
		profile:Save(elem)
		profiles:AddElement(elem)
		--elem:AddRef()
	end
	
	root:AddElement(profiles)
	
	local checksum = XMLElement("Checksum")
	checksum:AddAttribute("Value", tostring(GetChecksum(profiles)))
	root:AddElement(checksum)

	assert(self.players_file_ ~= "");
	doc:Save(self.players_file_);

	return true;
end

function ProfileManager:LoadProfiles()
	assert(self.players_file_ ~= "");	

	local doc = XMLDocument("")
	
	doc:Load(self.players_file_)
	
	local error_id = doc:GetErrorId()
    
    if error_id ~= TIXML_ERROR_OPENING_FILE and error_id ~= TIXML_NO_ERROR then	   
	   self:SetErrorMessageEnabled(true)
	end

	local root = doc:GetRootElement();

	local profiles = nil;
	local checksum = 0
	
    local i = 0
	
	local checksum_found = false
	local root_elements_size = root:GetElements():size()
	for i = 0, root_elements_size - 1 do
		local cur_element = root:GetElements():get(i)
		if cur_element:GetName() == 'Profiles' then
			profiles = cur_element
		end
		if cur_element:GetName() == 'Checksum' then
		    checksum_found = true
		    local checksum = -1
            if cur_element:HasAttribute('Value') then
                checksum = cur_element:GetAttribute('Value').value
            end
		      
			if tonumber(checksum) ~= GetChecksum(profiles) then
			    self:SetErrorMessageEnabled(true)
			    
				return false
			end
		end
	end
	
	if not checksum_found and root_elements_size ~= 0 then
	    self:SetErrorMessageEnabled(true)
		return
	end
	
	if not profiles then
		return true
	end
	
	i = 0
	local elements = profiles:GetElements()
    while i < elements:size() do
        local elem = elements:get(i);

		if elem:GetName() == "Profile" then
            local profile = Profile("", "")
			profile:Load(elem)		    
			
            local profile_index = -1
		    for k, w in pairs(self.profiles_) do
		        if w:GetName() == profile:GetName() then
		            profile_index = k
		            break
		        end
		    end
		
			if profile_index ~= -1 then
			    self.profiles_[profile_index] = profile
			else
			    self.profiles_[table.getn(self.profiles_) + 1] = profile
			end
		end
        i = i + 1
    end	
	return true;
end

function IsIntroMustBeShown()
	return GetProfileManager():GetCurrentProfile() ~= nil
end

function IsCustomCursorVisible()
    if not GetProfileManager():GetCurrentProfile() then
        return true
    end
	return GetProfileManager():GetCurrentProfile().custom_cursor_on
end

function GetChecksum(element)
    --local subElements = element:GetElements()

	local result = 0
	local s = element:GetString('')
	
    --[[for i = 0, subElements:size() - 1 do
        local doc = XMLDocument("")        
        doc:Load(subElements:get(i):GetAttribute("SavePath").value)
        local root = doc:GetRootElement()
        s = s..root:GetString('')
    end]]	
	
	local len = string.len(s)
	for i = 1, len do
		result = result + string.byte(s, i)
		if result > 256 then
		    result = result - 256
		end
	end
	return result
end