
/*
 * Copyright 2011-2022 Cuberite Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "CMDManager.h"

#include "StringUtils.h"
#include "Entities/Player.h"

#include "CoreCommands.h"

const cCMDManager::CommandMap cCMDManager::m_Commands = { 
    {"/clear", {"core.clear", "Clears the inventory of a player.", &HandleClearCommand}},
    {"/effect", {"core.effect", "Adds an effect to a player.", &HandleEffectCommand}},
    {"/kick", {"core.kick", "Kicks a player.", &HandleKickCommand}},
    {"/list", {"core.list", "Shows a list of connected players.", &HandleListCommand}},
    {"/rank", {"core.rank", "Shows or sets a player's rank.", &HandleRankCommand}},
    {"/listgroups", {"core.listgroups", "Shows a list of the available groups.", &HandleListGroupsCommand}},
    {"/listranks", {"core.listranks", "Shows a list of the available ranks.", &HandleListRanksCommand}},
    {"/me", {"core.me", "Broadcasts what you are doing.", &HandleMeCommand}},
    {"/numchunks", {"core.numchunks", "Shows number of chunks currently loaded.", &HandleNumChunksCommand}},
    {"/op", {"core.rank", "Add a player to the administrator rank.", &HandleOpCommand}},
    {"/players", {"core.players", "Shows a list of all connected players.", &HandlePlayersCommand}},
    {"/portal", {"core.portal", "Moves your player to a different world.", &HandlePortalCommand}},
    {"/save-all", {"core.save-all", "Saves all worlds.", &HandleSaveAllCommand}},
    {"/say", {"core.say", "Sends a message in the chat to other players.", &HandleSayCommand}},
    {"/seed", {"core.seed", "Shows the seed of the given world name or current world, if not given.", &HandleSeedCommand}},
    {"/setspawn", {"core.setspawn", "Changes the world's spawn point.", &HandleSetSpawnCommand}},
    {"/spawn", {"core.spawn", "Returns a player to the spawn point.", &HandleSpawnCommand}},
    {"/spawnpoint", {"core.spawnpoint", "Sets the spawn point for a player.", &HandleSpawnPointCommand}},
    {"/stop", {"core.stop", "Stops the server.", &HandleStopCommand}},
    {"/summon", {"core.summon", "Summons an entity in the world.", &HandleSummonCommand}}
};
const cCMDManager::CommandMap cCMDManager::m_ConsoleCommands = cCMDManager::m_Commands;

cCMDManager::CommandResult cCMDManager::HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions)
{
	AStringVector Split(StringSplit(a_Command, " "));
	if (Split.empty())
	{
		return crUnknownCommand;
	}
    
	CommandMap::const_iterator cmd = m_Commands.find(Split[0]);
	if (cmd == m_Commands.end())
	{
		// Command not found
		// If it started with a slash, ask the plugins if they still want to handle it:
		/*if (!a_Command.empty() && (a_Command[0] == '/'))
		{
			CommandResult Result = crUnknownCommand;
			CallHookExecuteCommand(&a_Player, Split, a_Command, Result);
			return Result;
		}*/
		return crUnknownCommand; // part of whole func comment out.
    }

	// Ask plugins first if a command is okay to execute the command:
	/*(CommandResult Result = crBlocked;
	if (CallHookExecuteCommand(&a_Player, Split, a_Command, Result))
	{
		if (Result == crBlocked)
		{
			LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str());
		}
		return Result;
	}*/

	if (
		a_ShouldCheckPermissions &&
		!cmd->second.m_Permission.empty() &&
		!a_Player.HasPermission(cmd->second.m_Permission)
	)
	{
		LOGINFO("Player %s tried to execute forbidden command: \"%s\"", a_Player.GetName().c_str(), Split[0].c_str());
		return crNoPermission;
	}

	//ASSERT(cmd->second.m_Handler/* != nullptr*/ && *cmd->second.m_Handler);

	if (!cmd->second.m_Handler(Split, &a_Player, a_Command, nullptr))
	{
		return crError;
	}

	return crExecuted;
}


bool cCMDManager::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command)
{
	if (a_Split.empty())
	{
		return false;
	}

	CommandMap::const_iterator cmd = m_ConsoleCommands.find(a_Split[0]);
	if (cmd == m_ConsoleCommands.end())
	{
		// Command not found
		// Still notify the plugins (so that plugins such as Aliases can intercept unknown commands).
		/*CommandResult res = crBlocked;
		CallHookExecuteCommand(nullptr, a_Split, a_Command, res);*/
		return false; // (res == crExecuted);
	}

	/*if (cmd->second.m_Plugin == nullptr)
	{
		// This is a built-in command
		return false;
	}*/

	// Ask plugins first if a command is okay to execute the console command:
	/*CommandResult res = crBlocked;
	if (CallHookExecuteCommand(nullptr, a_Split, a_Command, res))
	{
		return (res == crExecuted);
	}*/

    return cmd->second.m_Handler(a_Split, nullptr, a_Command, &a_Output);
}
