--------------------------------------------------------------------------------
local ConsoleCommands = {};
local RegisterCommand = function(name, command, help)
	table.insert(ConsoleCommands, {name = name, command = command, help = help})
end

RegisterCommand('exit',AppExit)

RegisterCommand('clearhistory', function()
	Console:ClearHistory()
end,
'Clear console history')

RegisterCommand('initfonts', function()
        dofile('data/Scripts/Fonts.lua')
        InitFonts()
    end)

RegisterCommand('start', function(params)
    if params:size() > 1 then
        local levelNum  = params:get(1)
        local levelPath = levels_index_map[levelNum]
        
        if levelPath ~= nil then
            if mainMenu then
                mainMenu.sound:StopSound()
            end
            
			PopUpScreensMgr:DestroyAll()

			local CurrPlayer = GetGameManager():GetCurrPlayer()
			if CurrPlayer and CurrPlayer.gameState then
				CurrPlayer.gameState:Clear()
				local tag = IsLevelCollectorsEdition(levelPath) and CE_TAG_NAME or DEFAULT_TAG_NAME
				CurrPlayer.gameState:AddState(PLAY_MODE, CASUAL_MODE, tag)
			end
			
			--VolumeRestoreAmbientMusic()
            --StopAmbientMusic(false)
            
            StartLevel(levelPath)
            return ""
        else
            return "Wrong level index"
        end
    else
        return "Level index is empty"
    end
end,
'start [level_number] : clear all state and start level_number')

RegisterCommand('loadlevel', function(params)
    local levelPath
    
    if params:size() > 1 then
        levelPath = levels_index_map[params:get(1)]
    else
        levelPath = GetCurrentLevel() and GetCurrentLevel().name
    end
    
    if levelPath then
        if mainMenu then
            mainMenu.sound:StopSound()
        end
            
		PopUpScreensMgr:DestroyAll()
        
        --VolumeRestoreAmbientMusic()
		--StopAmbientMusic(false)
		
        StartLevel(levelPath)
    else
        return "Wrong level index"
    end    
end,
'loadlevel [level_number]. Load current level if level_number is empty.')

RegisterCommand('toggleobjecttracking', function()
    SetTrackObjectUnderMouse(not GetTrackObjectUnderMouse())
end,
'Log to console name of objects under mouse.')

RegisterCommand('debuglocalization', function(params)
    debuglocalization = toboolean(params:get(1))
    local l = 'off'
    if debuglocalization then
        l = 'on'
    end
    Console:Log('Localization debugging is '..l)
end)

function GenerateSavePath(path)
    local ext = ExtractFileExtention(path)
    local name = ExtractFileName(path)
    local path = ExtractFilePath(path)
    local new_path = path..'\\'..name..'___save.'..ext

    return new_path
end

RegisterCommand('savestate', function(params)
    local CurrPlayer = GetGameManager():GetCurrPlayer()
    if CurrPlayer and CurrPlayer.gameState then
        local state = CurrPlayer.gameState
        local path = GenerateSavePath( state:GetPath() )
        if params:size() == 2 then 
            path, _ = string.gsub (path, '.xml', ' '..params:get(1)..'.xml')
        end
        state:Save(path)
    end
end,
'savestate [name] : Save current games state. Use default slot if name is empty.')

RegisterCommand('restorestate', function(params)
    local CurrPlayer = GetGameManager():GetCurrPlayer()
    if CurrPlayer and CurrPlayer.gameState then
		
        local state = CurrPlayer.gameState
        local path = GenerateSavePath(state:GetPath())
        if params:size() == 2 then
            if params:get(1) == '?' then
                local filter = string.gsub(state:GetPath(), '.xml', '*.xml')
                Console:Log('filter: '..filter)
                local tempFilePath = 'temp_dir.txt'
                os.execute('dir "'..filter..'" /B >'..tempFilePath)
                local outStr = ''
                for line in io.lines(tempFilePath) do 
                    local str = string.gsub(line, '.xml', '')
                    str = string.gsub(str, '.*_save', '')
                    outStr = outStr..' '..str
                    while outStr:len() % 20 ~= 0 do
                        outStr = outStr..' '
                    end
                    if outStr:len() > 60 then 
                        Console:Log(outStr)
                        outStr = ''
                    end
                end
                Console:Log(outStr)
                return
            else
                path, _ = string.gsub(path, '.xml', ' '..params:get(1)..'.xml')
            end
        end
        state:Load(path)
		
		PopUpScreensMgr:DestroyAll()
		
        StartGame(
				state:HasState(LAST_VISITED_SCENE, CE_TAG_NAME) 
			and CE_TAG_NAME 
			or  nil
		)
    end
end,
'restorestate [name] : Load saved game state. Load last saved state if name is empty.')

RegisterCommand('dofile', function(params)
	if params:size() > 1 then
		local filename = params:get(1)
		
		local vecFiles = {ParseDirectory('data/Scripts/'), ParseDirectory('data/Scripts/Screens/'), ParseDirectory('data/Scripts/Comics/'),
							ParseDirectory('data/Scripts/HO/'), ParseDirectory('data/Scripts/Helpers/')}
		for _, vec in ipairs(vecFiles) do
            for i = 0, vec:size() - 1 do
		        if filename == ExtractFileName(vec:get(i)) then
					dofile(vec:get(i))
					Console:Log('File '..vec:get(i)..' was reread.') 
					return
				end
		    end
        end 
		return 'File '..filename..' was not found.'
	else
		return "Specify filename, you."
	end
end,
'difile [filename] : Reloads filename.lua file.')

RegisterCommand('findtool', function(params)
	local level = GetCurrentLevel()
	
	if level == nil then
		return "Level not found"
	end
	
	if params:size() > 1 then
		local tool_name = params:get(1)
		local tool_info = level.tools[tool_name]
		
		if not tool_info then
			return "Wrong tool name"
		end
		
		if not HasGameState(TOOL_FOUND, tool_name) then
			AddGameState(TOOL_FOUND,      tool_name)
			AddGameState(TOOL_INFO_SHOWN, tool_name)
		   
			GenerateTool(tool_name)
		   
			local inv = play_field:GetLevelScreen():GetInventory():GetAssociatedLuaObject()
			inv:OnToolFound(tool_name)
			inv:RotateTargetCell(tool_name, false)
		else
			return "Tool already found"
		end
	else
		return "Not enough arguments"
	end
	return "";
end,
'findtool [tool_name] : Find tool. Autocompleting by TAB.')

RegisterCommand('addnote', function(params)
	local level = GetCurrentLevel()
	
	if level == nil then
		return "Level not found"
	end
	
	if params:size() > 1 then
		local notes = GetAllNotes()
		local correctNote = false
		
		for _, id in ipairs(notes) do
			if id == params:get(1) then
				correctNote = true
				break
			end
		end
		
		if correctNote then
			if not IsNotePushed(id) then
				NotebookPushNote(id)
				return ''
			else
				return 'Note "'..id..'" has already been pushed.'
			end
		else
			return 'Note "'..id..'" does not exist.'
		end
	else
		return "Not enough arguments"
	end
	
	return ''
end,
'addnote [name] : Add note to diary.')

RegisterCommand('addallnotes', function()
	local level = GetCurrentLevel()
	
	if level == nil then
		return "Level not found"
	end
	
	local notes = GetAllNotes()
	local addNotesCount = 0
	
	for _, id in ipairs(notes) do
		if not IsNotePushed(id) then
			NotebookPushNote(id)
			addNotesCount = addNotesCount + 1
		end
	end
	
	return tostring(addNotesCount).." notes added"
end,
'Add all notes to diary.')

RegisterCommand('addgamestate', function(params)
	if params:size() > 2 then
		if HasGameState(params:get(1), params:get(2)) then
			return "Game state already exists";
		end
		AddGameState(params:get(1), params:get(2))
	else
		return "Not enough arguments"
	end
	return "";
end,
'addgamestate [state] [value] : AddGameState(state, value)')

RegisterCommand('setgamestate', function(params)
	if params:size() > 2 then
		SetGameState(params:get(1), params:get(2))
	else
		return "Not enough arguments"
	end
	return "";
end,
'setgamestate [state] [value] : SetGameState(state, value)')

RegisterCommand('removegamestate', function(params)
	if params:size() > 2 then
		if not HasGameState(params:get(1), params:get(2)) then
			return "No such game state";
		end
		RemoveGameState(params:get(1), params:get(2))
	else
		return "Not enough arguments"
	end
	return "";
end,
'removegamestate [state] [value] : RemoveGameState(state, value)')

RegisterCommand('levelid', function()
	for i, v in pairs(levels_index_map) do
		if v == GetCurrentLevel().name then
			return "Level id is " .. i
		end
	end        
end,
'Returns current level id.')

RegisterCommand('reopenscene', function(params)
	if params:size() > 1 then
		local levelNum = params:get(1)
		local levelName = levels_index_map[levelNum]
		if levelName ~= nil then
			ReopenScene(levelName)
		end
		return "ReopenScene " ..levelName;
	else
		return "Not enough arguments"
	end
end)

RegisterCommand('togglegui', function()
    if not play_field then
        return 'Level not found'
    end

    if not play_field:GetLevelScreen():GetPanel():IsHidden() then
        play_field:GetLevelScreen():GetPanel():Hide()
        play_field:GetLevelScreen():GetInventory():Hide()
    else
        play_field:GetLevelScreen():GetPanel():Show()
        play_field:GetLevelScreen():GetInventory():Show()
    end
end,
'Hide/Show gui(panel and inventory).')

RegisterCommand('reparsestrings', function()
	SetStringTable(STRINGS_FILE_NAME)
end,
'Reload all strings from text file.')

RegisterCommand('help', function()
	for _, command_info in pairs(ConsoleCommands) do
		local info = command_info.name
		if command_info.help then
			info = info..' - '..command_info.help
		end
		Console:Log(info)
	end
	Console:Log('Ctrl + DOWN/UP : Resize console.')
	local size = (#ConsoleCommands+1)*25
	size = size < SCR_H and size or SCR_H
	Console:SetHeight(size)
end,
'Show all commands.')

RegisterCommand('man', function(params)
	if params:size() > 1 then
		local command_name = params:get(1)
		for _, command_info in pairs(ConsoleCommands) do
			if command_info.name == command_name then
				if command_info.help then
					return command_info.help
				else
					return 'Sorry, there is no info for this command'
				end
			end
		end
		return 'Command not found'
	else
		return "Not enough arguments"
	end
end,
'man [command] : Show command info. Autocompleting by TAB.')

RegisterCommand('mm', function(mm)
	CleanUpGameResources()
	CreateMainMenu()
end,
'Exit to main menu.')

RegisterCommand('soundvolume', function(params)
	if params:size() > 1 then
		local vol = CorrectValue(tonumber(params:get(1)), 0, 1);
		GM:SetSoundVolume(vol);            
	else
		return "Not enough arguments"
	end
	return "";
end)

RegisterCommand('musicvolume', function(params)
	if params:size() > 1 then
		local vol = CorrectValue(tonumber(params:get(1)), 0, 1);
		GM:SetMusicVolume(vol);            
	else
		return "Not enough arguments"
	end
	return "";
end)

RegisterCommand('printentityhierarchy', function()
    PrintEntityHierarchy()
    Console:Log('Entities hierarchy is printed to Log file.')
end)

RegisterCommand('dog', function()
    local level = GetCurrentLevel()
    
    if level == nil then
		return "Level not found"
	end
    
    if HasGameState('DOG_ACTIVE', 'true') then
        return "Dog already found"
    end
    
    SetGameState('DOG_ACTIVE', 'true')
    
    local inv   = level:GetInventoryLuaObject()
    local panel = level:GetPanelLuaObject()
    
    panel:GetObject('area_dog'):SetVisible(true)
    panel:PlayIdleDog()
    panel.dog.anims.idle:SetOpacity(1)
    
    panel:GetObject('cover_dog'):SetOpacity(0)
    
    for toolName, info in pairs(level.tools) do
        if info.target_name == '<DOG_ON_PANEL>' then
            local state, value = GenerateToolTargedUsed(toolName, info.target_name)
            
            if not HasGameState(state, value) and IsToolFound(toolName) then
                inv:MarkDogCell(toolName)
                panel:SetActiveDogState(true)
            end
        end
    end
end)

--------------------------------------------------------------------------------
function RegisterConsoleCommands()
	for _, command_info in pairs(ConsoleCommands) do
		Console:RegisterCommand(command_info.name)
	end
end

--------------------------------------------------------------------------------
function ProcessConsoleCommand(console, command)

    local params = ExplodeString(' ', command);

    for _, command_info in pairs(ConsoleCommands)
    do
        if (params:get(0) == command_info.name)
        then
            return command_info.command(params) or "";
        end
    end
    
    return "Command not found: " .. command;
end

function GetAutocompletion(console, pattern, command)
	local params = ExplodeString(' ', command);
	if params:get(0) == 'findtool' then
		local tool_name = ""				
		
		local pattern_parts = ExplodeString(' ', pattern);
		if pattern_parts:size() > 1 then
			tool_name = pattern_parts:get(1)
		end
		
		local candidates = {}
		
		for candidate, _ in pairs(GetCurrentLevel().tools) do
			if (string.find(candidate, tool_name) or params:get(1) == "") and not IsToolFound(candidate) then
				table.insert(candidates, candidate)
			end
		end

		if #candidates > 0 then	
			local best_variant = candidates[1]
			local index = 0
			for i, v in ipairs(candidates) do
				if v == params:get(1) then
					index = i
					break
				end
			end
			if index == 0 then
				return params:get(0).." "..candidates[1]			
			else		
				if index + 1 <= #candidates then
					return params:get(0).." "..candidates[index + 1]
				else
					return params:get(0).." "..candidates[1]
				end
			end
		end
			
        return command;
    end
    
    if params:get(0) == 'dofile' then
		local tool_name = ""				
		
		local pattern_parts = ExplodeString(' ', pattern);
		if pattern_parts:size() > 1 then
			filename = pattern_parts:get(1)
		end
		
		local vecFiles = {ParseDirectory('data/Scripts/'), ParseDirectory('data/Scripts/Screens/'), ParseDirectory('data/Scripts/Comics/'),
							ParseDirectory('data/Scripts/HO/'), ParseDirectory('data/Scripts/Helpers/')}
		local filenames =  {}
		for _, vec in ipairs(vecFiles) do
            for i = 0, vec:size() - 1 do
		        table.insert(filenames, ExtractFileName(vec:get(i)))
		    end
        end 
		
		local candidates = {}
		
		for _, candidate in ipairs(filenames) do
			if (string.find(string.lower(candidate), string.lower(filename)) or params:get(1) == "") then
				table.insert(candidates, candidate)
			end
		end

		if #candidates > 0 then	
			local best_variant = candidates[1]
			local index = 0
			for i, v in ipairs(candidates) do
				if v == params:get(1) then
					index = i
					break
				end
			end
			if index == 0 then
				return params:get(0).." "..candidates[1]			
			else		
				if index + 1 <= #candidates then
					return params:get(0).." "..candidates[index + 1]
				else
					return params:get(0).." "..candidates[1]
				end
			end
		end
			
        return command;
    end
    
	if params:get(0) == 'man' then
		local command_name = ""				
		
		local pattern_parts = ExplodeString(' ', pattern)
		if pattern_parts:size() > 1 then
			command_name = pattern_parts:get(1)
		end
		
		local candidates = {}
		local positions = {}
		
		for _, command_info in pairs(ConsoleCommands) do
			local result = string.find(command_info.name, command_name)
			if result or params:get(1) == "" then
				table.insert(candidates, command_info.name)
				table.insert(positions, result)
			end
		end

		if #candidates > 0 then	
			local index = 1
			for i, position in pairs(positions) do
				if position < positions[index]then
					index = i
				end
			end
			local best_variant = candidates[index]
			for i, v in ipairs(candidates) do
				if v == params:get(1) then
					if i + 1 <= #candidates then
						index = i + 1
					else
						index = 1
					end
					break
				end
			end
			return params:get(0).." "..candidates[index]
		end
			
        return command
    end
end
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------