text: /**
   * MaNGOS is a full featured server for World of Warcraft, supporting
   * the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
   *
   * Copyright (C) 2005-2016  MaNGOS project <https://getmangos.eu>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   *
   * World of Warcraft, and all World of Warcraft or Warcraft art, images,
   * and lore are copyrighted by Blizzard Entertainment, Inc.
   */
  
  #include "Chat.h"
  #include "Language.h"
  #include "Database/DatabaseEnv.h"
  #include "WorldPacket.h"
  #include "WorldSession.h"
  #include "Opcodes.h"
  #include "Log.h"
  #include "World.h"
  #include "ObjectMgr.h"
  #include "ObjectGuid.h"
  #include "Player.h"
  #include "UpdateMask.h"
  #include "GridNotifiersImpl.h"
  #include "CellImpl.h"
  #include "AccountMgr.h"
  #include "SpellMgr.h"
  #include "PoolManager.h"
  #include "GameEventMgr.h"
  #include "AuctionHouseBot/AuctionHouseBot.h"
  #ifdef ENABLE_ELUNA
  #include "LuaEngine.h"
  #endif /* ENABLE_ELUNA */
  
  // Supported shift-links (client generated and server side)
  // |color|Harea:area_id|h[name]|h|r
  // |color|Hareatrigger:id|h[name]|h|r
  // |color|Hareatrigger_target:id|h[name]|h|r
  // |color|Hcreature:creature_guid|h[name]|h|r
  // |color|Hcreature_entry:creature_id|h[name]|h|r
  // |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r          - client, at shift click in recipes list dialog
  // |color|Hgameevent:id|h[name]|h|r
  // |color|Hgameobject:go_guid|h[name]|h|r
  // |color|Hgameobject_entry:go_id|h[name]|h|r
  // |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:0:0:0|h[name]|h|r   - client, item icon shift click
  // |color|Hitemset:itemset_id|h[name]|h|r
  // |color|Hplayer:name|h[name]|h|r                                        - client, in some messages, at click copy only name instead link, so no way generate it in client string send to server
  // |color|Hpool:pool_id|h[name]|h|r
  // |color|Hquest:quest_id:quest_level|h[name]|h|r                         - client, quest list name shift-click
  // |color|Hskill:skill_id|h[name]|h|r
  // |color|Hspell:spell_id|h[name]|h|r                                     - client, spellbook spell icon shift-click
  // |color|Htalent:talent_id,rank|h[name]|h|r                              - client, talent icon shift-click rank==-1 if shift-copy unlearned talent
  // |color|Htaxinode:id|h[name]|h|r
  // |color|Htele:id|h[name]|h|r
  // |color|Htitle:id|h[name]|h|r
  
  bool ChatHandler::load_command_table = true;
  
  ChatCommand* ChatHandler::getCommandTable()
  {
      static ChatCommand accountSetCommandTable[] =
      {
          { "addon",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAccountSetAddonCommand,     "", NULL },
          { "gmlevel",        SEC_CONSOLE,        true,  &ChatHandler::HandleAccountSetGmLevelCommand,   "", NULL },
          { "password",       SEC_CONSOLE,        true,  &ChatHandler::HandleAccountSetPasswordCommand,  "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand accountCommandTable[] =
      {
          { "characters",     SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAccountCharactersCommand,   "", NULL },
          { "create",         SEC_CONSOLE,        true,  &ChatHandler::HandleAccountCreateCommand,       "", NULL },
          { "delete",         SEC_CONSOLE,        true,  &ChatHandler::HandleAccountDeleteCommand,       "", NULL },
          { "onlinelist",     SEC_CONSOLE,        true,  &ChatHandler::HandleAccountOnlineListCommand,   "", NULL },
          { "lock",           SEC_PLAYER,         true,  &ChatHandler::HandleAccountLockCommand,         "", NULL },
          { "set",            SEC_ADMINISTRATOR,  true,  NULL,                                           "", accountSetCommandTable },
          { "password",       SEC_PLAYER,         true,  &ChatHandler::HandleAccountPasswordCommand,     "", NULL },
          { "",               SEC_PLAYER,         true,  &ChatHandler::HandleAccountCommand,             "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand ahbotItemsAmountCommandTable[] =
      {
          { "grey",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountQualityCommand<AUCTION_QUALITY_GREY>,  "", NULL },
          { "white",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountQualityCommand<AUCTION_QUALITY_WHITE>, "", NULL },
          { "green",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountQualityCommand<AUCTION_QUALITY_GREEN>, "", NULL },
          { "blue",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountQualityCommand<AUCTION_QUALITY_BLUE>,  "", NULL },
          { "purple",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountQualityCommand<AUCTION_QUALITY_PURPLE>, "", NULL },
          { "orange",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountQualityCommand<AUCTION_QUALITY_ORANGE>, "", NULL },
          { "yellow",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountQualityCommand<AUCTION_QUALITY_YELLOW>, "", NULL },
          { "",               SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsAmountCommand,      "", NULL },
          { NULL,             0,                  true,  NULL,                                             "", NULL }
      };
  
      static ChatCommand ahbotItemsRatioCommandTable[] =
      {
          { "alliance",       SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsRatioHouseCommand<AUCTION_HOUSE_ALLIANCE>,  "", NULL },
          { "horde",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsRatioHouseCommand<AUCTION_HOUSE_HORDE>,     "", NULL },
          { "neutral",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsRatioHouseCommand<AUCTION_HOUSE_NEUTRAL>,   "", NULL },
          { "",               SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotItemsRatioCommand,      "", NULL },
          { NULL,             0,                  true,  NULL,                                             "", NULL }
      };
  
      static ChatCommand ahbotItemsCommandTable[] =
      {
          { "amount",         SEC_ADMINISTRATOR,  true,  NULL,                                           "", ahbotItemsAmountCommandTable},
          { "ratio",          SEC_ADMINISTRATOR,  true,  NULL,                                           "", ahbotItemsRatioCommandTable},
          { NULL,             0,                  true,  NULL,                                           "", NULL }
      };
  
      static ChatCommand ahbotCommandTable[] =
      {
          { "items",          SEC_ADMINISTRATOR,  true,  NULL,                                           "", ahbotItemsCommandTable},
          { "rebuild",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotRebuildCommand,        "", NULL },
          { "reload",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotReloadCommand,         "", NULL },
          { "status",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAHBotStatusCommand,         "", NULL },
          { NULL,             0,                  true,  NULL,                                           "", NULL }
      };
  
      static ChatCommand auctionCommandTable[] =
      {
          { "alliance",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleAuctionAllianceCommand,     "", NULL },
          { "goblin",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleAuctionGoblinCommand,       "", NULL },
          { "horde",          SEC_ADMINISTRATOR,  false, &ChatHandler::HandleAuctionHordeCommand,        "", NULL },
          { "item",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleAuctionItemCommand,         "", NULL },
          { "",               SEC_ADMINISTRATOR,  false, &ChatHandler::HandleAuctionCommand,             "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand banCommandTable[] =
      {
          { "account",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanAccountCommand,          "", NULL },
          { "character",      SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanCharacterCommand,        "", NULL },
          { "ip",             SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanIPCommand,               "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand baninfoCommandTable[] =
      {
          { "account",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanInfoAccountCommand,      "", NULL },
          { "character",      SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanInfoCharacterCommand,    "", NULL },
          { "ip",             SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanInfoIPCommand,           "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand banlistCommandTable[] =
      {
          { "account",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanListAccountCommand,      "", NULL },
          { "character",      SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanListCharacterCommand,    "", NULL },
          { "ip",             SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleBanListIPCommand,           "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand castCommandTable[] =
      {
          { "back",           SEC_ADMINISTRATOR,  false, &ChatHandler::HandleCastBackCommand,            "", NULL },
          { "dist",           SEC_ADMINISTRATOR,  false, &ChatHandler::HandleCastDistCommand,            "", NULL },
          { "self",           SEC_ADMINISTRATOR,  false, &ChatHandler::HandleCastSelfCommand,            "", NULL },
          { "target",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleCastTargetCommand,          "", NULL },
          { "",               SEC_ADMINISTRATOR,  false, &ChatHandler::HandleCastCommand,                "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand characterDeletedCommandTable[] =
      {
          { "delete",         SEC_CONSOLE,        true,  &ChatHandler::HandleCharacterDeletedDeleteCommand, "", NULL },
          { "list",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleCharacterDeletedListCommand, "", NULL },
          { "restore",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleCharacterDeletedRestoreCommand, "", NULL },
          { "old",            SEC_CONSOLE,        true,  &ChatHandler::HandleCharacterDeletedOldCommand, "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand characterCommandTable[] =
      {
          { "deleted",        SEC_GAMEMASTER,     true,  NULL,                                           "", characterDeletedCommandTable},
          { "erase",          SEC_CONSOLE,        true,  &ChatHandler::HandleCharacterEraseCommand,      "", NULL },
          { "level",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleCharacterLevelCommand,      "", NULL },
          { "rename",         SEC_GAMEMASTER,     true,  &ChatHandler::HandleCharacterRenameCommand,     "", NULL },
          { "reputation",     SEC_GAMEMASTER,     true,  &ChatHandler::HandleCharacterReputationCommand, "", NULL },
          { "titles",         SEC_GAMEMASTER,     true,  &ChatHandler::HandleCharacterTitlesCommand,     "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand debugPlayCommandTable[] =
      {
          { "cinematic",      SEC_MODERATOR,      false, &ChatHandler::HandleDebugPlayCinematicCommand,       "", NULL },
          { "sound",          SEC_MODERATOR,      false, &ChatHandler::HandleDebugPlaySoundCommand,           "", NULL },
          { NULL,             0,                  false, NULL,                                                "", NULL }
      };
  
      static ChatCommand debugSendCommandTable[] =
      {
          { "buyerror",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendBuyErrorCommand,        "", NULL },
          { "channelnotify",  SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendChannelNotifyCommand,   "", NULL },
          { "chatmmessage",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendChatMsgCommand,         "", NULL },
          { "equiperror",     SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendEquipErrorCommand,      "", NULL },
          { "opcode",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendOpcodeCommand,          "", NULL },
          { "poi",            SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendPoiCommand,             "", NULL },
          { "qpartymsg",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendQuestPartyMsgCommand,   "", NULL },
          { "qinvalidmsg",    SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendQuestInvalidMsgCommand, "", NULL },
          { "sellerror",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendSellErrorCommand,       "", NULL },
          { "spellfail",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSendSpellFailCommand,       "", NULL },
          { NULL,             0,                  false, NULL,                                                "", NULL }
      };
  
      static ChatCommand debugCommandTable[] =
      {
          { "anim",           SEC_GAMEMASTER,     false, &ChatHandler::HandleDebugAnimCommand,                "", NULL },
          { "arena",          SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugArenaCommand,               "", NULL },
          { "bg",             SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugBattlegroundCommand,        "", NULL },
          { "getitemstate",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugGetItemStateCommand,        "", NULL },
          { "lootrecipient",  SEC_GAMEMASTER,     false, &ChatHandler::HandleDebugGetLootRecipientCommand,    "", NULL },
          { "getitemvalue",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugGetItemValueCommand,        "", NULL },
          { "getvalue",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugGetValueCommand,            "", NULL },
          { "moditemvalue",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugModItemValueCommand,        "", NULL },
          { "modvalue",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugModValueCommand,            "", NULL },
          { "play",           SEC_MODERATOR,      false, NULL,                                                "", debugPlayCommandTable },
          { "send",           SEC_ADMINISTRATOR,  false, NULL,                                                "", debugSendCommandTable },
          { "setaurastate",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSetAuraStateCommand,        "", NULL },
          { "setitemvalue",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSetItemValueCommand,        "", NULL },
          { "setvalue",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSetValueCommand,            "", NULL },
          { "spellcheck",     SEC_CONSOLE,        true,  &ChatHandler::HandleDebugSpellCheckCommand,          "", NULL },
          { "spellcoefs",     SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleDebugSpellCoefsCommand,          "", NULL },
          { "spellmods",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugSpellModsCommand,           "", NULL },
          { "uws",            SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDebugUpdateWorldStateCommand,    "", NULL },
          { NULL,             0,                  false, NULL,                                                "", NULL }
      };
  
      static ChatCommand eventCommandTable[] =
      {
          { "list",           SEC_GAMEMASTER,     true,  &ChatHandler::HandleEventListCommand,           "", NULL },
          { "start",          SEC_GAMEMASTER,     true,  &ChatHandler::HandleEventStartCommand,          "", NULL },
          { "stop",           SEC_GAMEMASTER,     true,  &ChatHandler::HandleEventStopCommand,           "", NULL },
          { "",               SEC_GAMEMASTER,     true,  &ChatHandler::HandleEventInfoCommand,           "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand gmCommandTable[] =
      {
          { "chat",           SEC_MODERATOR,      false, &ChatHandler::HandleGMChatCommand,              "", NULL },
          { "fly",            SEC_ADMINISTRATOR,  false, &ChatHandler::HandleGMFlyCommand,               "", NULL },
          { "ingame",         SEC_PLAYER,         true,  &ChatHandler::HandleGMListIngameCommand,        "", NULL },
          { "list",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleGMListFullCommand,          "", NULL },
          { "visible",        SEC_MODERATOR,      false, &ChatHandler::HandleGMVisibleCommand,           "", NULL },
          { "setview",        SEC_MODERATOR,      false, &ChatHandler::HandleSetViewCommand,             "", NULL },
          { "",               SEC_MODERATOR,      false, &ChatHandler::HandleGMCommand,                  "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand goCommandTable[] =
      {
          { "creature",       SEC_MODERATOR,      false, &ChatHandler::HandleGoCreatureCommand,          "", NULL },
          { "graveyard",      SEC_MODERATOR,      false, &ChatHandler::HandleGoGraveyardCommand,         "", NULL },
          { "grid",           SEC_MODERATOR,      false, &ChatHandler::HandleGoGridCommand,              "", NULL },
          { "object",         SEC_MODERATOR,      false, &ChatHandler::HandleGoObjectCommand,            "", NULL },
          { "taxinode",       SEC_MODERATOR,      false, &ChatHandler::HandleGoTaxinodeCommand,          "", NULL },
          { "trigger",        SEC_MODERATOR,      false, &ChatHandler::HandleGoTriggerCommand,           "", NULL },
          { "zonexy",         SEC_MODERATOR,      false, &ChatHandler::HandleGoZoneXYCommand,            "", NULL },
          { "xy",             SEC_MODERATOR,      false, &ChatHandler::HandleGoXYCommand,                "", NULL },
          { "xyz",            SEC_MODERATOR,      false, &ChatHandler::HandleGoXYZCommand,               "", NULL },
          { "",               SEC_MODERATOR,      false, &ChatHandler::HandleGoCommand,                  "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand gobjectCommandTable[] =
      {
          { "add",            SEC_GAMEMASTER,     false, &ChatHandler::HandleGameObjectAddCommand,       "", NULL },
          { "delete",         SEC_GAMEMASTER,     false, &ChatHandler::HandleGameObjectDeleteCommand,    "", NULL },
          { "move",           SEC_GAMEMASTER,     false, &ChatHandler::HandleGameObjectMoveCommand,      "", NULL },
          { "near",           SEC_GAMEMASTER,     false, &ChatHandler::HandleGameObjectNearCommand,      "", NULL },
          { "target",         SEC_GAMEMASTER,     false, &ChatHandler::HandleGameObjectTargetCommand,    "", NULL },
          { "turn",           SEC_GAMEMASTER,     false, &ChatHandler::HandleGameObjectTurnCommand,      "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand guildCommandTable[] =
      {
          { "create",         SEC_GAMEMASTER,     true,  &ChatHandler::HandleGuildCreateCommand,         "", NULL },
          { "delete",         SEC_GAMEMASTER,     true,  &ChatHandler::HandleGuildDeleteCommand,         "", NULL },
          { "invite",         SEC_GAMEMASTER,     true,  &ChatHandler::HandleGuildInviteCommand,         "", NULL },
          { "uninvite",       SEC_GAMEMASTER,     true,  &ChatHandler::HandleGuildUninviteCommand,       "", NULL },
          { "rank",           SEC_GAMEMASTER,     true,  &ChatHandler::HandleGuildRankCommand,           "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand honorCommandTable[] =
      {
          { "add",            SEC_GAMEMASTER,     false, &ChatHandler::HandleHonorAddCommand,            "", NULL },
          { "addkill",        SEC_GAMEMASTER,     false, &ChatHandler::HandleHonorAddKillCommand,        "", NULL },
          { "update",         SEC_GAMEMASTER,     false, &ChatHandler::HandleHonorUpdateCommand,         "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand instanceCommandTable[] =
      {
          { "listbinds",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleInstanceListBindsCommand,   "", NULL },
          { "unbind",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleInstanceUnbindCommand,      "", NULL },
          { "stats",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleInstanceStatsCommand,       "", NULL },
          { "savedata",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleInstanceSaveDataCommand,    "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand learnCommandTable[] =
      {
          { "all",            SEC_ADMINISTRATOR,  false, &ChatHandler::HandleLearnAllCommand,            "", NULL },
          { "all_gm",         SEC_GAMEMASTER,     false, &ChatHandler::HandleLearnAllGMCommand,          "", NULL },
          { "all_crafts",     SEC_GAMEMASTER,     false, &ChatHandler::HandleLearnAllCraftsCommand,      "", NULL },
          { "all_default",    SEC_MODERATOR,      false, &ChatHandler::HandleLearnAllDefaultCommand,     "", NULL },
          { "all_lang",       SEC_MODERATOR,      false, &ChatHandler::HandleLearnAllLangCommand,        "", NULL },
          { "all_myclass",    SEC_ADMINISTRATOR,  false, &ChatHandler::HandleLearnAllMyClassCommand,     "", NULL },
          { "all_myspells",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleLearnAllMySpellsCommand,    "", NULL },
          { "all_mytalents",  SEC_ADMINISTRATOR,  false, &ChatHandler::HandleLearnAllMyTalentsCommand,   "", NULL },
          { "all_recipes",    SEC_GAMEMASTER,     false, &ChatHandler::HandleLearnAllRecipesCommand,     "", NULL },
          { "",               SEC_ADMINISTRATOR,  false, &ChatHandler::HandleLearnCommand,               "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand listCommandTable[] =
      {
          { "auras",          SEC_ADMINISTRATOR,  false, &ChatHandler::HandleListAurasCommand,           "", NULL },
          { "creature",       SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleListCreatureCommand,        "", NULL },
          { "item",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleListItemCommand,            "", NULL },
          { "object",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleListObjectCommand,          "", NULL },
          { "talents",        SEC_ADMINISTRATOR,  false, &ChatHandler::HandleListTalentsCommand,         "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand lookupAccountCommandTable[] =
      {
          { "email",          SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupAccountEmailCommand,  "", NULL },
          { "ip",             SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupAccountIpCommand,     "", NULL },
          { "name",           SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupAccountNameCommand,   "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand lookupPlayerCommandTable[] =
      {
          { "account",        SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupPlayerAccountCommand, "", NULL },
          { "email",          SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupPlayerEmailCommand,   "", NULL },
          { "ip",             SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupPlayerIpCommand,      "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand lookupCommandTable[] =
      {
          { "account",        SEC_GAMEMASTER,     true,  NULL,                                           "", lookupAccountCommandTable },
          { "area",           SEC_MODERATOR,      true,  &ChatHandler::HandleLookupAreaCommand,          "", NULL },
          { "creature",       SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupCreatureCommand,      "", NULL },
          { "event",          SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupEventCommand,         "", NULL },
          { "faction",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupFactionCommand,       "", NULL },
          { "item",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupItemCommand,          "", NULL },
          { "itemset",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupItemSetCommand,       "", NULL },
          { "object",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupObjectCommand,        "", NULL },
          { "quest",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupQuestCommand,         "", NULL },
          { "player",         SEC_GAMEMASTER,     true,  NULL,                                           "", lookupPlayerCommandTable },
          { "pool",           SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupPoolCommand,          "", NULL },
          { "skill",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupSkillCommand,         "", NULL },
          { "spell",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupSpellCommand,         "", NULL },
          { "taxinode",       SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLookupTaxiNodeCommand,      "", NULL },
          { "tele",           SEC_MODERATOR,      true,  &ChatHandler::HandleLookupTeleCommand,          "", NULL },
          { "title",          SEC_GAMEMASTER,     true,  &ChatHandler::HandleLookupTitleCommand,         "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand mmapCommandTable[] =
      {
          { "path",           SEC_GAMEMASTER,     false, &ChatHandler::HandleMmapPathCommand,            "", NULL },
          { "loc",            SEC_GAMEMASTER,     false, &ChatHandler::HandleMmapLocCommand,             "", NULL },
          { "loadedtiles",    SEC_GAMEMASTER,     false, &ChatHandler::HandleMmapLoadedTilesCommand,     "", NULL },
          { "stats",          SEC_GAMEMASTER,     false, &ChatHandler::HandleMmapStatsCommand,           "", NULL },
          { "testarea",       SEC_GAMEMASTER,     false, &ChatHandler::HandleMmapTestArea,               "", NULL },
          { "testheight",     SEC_GAMEMASTER,     false, &ChatHandler::HandleMmapTestHeight,             "", NULL },
          { "",               SEC_ADMINISTRATOR,  false, &ChatHandler::HandleMmap,                       "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand modifyCommandTable[] =
      {
          { "hp",             SEC_MODERATOR,      false, &ChatHandler::HandleModifyHPCommand,            "", NULL },
          { "mana",           SEC_MODERATOR,      false, &ChatHandler::HandleModifyManaCommand,          "", NULL },
          { "rage",           SEC_MODERATOR,      false, &ChatHandler::HandleModifyRageCommand,          "", NULL },
          { "energy",         SEC_MODERATOR,      false, &ChatHandler::HandleModifyEnergyCommand,        "", NULL },
          { "money",          SEC_MODERATOR,      false, &ChatHandler::HandleModifyMoneyCommand,         "", NULL },
          { "speed",          SEC_MODERATOR,      false, &ChatHandler::HandleModifySpeedCommand,         "", NULL },
          { "swim",           SEC_MODERATOR,      false, &ChatHandler::HandleModifySwimCommand,          "", NULL },
          { "scale",          SEC_MODERATOR,      false, &ChatHandler::HandleModifyScaleCommand,         "", NULL },
          { "bwalk",          SEC_MODERATOR,      false, &ChatHandler::HandleModifyBWalkCommand,         "", NULL },
          { "fly",            SEC_MODERATOR,      false, &ChatHandler::HandleModifyFlyCommand,           "", NULL },
          { "aspeed",         SEC_MODERATOR,      false, &ChatHandler::HandleModifyASpeedCommand,        "", NULL },
          { "faction",        SEC_MODERATOR,      false, &ChatHandler::HandleModifyFactionCommand,       "", NULL },
          { "tp",             SEC_MODERATOR,      false, &ChatHandler::HandleModifyTalentCommand,        "", NULL },
          { "mount",          SEC_MODERATOR,      false, &ChatHandler::HandleModifyMountCommand,         "", NULL },
          { "honor",          SEC_MODERATOR,      false, &ChatHandler::HandleModifyHonorCommand,         "", NULL },
          { "rep",            SEC_GAMEMASTER,     false, &ChatHandler::HandleModifyRepCommand,           "", NULL },
          { "arena",          SEC_MODERATOR,      false, &ChatHandler::HandleModifyArenaCommand,         "", NULL },
          { "drunk",          SEC_MODERATOR,      false, &ChatHandler::HandleModifyDrunkCommand,         "", NULL },
          { "standstate",     SEC_GAMEMASTER,     false, &ChatHandler::HandleModifyStandStateCommand,    "", NULL },
          { "morph",          SEC_GAMEMASTER,     false, &ChatHandler::HandleModifyMorphCommand,         "", NULL },
          { "gender",         SEC_GAMEMASTER,     false, &ChatHandler::HandleModifyGenderCommand,        "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand npcCommandTable[] =
      {
          { "add",            SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcAddCommand,              "", NULL },
          { "additem",        SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcAddVendorItemCommand,    "", NULL },
          { "aiinfo",         SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcAIInfoCommand,           "", NULL },
          { "allowmove",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleNpcAllowMovementCommand,    "", NULL },
          { "changeentry",    SEC_ADMINISTRATOR,  false, &ChatHandler::HandleNpcChangeEntryCommand,      "", NULL },
          { "changelevel",    SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcChangeLevelCommand,      "", NULL },
          { "delete",         SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcDeleteCommand,           "", NULL },
          { "delitem",        SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcDelVendorItemCommand,    "", NULL },
          { "factionid",      SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcFactionIdCommand,        "", NULL },
          { "flag",           SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcFlagCommand,             "", NULL },
          { "follow",         SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcFollowCommand,           "", NULL },
          { "info",           SEC_ADMINISTRATOR,  false, &ChatHandler::HandleNpcInfoCommand,             "", NULL },
          { "move",           SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcMoveCommand,             "", NULL },
          { "playemote",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleNpcPlayEmoteCommand,        "", NULL },
          { "setmodel",       SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcSetModelCommand,         "", NULL },
          { "setmovetype",    SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcSetMoveTypeCommand,      "", NULL },
          { "spawndist",      SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcSpawnDistCommand,        "", NULL },
          { "spawntime",      SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcSpawnTimeCommand,        "", NULL },
          { "say",            SEC_MODERATOR,      false, &ChatHandler::HandleNpcSayCommand,              "", NULL },
          { "textemote",      SEC_MODERATOR,      false, &ChatHandler::HandleNpcTextEmoteCommand,        "", NULL },
          { "unfollow",       SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcUnFollowCommand,         "", NULL },
          { "whisper",        SEC_MODERATOR,      false, &ChatHandler::HandleNpcWhisperCommand,          "", NULL },
          { "yell",           SEC_MODERATOR,      false, &ChatHandler::HandleNpcYellCommand,             "", NULL },
          { "tame",           SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcTameCommand,             "", NULL },
          { "setdeathstate",  SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcSetDeathStateCommand,    "", NULL },
  
          //{ TODO: fix or remove this commands
          { "addweapon",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleNpcAddWeaponCommand,        "", NULL },
          { "name",           SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcNameCommand,             "", NULL },
          { "subname",        SEC_GAMEMASTER,     false, &ChatHandler::HandleNpcSubNameCommand,          "", NULL },
          //}
  
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand pdumpCommandTable[] =
      {
          { "load",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandlePDumpLoadCommand,           "", NULL },
          { "write",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandlePDumpWriteCommand,          "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand poolCommandTable[] =
      {
          { "list",           SEC_GAMEMASTER,     false, &ChatHandler::HandlePoolListCommand,            "", NULL },
          { "spawns",         SEC_GAMEMASTER,     false, &ChatHandler::HandlePoolSpawnsCommand,          "", NULL },
          { "",               SEC_GAMEMASTER,     true,  &ChatHandler::HandlePoolInfoCommand,            "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand questCommandTable[] =
      {
          { "add",            SEC_ADMINISTRATOR,  false, &ChatHandler::HandleQuestAddCommand,            "", NULL },
          { "complete",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleQuestCompleteCommand,       "", NULL },
          { "remove",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleQuestRemoveCommand,         "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand reloadCommandTable[] =
      {
          { "all",            SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllCommand,           "", NULL },
          { "all_area",       SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllAreaCommand,       "", NULL },
          { "all_eventai",    SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllEventAICommand,    "", NULL },
          { "all_gossips",    SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllGossipsCommand,    "", NULL },
          { "all_item",       SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllItemCommand,       "", NULL },
          { "all_locales",    SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllLocalesCommand,    "", NULL },
          { "all_loot",       SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllLootCommand,       "", NULL },
          { "all_npc",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllNpcCommand,        "", NULL },
          { "all_quest",      SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllQuestCommand,      "", NULL },
          { "all_scripts",    SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllScriptsCommand,    "", NULL },
          { "all_spell",      SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadAllSpellCommand,      "", NULL },
  
          { "config",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReloadConfigCommand,        "", NULL },
  
          { "areatrigger_involvedrelation", SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadQuestAreaTriggersCommand,       "", NULL },
          { "areatrigger_tavern",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadAreaTriggerTavernCommand,       "", NULL },
          { "areatrigger_teleport",        SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadAreaTriggerTeleportCommand,     "", NULL },
          { "command",                     SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadCommandCommand,                 "", NULL },
          { "conditions",                  SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadConditionsCommand,              "", NULL },
          { "creature_ai_scripts",         SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadEventAIScriptsCommand,          "", NULL },
          { "creature_ai_summons",         SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadEventAISummonsCommand,          "", NULL },
          { "creature_ai_texts",           SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadEventAITextsCommand,            "", NULL },
          { "creature_battleground",       SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadBattleEventCommand,             "", NULL },
          { "creature_involvedrelation",   SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadCreatureQuestInvRelationsCommand, "", NULL },
          { "creature_loot_template",      SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesCreatureCommand,   "", NULL },
          { "creature_questrelation",      SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadCreatureQuestRelationsCommand,  "", NULL },
          { "creature_template_classlevelstats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreaturesStatsCommand,     "", NULL },
          { "db_script_string",            SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDbScriptStringCommand,          "", NULL },
          { "dbscripts_on_creature_death", SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDBScriptsOnCreatureDeathCommand, "", NULL },
          { "dbscripts_on_event",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDBScriptsOnEventCommand,        "", NULL },
          { "dbscripts_on_gossip",         SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDBScriptsOnGossipCommand,       "", NULL },
          { "dbscripts_on_go_use",         SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDBScriptsOnGoUseCommand,        "", NULL },
          { "dbscripts_on_quest_end",      SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDBScriptsOnQuestEndCommand,     "", NULL },
          { "dbscripts_on_quest_start",    SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDBScriptsOnQuestStartCommand,   "", NULL },
          { "dbscripts_on_spell",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadDBScriptsOnSpellCommand,        "", NULL },
          { "disenchant_loot_template",    SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesDisenchantCommand, "", NULL },
          { "fishing_loot_template",       SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesFishingCommand,    "", NULL },
          { "game_graveyard_zone",         SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadGameGraveyardZoneCommand,       "", NULL },
          { "game_tele",                   SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadGameTeleCommand,                "", NULL },
          { "gameobject_involvedrelation", SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadGOQuestInvRelationsCommand,     "", NULL },
          { "gameobject_loot_template",    SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesGameobjectCommand, "", NULL },
          { "gameobject_questrelation",    SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadGOQuestRelationsCommand,        "", NULL },
          { "gameobject_battleground",     SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadBattleEventCommand,             "", NULL },
          { "gossip_menu",                 SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadGossipMenuCommand,              "", NULL },
          { "gossip_menu_option",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadGossipMenuCommand,              "", NULL },
          { "item_enchantment_template",   SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadItemEnchantementsCommand,       "", NULL },
          { "item_loot_template",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesItemCommand,       "", NULL },
          { "item_required_target",        SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadItemRequiredTragetCommand,      "", NULL },
          { "locales_creature",            SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesCreatureCommand,         "", NULL },
          { "locales_gameobject",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesGameobjectCommand,       "", NULL },
          { "locales_gossip_menu_option",  SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesGossipMenuOptionCommand, "", NULL },
          { "locales_item",                SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesItemCommand,             "", NULL },
          { "locales_npc_text",            SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesNpcTextCommand,          "", NULL },
          { "locales_page_text",           SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesPageTextCommand,         "", NULL },
          { "locales_points_of_interest",  SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL },
          { "locales_quest",               SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLocalesQuestCommand,            "", NULL },
          { "mail_level_reward",           SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadMailLevelRewardCommand,         "", NULL },
          { "mail_loot_template",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesMailCommand,       "", NULL },
          { "mangos_string",               SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadMangosStringCommand,            "", NULL },
          { "npc_gossip",                  SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadNpcGossipCommand,               "", NULL },
          { "npc_text",                    SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadNpcTextCommand,                 "", NULL },
          { "npc_trainer",                 SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadNpcTrainerCommand,              "", NULL },
          { "npc_vendor",                  SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadNpcVendorCommand,               "", NULL },
          { "page_text",                   SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadPageTextsCommand,               "", NULL },
          { "pickpocketing_loot_template", SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand, "", NULL},
          { "points_of_interest",          SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadPointsOfInterestCommand,        "", NULL },
          { "prospecting_loot_template",   SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesProspectingCommand, "", NULL },
          { "quest_template",              SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadQuestTemplateCommand,           "", NULL },
          { "reference_loot_template",     SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesReferenceCommand,  "", NULL },
          { "reserved_name",               SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadReservedNameCommand,            "", NULL },
          { "reputation_reward_rate",      SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadReputationRewardRateCommand,    "", NULL },
          { "reputation_spillover_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadReputationSpilloverTemplateCommand, "", NULL },
          { "skill_discovery_template",    SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSkillDiscoveryTemplateCommand,  "", NULL },
          { "skill_extra_item_template",   SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSkillExtraItemTemplateCommand,  "", NULL },
          { "skill_fishing_base_level",    SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSkillFishingBaseLevelCommand,   "", NULL },
          { "skinning_loot_template",      SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadLootTemplatesSkinningCommand,   "", NULL },
          { "spell_affect",                SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellAffectCommand,             "", NULL },
          { "spell_area",                  SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellAreaCommand,               "", NULL },
          { "spell_bonus_data",            SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellBonusesCommand,            "", NULL },
          { "spell_chain",                 SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellChainCommand,              "", NULL },
          { "spell_elixir",                SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellElixirCommand,             "", NULL },
          { "spell_learn_spell",           SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellLearnSpellCommand,         "", NULL },
          { "spell_pet_auras",             SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellPetAurasCommand,           "", NULL },
          { "spell_proc_event",            SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellProcEventCommand,          "", NULL },
          { "spell_proc_item_enchant",     SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellProcItemEnchantCommand,    "", NULL },
          { "spell_script_target",         SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellScriptTargetCommand,       "", NULL },
          { "spell_target_position",       SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellTargetPositionCommand,     "", NULL },
          { "spell_threats",               SEC_ADMINISTRATOR, true,  &ChatHandler::HandleReloadSpellThreatsCommand,            "", NULL },
  
          { NULL,                          0,                 false, NULL,                                                     "", NULL }
      };
  
      static ChatCommand resetCommandTable[] =
      {
          { "honor",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleResetHonorCommand,          "", NULL },
          { "level",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleResetLevelCommand,          "", NULL },
          { "spells",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleResetSpellsCommand,         "", NULL },
          { "stats",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleResetStatsCommand,          "", NULL },
          { "talents",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleResetTalentsCommand,        "", NULL },
          { "all",            SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleResetAllCommand,            "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand sendMassCommandTable[] =
      {
          { "items",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleSendMassItemsCommand,       "", NULL },
          { "mail",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleSendMassMailCommand,        "", NULL },
          { "money",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleSendMassMoneyCommand,       "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand sendCommandTable[] =
      {
          { "mass",           SEC_ADMINISTRATOR,  true,  NULL,                                           "", sendMassCommandTable },
  
          { "items",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleSendItemsCommand,           "", NULL },
          { "mail",           SEC_MODERATOR,      true,  &ChatHandler::HandleSendMailCommand,            "", NULL },
          { "message",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleSendMessageCommand,         "", NULL },
          { "money",          SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleSendMoneyCommand,           "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand serverIdleRestartCommandTable[] =
      {
          { "cancel",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerShutDownCancelCommand, "", NULL },
          { ""   ,            SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerIdleRestartCommand,   "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand serverIdleShutdownCommandTable[] =
      {
          { "cancel",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerShutDownCancelCommand, "", NULL },
          { ""   ,            SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerIdleShutDownCommand,  "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand serverRestartCommandTable[] =
      {
          { "cancel",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerShutDownCancelCommand, "", NULL },
          { ""   ,            SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerRestartCommand,       "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand serverShutdownCommandTable[] =
      {
          { "cancel",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerShutDownCancelCommand, "", NULL },
          { ""   ,            SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerShutDownCommand,      "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand serverLogCommandTable[] =
      {
          { "filter",         SEC_CONSOLE,        true,  &ChatHandler::HandleServerLogFilterCommand,     "", NULL },
          { "level",          SEC_CONSOLE,        true,  &ChatHandler::HandleServerLogLevelCommand,      "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand serverSetCommandTable[] =
      {
          { "motd",           SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerSetMotdCommand,       "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand serverCommandTable[] =
      {
          { "corpses",        SEC_GAMEMASTER,     true,  &ChatHandler::HandleServerCorpsesCommand,       "", NULL },
          { "exit",           SEC_CONSOLE,        true,  &ChatHandler::HandleServerExitCommand,          "", NULL },
          { "idlerestart",    SEC_ADMINISTRATOR,  true,  NULL,                                           "", serverIdleRestartCommandTable },
          { "idleshutdown",   SEC_ADMINISTRATOR,  true,  NULL,                                           "", serverIdleShutdownCommandTable },
          { "info",           SEC_PLAYER,         true,  &ChatHandler::HandleServerInfoCommand,          "", NULL },
          { "log",            SEC_CONSOLE,        true,  NULL,                                           "", serverLogCommandTable },
          { "motd",           SEC_PLAYER,         true,  &ChatHandler::HandleServerMotdCommand,          "", NULL },
          { "plimit",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerPLimitCommand,        "", NULL },
          { "resetallraid",   SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleServerResetAllRaidCommand,  "", NULL },
          { "restart",        SEC_ADMINISTRATOR,  true,  NULL,                                           "", serverRestartCommandTable },
          { "shutdown",       SEC_ADMINISTRATOR,  true,  NULL,                                           "", serverShutdownCommandTable },
          { "set",            SEC_ADMINISTRATOR,  true,  NULL,                                           "", serverSetCommandTable },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand teleCommandTable[] =
      {
          { "add",            SEC_ADMINISTRATOR,  false, &ChatHandler::HandleTeleAddCommand,             "", NULL },
          { "del",            SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleTeleDelCommand,             "", NULL },
          { "name",           SEC_MODERATOR,      true,  &ChatHandler::HandleTeleNameCommand,            "", NULL },
          { "group",          SEC_MODERATOR,      false, &ChatHandler::HandleTeleGroupCommand,           "", NULL },
          { "",               SEC_MODERATOR,      false, &ChatHandler::HandleTeleCommand,                "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand titlesCommandTable[] =
      {
          { "add",            SEC_GAMEMASTER,     false, &ChatHandler::HandleTitlesAddCommand,           "", NULL },
          { "current",        SEC_GAMEMASTER,     false, &ChatHandler::HandleTitlesCurrentCommand,       "", NULL },
          { "remove",         SEC_GAMEMASTER,     false, &ChatHandler::HandleTitlesRemoveCommand,        "", NULL },
          { "setmask",        SEC_GAMEMASTER,     false, &ChatHandler::HandleTitlesSetMaskCommand,       "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand triggerCommandTable[] =
      {
          { "active",         SEC_GAMEMASTER,     false, &ChatHandler::HandleTriggerActiveCommand,       "", NULL },
          { "near",           SEC_GAMEMASTER,     false, &ChatHandler::HandleTriggerNearCommand,         "", NULL },
          { "",               SEC_GAMEMASTER,     true,  &ChatHandler::HandleTriggerCommand,             "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand unbanCommandTable[] =
      {
          { "account",        SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleUnBanAccountCommand,      "", NULL },
          { "character",      SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleUnBanCharacterCommand,    "", NULL },
          { "ip",             SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleUnBanIPCommand,           "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand wpCommandTable[] =
      {
          { "show",           SEC_GAMEMASTER,     false, &ChatHandler::HandleWpShowCommand,              "", NULL },
          { "add",            SEC_GAMEMASTER,     false, &ChatHandler::HandleWpAddCommand,               "", NULL },
          { "modify",         SEC_GAMEMASTER,     false, &ChatHandler::HandleWpModifyCommand,            "", NULL },
          { "export",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleWpExportCommand,            "", NULL },
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      static ChatCommand commandTable[] =
      {
          { "account",        SEC_PLAYER,         true,  NULL,                                           "", accountCommandTable  },
          { "auction",        SEC_ADMINISTRATOR,  false, NULL,                                           "", auctionCommandTable  },
          { "ahbot",          SEC_ADMINISTRATOR,  true,  NULL,                                           "", ahbotCommandTable    },
          { "cast",           SEC_ADMINISTRATOR,  false, NULL,                                           "", castCommandTable     },
          { "character",      SEC_GAMEMASTER,     true,  NULL,                                           "", characterCommandTable},
          { "debug",          SEC_MODERATOR,      true,  NULL,                                           "", debugCommandTable    },
          { "event",          SEC_GAMEMASTER,     false, NULL,                                           "", eventCommandTable    },
          { "gm",             SEC_PLAYER,         true,  NULL,                                           "", gmCommandTable       },
          { "honor",          SEC_GAMEMASTER,     false, NULL,                                           "", honorCommandTable    },
          { "go",             SEC_MODERATOR,      false, NULL,                                           "", goCommandTable       },
          { "gobject",        SEC_GAMEMASTER,     false, NULL,                                           "", gobjectCommandTable  },
          { "guild",          SEC_GAMEMASTER,     true,  NULL,                                           "", guildCommandTable    },
          { "instance",       SEC_ADMINISTRATOR,  true,  NULL,                                           "", instanceCommandTable },
          { "learn",          SEC_MODERATOR,      false, NULL,                                           "", learnCommandTable    },
          { "list",           SEC_ADMINISTRATOR,  true,  NULL,                                           "", listCommandTable     },
          { "lookup",         SEC_MODERATOR,      true,  NULL,                                           "", lookupCommandTable   },
          { "modify",         SEC_MODERATOR,      false, NULL,                                           "", modifyCommandTable   },
          { "npc",            SEC_MODERATOR,      false, NULL,                                           "", npcCommandTable      },
          { "pool",           SEC_GAMEMASTER,     true,  NULL,                                           "", poolCommandTable     },
          { "pdump",          SEC_ADMINISTRATOR,  true,  NULL,                                           "", pdumpCommandTable    },
          { "quest",          SEC_ADMINISTRATOR,  false, NULL,                                           "", questCommandTable    },
          { "reload",         SEC_ADMINISTRATOR,  true,  NULL,                                           "", reloadCommandTable   },
          { "reset",          SEC_ADMINISTRATOR,  true,  NULL,                                           "", resetCommandTable    },
          { "server",         SEC_PLAYER,         true,  NULL,                                           "", serverCommandTable   },
          { "tele",           SEC_MODERATOR,      true,  NULL,                                           "", teleCommandTable     },
          { "titles",         SEC_GAMEMASTER,     false, NULL,                                           "", titlesCommandTable   },
          { "trigger",        SEC_GAMEMASTER,     false, NULL,                                           "", triggerCommandTable  },
          { "wp",             SEC_GAMEMASTER,     false, NULL,                                           "", wpCommandTable       },
  
          { "aura",           SEC_ADMINISTRATOR,  false, &ChatHandler::HandleAuraCommand,                "", NULL },
          { "unaura",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleUnAuraCommand,              "", NULL },
          { "announce",       SEC_MODERATOR,      true,  &ChatHandler::HandleAnnounceCommand,            "", NULL },
          { "notify",         SEC_MODERATOR,      true,  &ChatHandler::HandleNotifyCommand,              "", NULL },
          { "appear",         SEC_MODERATOR,      false, &ChatHandler::HandleAppearCommand,              "", NULL },
          { "summon",         SEC_MODERATOR,      false, &ChatHandler::HandleSummonCommand,              "", NULL },
          { "groupgo",        SEC_MODERATOR,      false, &ChatHandler::HandleGroupgoCommand,             "", NULL },
          { "commands",       SEC_PLAYER,         true,  &ChatHandler::HandleCommandsCommand,            "", NULL },
          { "demorph",        SEC_GAMEMASTER,     false, &ChatHandler::HandleDeMorphCommand,             "", NULL },
          { "die",            SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDieCommand,                 "", NULL },
          { "revive",         SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleReviveCommand,              "", NULL },
          { "dismount",       SEC_PLAYER,         false, &ChatHandler::HandleDismountCommand,            "", NULL },
          { "gps",            SEC_MODERATOR,      false, &ChatHandler::HandleGPSCommand,                 "", NULL },
          { "guid",           SEC_GAMEMASTER,     false, &ChatHandler::HandleGUIDCommand,                "", NULL },
          { "help",           SEC_PLAYER,         true,  &ChatHandler::HandleHelpCommand,                "", NULL },
          { "itemmove",       SEC_GAMEMASTER,     false, &ChatHandler::HandleItemMoveCommand,            "", NULL },
          { "cooldown",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleCooldownCommand,            "", NULL },
          { "unlearn",        SEC_ADMINISTRATOR,  false, &ChatHandler::HandleUnLearnCommand,             "", NULL },
          { "distance",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleGetDistanceCommand,         "", NULL },
          { "recall",         SEC_MODERATOR,      false, &ChatHandler::HandleRecallCommand,              "", NULL },
          { "save",           SEC_PLAYER,         false, &ChatHandler::HandleSaveCommand,                "", NULL },
          { "saveall",        SEC_MODERATOR,      true,  &ChatHandler::HandleSaveAllCommand,             "", NULL },
          { "kick",           SEC_GAMEMASTER,     true,  &ChatHandler::HandleKickPlayerCommand,          "", NULL },
          { "ban",            SEC_ADMINISTRATOR,  true,  NULL,                                           "", banCommandTable      },
          { "unban",          SEC_ADMINISTRATOR,  true,  NULL,                                           "", unbanCommandTable    },
          { "baninfo",        SEC_ADMINISTRATOR,  false, NULL,                                           "", baninfoCommandTable  },
          { "banlist",        SEC_ADMINISTRATOR,  true,  NULL,                                           "", banlistCommandTable  },
          { "start",          SEC_PLAYER,         false, &ChatHandler::HandleStartCommand,               "", NULL },
          { "taxicheat",      SEC_MODERATOR,      false, &ChatHandler::HandleTaxiCheatCommand,           "", NULL },
          { "linkgrave",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleLinkGraveCommand,           "", NULL },
          { "neargrave",      SEC_ADMINISTRATOR,  false, &ChatHandler::HandleNearGraveCommand,           "", NULL },
          { "explorecheat",   SEC_ADMINISTRATOR,  false, &ChatHandler::HandleExploreCheatCommand,        "", NULL },
          { "levelup",        SEC_ADMINISTRATOR,  false, &ChatHandler::HandleLevelUpCommand,             "", NULL },
          { "showarea",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleShowAreaCommand,            "", NULL },
          { "hidearea",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleHideAreaCommand,            "", NULL },
          { "additem",        SEC_ADMINISTRATOR,  false, &ChatHandler::HandleAddItemCommand,             "", NULL },
          { "additemset",     SEC_ADMINISTRATOR,  false, &ChatHandler::HandleAddItemSetCommand,          "", NULL },
          { "bank",           SEC_ADMINISTRATOR,  false, &ChatHandler::HandleBankCommand,                "", NULL },
          { "wchange",        SEC_ADMINISTRATOR,  false, &ChatHandler::HandleChangeWeatherCommand,       "", NULL },
          { "ticket",         SEC_GAMEMASTER,     true,  &ChatHandler::HandleTicketCommand,              "", NULL },
          { "delticket",      SEC_GAMEMASTER,     true,  &ChatHandler::HandleDelTicketCommand,           "", NULL },
          { "maxskill",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleMaxSkillCommand,            "", NULL },
          { "setskill",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleSetSkillCommand,            "", NULL },
          { "whispers",       SEC_MODERATOR,      false, &ChatHandler::HandleWhispersCommand,            "", NULL },
          { "pinfo",          SEC_GAMEMASTER,     true,  &ChatHandler::HandlePInfoCommand,               "", NULL },
          { "respawn",        SEC_ADMINISTRATOR,  false, &ChatHandler::HandleRespawnCommand,             "", NULL },
          { "send",           SEC_MODERATOR,      true,  NULL,                                           "", sendCommandTable     },
          { "loadscripts",    SEC_ADMINISTRATOR,  true,  &ChatHandler::HandleLoadScriptsCommand,         "", NULL },
          { "mute",           SEC_MODERATOR,      true,  &ChatHandler::HandleMuteCommand,                "", NULL },
          { "unmute",         SEC_MODERATOR,      true,  &ChatHandler::HandleUnmuteCommand,              "", NULL },
          { "movegens",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleMovegensCommand,            "", NULL },
          { "cometome",       SEC_ADMINISTRATOR,  false, &ChatHandler::HandleComeToMeCommand,            "", NULL },
          { "damage",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleDamageCommand,              "", NULL },
          { "combatstop",     SEC_GAMEMASTER,     false, &ChatHandler::HandleCombatStopCommand,          "", NULL },
          { "flusharenapoints", SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand,    "", NULL },
          { "repairitems",    SEC_GAMEMASTER,     true,  &ChatHandler::HandleRepairitemsCommand,         "", NULL },
          { "stable",         SEC_ADMINISTRATOR,  false, &ChatHandler::HandleStableCommand,              "", NULL },
          { "waterwalk",      SEC_GAMEMASTER,     false, &ChatHandler::HandleWaterwalkCommand,           "", NULL },
          { "quit",           SEC_CONSOLE,        true,  &ChatHandler::HandleQuitCommand,                "", NULL },
          { "mmap",           SEC_GAMEMASTER,     false, NULL,                                           "", mmapCommandTable },
  
          { NULL,             0,                  false, NULL,                                           "", NULL }
      };
  
      if (load_command_table)
      {
          load_command_table = false;
  
          // check hardcoded part integrity
          CheckIntegrity(commandTable, NULL);
  
          QueryResult* result = WorldDatabase.Query("SELECT name,security,help FROM command");
          if (result)
          {
              do
              {
                  Field* fields = result->Fetch();
                  std::string name = fields[0].GetCppString();
  
                  SetDataForCommandInTable(commandTable, name.c_str(), fields[1].GetUInt16(), fields[2].GetCppString());
              }
              while (result->NextRow());
              delete result;
          }
      }
  
      return commandTable;
  }
  
  ChatHandler::ChatHandler(WorldSession* session) : m_session(session) {}
  
  ChatHandler::ChatHandler(Player* player) : m_session(player->GetSession()) {}
  
  ChatHandler::~ChatHandler() {}
  
  const char* ChatHandler::GetMangosString(int32 entry) const
  {
      return m_session->GetMangosString(entry);
  }
  
  const char* ChatHandler::GetOnOffStr(bool value) const
  {
      return value ?  GetMangosString(LANG_ON) : GetMangosString(LANG_OFF);
  }
  
  uint32 ChatHandler::GetAccountId() const
  {
      return m_session->GetAccountId();
  }
  
  AccountTypes ChatHandler::GetAccessLevel() const
  {
      return m_session->GetSecurity();
  }
  
  bool ChatHandler::isAvailable(ChatCommand const& cmd) const
  {
      // check security level only for simple  command (without child commands)
      return GetAccessLevel() >= (AccountTypes)cmd.SecurityLevel;
  }
  
  std::string ChatHandler::GetNameLink() const
  {
      return GetNameLink(m_session->GetPlayer());
  }
  
  bool ChatHandler::HasLowerSecurity(Player* target, ObjectGuid guid, bool strong)
  {
      WorldSession* target_session = NULL;
      uint32 target_account = 0;
  
      if (target)
          { target_session = target->GetSession(); }
      else
          { target_account = sObjectMgr.GetPlayerAccountIdByGUID(guid); }
  
      if (!target_session && !target_account)
      {
          SendSysMessage(LANG_PLAYER_NOT_FOUND);
          SetSentErrorMessage(true);
          return true;
      }
  
      return HasLowerSecurityAccount(target_session, target_account, strong);
  }
  
  bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_account, bool strong)
  {
      AccountTypes target_sec;
  
      // ignore only for non-players for non strong checks (when allow apply command at least to same sec level)
      if (GetAccessLevel() > SEC_PLAYER && !strong && !sWorld.getConfig(CONFIG_BOOL_GM_LOWER_SECURITY))
          { return false; }
  
      if (target)
          { target_sec = target->GetSecurity(); }
      else if (target_account)
          { target_sec = sAccountMgr.GetSecurity(target_account); }
      else
          { return true; }                                        // caller must report error for (target==NULL && target_account==0)
  
      if (GetAccessLevel() < target_sec || (strong && GetAccessLevel() <= target_sec))
      {
          SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
          SetSentErrorMessage(true);
          return true;
      }
  
      return false;
  }
  
  bool ChatHandler::hasStringAbbr(const char* name, const char* part)
  {
      // non "" command
      if (*name)
      {
          // "" part from non-"" command
          if (!*part)
              { return false; }
  
          for (;;)
          {
              if (!*part)
                  { return true; }
              else if (!*name)
                  { return false; }
              else if (tolower(*name) != tolower(*part))
                  { return false; }
              ++name; ++part;
          }
      }
      // allow with any for ""
  
      return true;
  }
  
  void ChatHandler::SendSysMessage(const char* str)
  {
      WorldPacket data;
  
      // need copy to prevent corruption by strtok call in LineFromMessage original string
      char* buf = mangos_strdup(str);
      char* pos = buf;
  
      while (char* line = LineFromMessage(pos))
      {
          // m_session == null when we're accessing these command from the console.
          ObjectGuid senderGuid;
          if (m_session)
              senderGuid = m_session->GetPlayer()->GetObjectGuid();
  
          ChatHandler::BuildChatPacket(data, CHAT_MSG_SYSTEM, line, LANG_UNIVERSAL, CHAT_TAG_NONE, senderGuid);
          m_session->SendPacket(&data);
      }
  
      delete[] buf;
  }
  
  void ChatHandler::SendGlobalSysMessage(const char* str)
  {
      // Chat output
      WorldPacket data;
  
      // need copy to prevent corruption by strtok call in LineFromMessage original string
      char* buf = mangos_strdup(str);
      char* pos = buf;
      ObjectGuid senderGuid = m_session ? m_session->GetPlayer()->GetObjectGuid() : ObjectGuid();
  
      while (char* line = LineFromMessage(pos))
      {
          // m_session == null when we're accessing these command from the console.
          ObjectGuid senderGuid;
          if (m_session)
              senderGuid = m_session->GetPlayer()->GetObjectGuid();
          
          ChatHandler::BuildChatPacket(data, CHAT_MSG_SYSTEM, line, LANG_UNIVERSAL, CHAT_TAG_NONE, senderGuid);
          sWorld.SendGlobalMessage(&data);
      }
  
      delete[] buf;
  }
  
  void ChatHandler::SendSysMessage(int32 entry)
  {
      SendSysMessage(GetMangosString(entry));
  }
  
  void ChatHandler::PSendSysMessage(int32 entry, ...)
  {
      const char* format = GetMangosString(entry);
      va_list ap;
      char str [2048];
      va_start(ap, entry);
      vsnprintf(str, 2048, format, ap);
      va_end(ap);
      SendSysMessage(str);
  }
  
  void ChatHandler::PSendSysMessage(const char* format, ...)
  {
      va_list ap;
      char str [2048];
      va_start(ap, format);
      vsnprintf(str, 2048, format, ap);
      va_end(ap);
      SendSysMessage(str);
  }
  
  void ChatHandler::CheckIntegrity(ChatCommand* table, ChatCommand* parentCommand)
  {
      for (uint32 i = 0; table[i].Name != NULL; ++i)
      {
          ChatCommand* command = &table[i];
  
          if (parentCommand && command->SecurityLevel < parentCommand->SecurityLevel)
              sLog.outError("Subcommand '%s' of command '%s' have less access level (%u) that parent (%u)",
                            command->Name, parentCommand->Name, command->SecurityLevel, parentCommand->SecurityLevel);
  
          if (!parentCommand && strlen(command->Name) == 0)
              { sLog.outError("Subcommand '' at top level"); }
  
          if (command->ChildCommands)
          {
              if (command->Handler)
              {
                  if (parentCommand)
                      sLog.outError("Subcommand '%s' of command '%s' have handler and subcommands in same time, must be used '' subcommand for handler instead.",
                                    command->Name, parentCommand->Name);
                  else
                      sLog.outError("First level command '%s' have handler and subcommands in same time, must be used '' subcommand for handler instead.",
                                    command->Name);
              }
  
              if (parentCommand && strlen(command->Name) == 0)
                  { sLog.outError("Subcommand '' of command '%s' have subcommands", parentCommand->Name); }
  
              CheckIntegrity(command->ChildCommands, command);
          }
          else if (!command->Handler)
          {
              if (parentCommand)
                  sLog.outError("Subcommand '%s' of command '%s' not have handler and subcommands in same time. Must have some from its!",
                                command->Name, parentCommand->Name);
              else
                  sLog.outError("First level command '%s' not have handler and subcommands in same time. Must have some from its!",
                                command->Name);
          }
      }
  }
  
  /**
   * Search (sub)command for command line available for chat handler access level
   *
   * @param text  Command line string that will parsed for (sub)command search
   *
   * @return Pointer to found command structure or NULL if appropriate command not found
   */
  ChatCommand const* ChatHandler::FindCommand(char const* text)
  {
      ChatCommand* command = NULL;
      char const* textPtr = text;
  
      return FindCommand(getCommandTable(), textPtr, command) == CHAT_COMMAND_OK ? command : NULL;
  }
  
  /**
   * Search (sub)command for command line available for chat handler access level with options and fail case additional info
   *
   * @param table         Pointer to command C-style array first level command where will be searched
   * @param text          Command line string that will parsed for (sub)command search,
   *                      it modified at return from function and pointed to not parsed tail
   * @param command       At success this is found command, at other cases this is last found parent command
   *                      before subcommand search fail
   * @param parentCommand Output arg for optional return parent command for command arg.
   * @param cmdNamePtr    Output arg for optional return last parsed command name.
   * @param allAvailable  Optional arg (with false default value) control use command access level checks while command search.
   * @param exactlyName   Optional arg (with false default value) control use exactly name in checks while command search.
   *
   * @return one from enum value of ChatCommandSearchResult. Output args return values highly dependent from this return result:
   *
   *      CHAT_COMMAND_OK       - Command found!
   *                              text point to non parsed tail with possible command specific data, command store found command pointer,
   *                              parentCommand have parent of found command or NULL if command found in table array directly
   *                              cmdNamePtr store found command name in original form from command line
   *      CHAT_COMMAND_UNKNOWN  - Command not found in table directly
   *                              text only skip possible whitespaces,
   *                              command is NULL
   *                              parentCommand is NULL
   *                              cmdNamePtr store command name that not found as it extracted from command line
   *      CHAT_COMMAND_UNKNOWN_SUBCOMMAND - Subcommand not found in some deed subcomand lists
   *                              text point to non parsed tail including not found command name in command line,
   *                              command store last found parent command if any
   *                              parentCommand have parent of command in command arg or NULL
   *                              cmdNamePtr store command name that not found as it extracted from command line
   */
  ChatCommandSearchResult ChatHandler::FindCommand(ChatCommand* table, char const*& text, ChatCommand*& command, ChatCommand** parentCommand /*= NULL*/, std::string* cmdNamePtr /*= NULL*/, bool allAvailable /*= false*/, bool exactlyName /*= false*/)
  {
      std::string cmd = "";
  
      // skip whitespaces
      while (*text != ' ' && *text != '\0')
      {
          cmd += *text;
          ++text;
      }
  
      while (*text == ' ') { ++text; }
  
      // search first level command in table
      for (uint32 i = 0; table[i].Name != NULL; ++i)
      {
          if (exactlyName)
          {
              size_t len = strlen(table[i].Name);
              if (strncmp(table[i].Name, cmd.c_str(), len + 1) != 0)
                  { continue; }
          }
          else
          {
              if (!hasStringAbbr(table[i].Name, cmd.c_str()))
                  { continue; }
          }
          // select subcommand from child commands list
          if (table[i].ChildCommands != NULL)
          {
              char const* oldchildtext = text;
              ChatCommand* parentSubcommand = NULL;
              ChatCommandSearchResult res = FindCommand(table[i].ChildCommands, text, command, &parentSubcommand, cmdNamePtr, allAvailable, exactlyName);
  
              switch (res)
              {
                  case CHAT_COMMAND_OK:
                  {
                      // if subcommand success search not return parent command, then this parent command is owner of child commands
                      if (parentCommand)
                          { *parentCommand = parentSubcommand ? parentSubcommand : &table[i]; }
  
                      // Name == "" is special case: restore original command text for next level "" (where parentSubcommand==NULL)
                      if (strlen(command->Name) == 0 && !parentSubcommand)
                          { text = oldchildtext; }
  
                      return CHAT_COMMAND_OK;
                  }
                  case CHAT_COMMAND_UNKNOWN:
                  {
                      // command not found directly in child command list, return child command list owner
                      command = &table[i];
                      if (parentCommand)
                          { *parentCommand = NULL; }              // we don't known parent of table list at this point
  
                      text = oldchildtext;                    // restore text to stated just after parse found parent command
                      return CHAT_COMMAND_UNKNOWN_SUBCOMMAND; // we not found subcommand for table[i]
                  }
                  case CHAT_COMMAND_UNKNOWN_SUBCOMMAND:
                  default:
                  {
                      // some deep subcommand not found, if this second level subcommand then parentCommand can be NULL, use known value for it
                      if (parentCommand)
                          { *parentCommand = parentSubcommand ? parentSubcommand : &table[i]; }
                      return res;
                  }
              }
          }
  
          // must be available (not checked for subcommands case because parent command expected have most low access that all subcommands always
          if (!allAvailable && !isAvailable(table[i]))
              { continue; }
  
          // must be have handler is explicitly selected
          if (!table[i].Handler)
              { continue; }
  
          // command found directly in to table
          command = &table[i];
  
          // unknown table owner at this point
          if (parentCommand)
              { *parentCommand = NULL; }
  
          if (cmdNamePtr)
              { *cmdNamePtr = cmd; }
  
          return CHAT_COMMAND_OK;
      }
  
      // command not found in table directly
      command = NULL;
  
      // unknown table owner at this point
      if (parentCommand)
          { *parentCommand = NULL; }
  
      if (cmdNamePtr)
          { *cmdNamePtr = cmd; }
  
      return CHAT_COMMAND_UNKNOWN;
  }
  
  /**
   * Execute (sub)command available for chat handler access level with options in command line string
   *
   * @param text  Command line string that will parsed for (sub)command search and command specific data
   *
   * Command output and errors in command execution will send to chat handler.
   */
  void ChatHandler::ExecuteCommand(const char* text)
  {
      std::string fullcmd = text;                             // original `text` can't be used. It content destroyed in command code processing.
  
      ChatCommand* command = NULL;
      ChatCommand* parentCommand = NULL;
  
      ChatCommandSearchResult res = FindCommand(getCommandTable(), text, command, &parentCommand);
  
      switch (res)
      {
          case CHAT_COMMAND_OK:
          {
              SetSentErrorMessage(false);
              if ((this->*(command->Handler))((char*)text))   // text content destroyed at call
              {
                  if (command->SecurityLevel > SEC_PLAYER)
                      { LogCommand(fullcmd.c_str()); }
              }
              // some commands have custom error messages. Don't send the default one in these cases.
              else if (!HasSentErrorMessage())
              {
                  if (!command->Help.empty())
                      { SendSysMessage(command->Help.c_str()); }
                  else
                      { SendSysMessage(LANG_CMD_SYNTAX); }
  
                  if (ChatCommand* showCommand = (strlen(command->Name) == 0 && parentCommand ? parentCommand : command))
                      if (ChatCommand* childs = showCommand->ChildCommands)
                          { ShowHelpForSubCommands(childs, showCommand->Name); }
  
                  SetSentErrorMessage(true);
              }
              break;
          }
          case CHAT_COMMAND_UNKNOWN_SUBCOMMAND:
          {
  #ifdef ENABLE_ELUNA
              if (!sEluna->OnCommand(m_session ? m_session->GetPlayer() : NULL, fullcmd.c_str()))
                  return;
  #endif /* ENABLE_ELUNA */
              SendSysMessage(LANG_NO_SUBCMD);
              ShowHelpForCommand(command->ChildCommands, text);
              SetSentErrorMessage(true);
              break;
          }
          case CHAT_COMMAND_UNKNOWN:
          {
  #ifdef ENABLE_ELUNA
              if (!sEluna->OnCommand(m_session ? m_session->GetPlayer() : NULL, fullcmd.c_str()))
                  return;
  #endif /* ENABLE_ELUNA */
              SendSysMessage(LANG_NO_CMD);
              SetSentErrorMessage(true);
              break;
          }
      }
  }
  
  /**
   * Function find appropriate command and update command security level and help text
   *
   * @param commandTable  Table for first level command search
   * @param text          Command line string that will parsed for (sub)command search
   * @param security      New security level for command
   * @param help          New help text  for command
   *
   * @return true if command has been found, and false in other case
   *
   * All problems found while command search and updated output as to DB errors log
   */
  bool ChatHandler::SetDataForCommandInTable(ChatCommand* commandTable, const char* text, uint32 security, std::string const& help)
  {
      std::string fullcommand = text;                         // original `text` can't be used. It content destroyed in command code processing.
  
      ChatCommand* command = NULL;
      std::string cmdName;
  
      ChatCommandSearchResult res = FindCommand(commandTable, text, command, NULL, &cmdName, true, true);
  
      switch (res)
      {
          case CHAT_COMMAND_OK:
          {
              if (command->SecurityLevel != security)
                  DETAIL_LOG("Table `command` overwrite for command '%s' default security (%u) by %u",
                             fullcommand.c_str(), command->SecurityLevel, security);
  
              command->SecurityLevel = security;
              command->Help          = help;
              return true;
          }
          case CHAT_COMMAND_UNKNOWN_SUBCOMMAND:
          {
              // command have subcommands, but not '' subcommand and then any data in `command` useless for it.
              if (cmdName.empty())
                  { sLog.outErrorDb("Table `command` have command '%s' that only used with some subcommand selection, it can't have help or overwritten access level, skip.", cmdName.c_str()); }
              else
                  { sLog.outErrorDb("Table `command` have unexpected subcommand '%s' in command '%s', skip.", cmdName.c_str(), fullcommand.c_str()); }
              return false;
          }
          case CHAT_COMMAND_UNKNOWN:
          {
              sLog.outErrorDb("Table `command` have nonexistent command '%s', skip.", cmdName.c_str());
              return false;
          }
      }
  
      return false;
  }
  
  bool ChatHandler::ParseCommands(const char* text)
  {
      MANGOS_ASSERT(text);
      MANGOS_ASSERT(*text);
  
      /// chat case (.command or !command format)
      if (m_session)
      {
          if (m_session->GetSecurity() == SEC_PLAYER && !sWorld.getConfig(CONFIG_BOOL_PLAYER_COMMANDS))
              { return false; }
  
          if (text[0] != '!' && text[0] != '.')
              { return false; }
  
          /// ignore single . and ! in line
          if (strlen(text) < 2)
              { return false; }
      }
  
      /// ignore messages staring from many dots.
      if ((text[0] == '.' && text[1] == '.') || (text[0] == '!' && text[1] == '!'))
          { return false; }
  
      /// skip first . or ! (in console allowed use command with . and ! and without its)
      if (text[0] == '!' || text[0] == '.')
          { ++text; }
  
      ExecuteCommand(text);
  
      return true;
  }
  
  bool ChatHandler::ShowHelpForSubCommands(ChatCommand* table, char const* cmd)
  {
      std::string list;
      for (uint32 i = 0; table[i].Name != NULL; ++i)
      {
          // must be available (ignore handler existence for show command with possible available subcommands
          if (!isAvailable(table[i]))
              { continue; }
  
          if (m_session)
              { list += "\n    "; }
          else
              { list += "\n\r    "; }
  
          list += table[i].Name;
  
          if (table[i].ChildCommands)
              { list += " ..."; }
      }
  
      if (list.empty())
          { return false; }
  
      if (table == getCommandTable())
      {
          SendSysMessage(LANG_AVIABLE_CMD);
          SendSysMessage(list.c_str());
      }
      else
      {
          PSendSysMessage(LANG_SUBCMDS_LIST, cmd);
          SendSysMessage(list.c_str());
      }
      return true;
  }
  
  bool ChatHandler::ShowHelpForCommand(ChatCommand* table, const char* cmd)
  {
      char const* oldCmd = cmd;
      ChatCommand* command = NULL;
      ChatCommand* parentCommand = NULL;
  
      ChatCommand* showCommand = NULL;
      ChatCommand* childCommands = NULL;
  
      ChatCommandSearchResult res = FindCommand(table, cmd, command, &parentCommand);
  
      switch (res)
      {
          case CHAT_COMMAND_OK:
          {
              // for "" subcommand use parent command if any for subcommands list output
              if (strlen(command->Name) == 0 && parentCommand)
              {
                  showCommand = parentCommand;
                  cmd = "";
              }
              else
                  { showCommand = command; }
  
              childCommands = showCommand->ChildCommands;
              break;
          }
          case CHAT_COMMAND_UNKNOWN_SUBCOMMAND:
              showCommand = command;
              childCommands = showCommand->ChildCommands;
              break;
          case CHAT_COMMAND_UNKNOWN:
              // not show command list at error in first level command find fail
              childCommands = table != getCommandTable() || strlen(oldCmd) == 0 ? table : NULL;
              command = NULL;
              break;
      }
  
      if (command && !command->Help.empty())
          { SendSysMessage(command->Help.c_str()); }
  
      if (childCommands)
          if (ShowHelpForSubCommands(childCommands, showCommand ? showCommand->Name : ""))
              { return true; }
  
      if (command && command->Help.empty())
          { SendSysMessage(LANG_NO_HELP_CMD); }
  
      return command || childCommands;
  }
  
  bool ChatHandler::isValidChatMessage(const char* message)
  {
      /*
  
      valid examples:
      |cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r
      |cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r
      |cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r
      |cff71d5ff|Hspell:21563|h[Command]|h|r
      |cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r
  
      | will be escaped to ||
      */
  
      if (strlen(message) > 255)
          { return false; }
  
      const char validSequence[6] = "cHhhr";
      const char* validSequenceIterator = validSequence;
  
      // more simple checks
      if (sWorld.getConfig(CONFIG_UINT32_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3)
      {
          const std::string validCommands = "cHhr|";
  
          while (*message)
          {
              // find next pipe command
              message = strchr(message, '|');
  
              if (!message)
                  { return true; }
  
              ++message;
              char commandChar = *message;
              if (validCommands.find(commandChar) == std::string::npos)
                  { return false; }
  
              ++message;
              // validate sequence
              if (sWorld.getConfig(CONFIG_UINT32_CHAT_STRICT_LINK_CHECKING_SEVERITY) == 2)
              {
                  if (commandChar == *validSequenceIterator)
                  {
                      if (validSequenceIterator == validSequence + 4)
                          { validSequenceIterator = validSequence; }
                      else
                          { ++validSequenceIterator; }
                  }
                  else if (commandChar != '|')
                      { return false; }
              }
          }
          return true;
      }
  
      std::istringstream reader(message);
      char buffer[256];
  
      uint32 color = 0;
  
      ItemPrototype const* linkedItem = NULL;
      Quest const* linkedQuest = NULL;
      SpellEntry const* linkedSpell = NULL;
      ItemRandomPropertiesEntry const* itemProperty = NULL;
      ItemRandomSuffixEntry const* itemSuffix = NULL;
  
      while (!reader.eof())
      {
          if (validSequence == validSequenceIterator)
          {
              linkedItem = NULL;
              linkedQuest = NULL;
              linkedSpell = NULL;
              itemProperty = NULL;
              itemSuffix = NULL;
  
              reader.ignore(255, '|');
          }
          else if (reader.get() != '|')
          {
              DEBUG_LOG("ChatHandler::isValidChatMessage sequence aborted unexpectedly");
              return false;
          }
  
          // pipe has always to be followed by at least one char
          if (reader.peek() == '\0')
          {
              DEBUG_LOG("ChatHandler::isValidChatMessage pipe followed by \\0");
              return false;
          }
  
          // no further pipe commands
          if (reader.eof())
              { break; }
  
          char commandChar;
          reader >> commandChar;
  
          // | in normal messages is escaped by ||
          if (commandChar != '|')
          {
              if (commandChar == *validSequenceIterator)
              {
                  if (validSequenceIterator == validSequence + 4)
                      { validSequenceIterator = validSequence; }
                  else
                      { ++validSequenceIterator; }
              }
              else
              {
                  DEBUG_LOG("ChatHandler::isValidChatMessage invalid sequence, expected %c but got %c", *validSequenceIterator, commandChar);
                  return false;
              }
          }
          else if (validSequence != validSequenceIterator)
          {
              // no escaped pipes in sequences
              DEBUG_LOG("ChatHandler::isValidChatMessage got escaped pipe in sequence");
              return false;
          }
  
          switch (commandChar)
          {
              case 'c':
                  color = 0;
                  // validate color, expect 8 hex chars
                  for (int i = 0; i < 8; ++i)
                  {
                      char c;
                      reader >> c;
                      if (!c)
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage got \\0 while reading color in |c command");
                          return false;
                      }
  
                      color <<= 4;
                      // check for hex char
                      if (c >= '0' && c <= '9')
                      {
                          color |= c - '0';
                          continue;
                      }
                      if (c >= 'a' && c <= 'f')
                      {
                          color |= 10 + c - 'a';
                          continue;
                      }
                      DEBUG_LOG("ChatHandler::isValidChatMessage got non hex char '%c' while reading color", c);
                      return false;
                  }
                  break;
              case 'H':
                  // read chars up to colon  = link type
                  reader.getline(buffer, 256, ':');
                  if (reader.eof())                           // : must be
                      { return false; }
  
                  if (strcmp(buffer, "item") == 0)
                  {
                      // read item entry
                      reader.getline(buffer, 256, ':');
                      if (reader.eof())                       // : must be
                          { return false; }
  
                      linkedItem = ObjectMgr::GetItemPrototype(atoi(buffer));
                      if (!linkedItem)
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage got invalid itemID %u in |item command", atoi(buffer));
                          return false;
                      }
  
                      if (color != ItemQualityColors[linkedItem->Quality])
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage linked item has color %u, but user claims %u", ItemQualityColors[linkedItem->Quality],
                                    color);
                          return false;
                      }
  
                      // the itementry is followed by several integers which describe an instance of this item
  
                      // position relative after itemEntry
                      const uint8 randomPropertyPosition = 6;
  
                      int32 propertyId = 0;
                      bool negativeNumber = false;
                      char c;
                      for (uint8 i = 0; i < randomPropertyPosition; ++i)
                      {
                          propertyId = 0;
                          negativeNumber = false;
                          while ((c = reader.get()) != ':')
                          {
                              if (c >= '0' && c <= '9')
                              {
                                  propertyId *= 10;
                                  propertyId += c - '0';
                              }
                              else if (c == '-')
                                  { negativeNumber = true; }
                              else
                                  { return false; }
                          }
                      }
                      if (negativeNumber)
                          { propertyId *= -1; }
  
                      if (propertyId > 0)
                      {
                          itemProperty = sItemRandomPropertiesStore.LookupEntry(propertyId);
                          if (!itemProperty)
                              { return false; }
                      }
                      else if (propertyId < 0)
                      {
                          itemSuffix = sItemRandomSuffixStore.LookupEntry(-propertyId);
                          if (!itemSuffix)
                              return false;
                      }
  
                      // ignore other integers
                      while ((c >= '0' && c <= '9') || c == ':')
                      {
                          reader.ignore(1);
                          c = reader.peek();
                      }
                  }
                  else if (strcmp(buffer, "quest") == 0)
                  {
                      // no color check for questlinks, each client will adapt it anyway
                      uint32 questid = 0;
                      // read questid
                      char c = reader.peek();
                      while (c >= '0' && c <= '9')
                      {
                          reader.ignore(1);
                          questid *= 10;
                          questid += c - '0';
                          c = reader.peek();
                      }
  
                      linkedQuest = sObjectMgr.GetQuestTemplate(questid);
  
                      if (!linkedQuest)
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage Questtemplate %u not found", questid);
                          return false;
                      }
  
                      if (c != ':')
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage Invalid quest link structure");
                          return false;
                      }
  
                      reader.ignore(1);
                      c = reader.peek();
                      // level
                      uint32 questlevel = 0;
                      while (c >= '0' && c <= '9')
                      {
                          reader.ignore(1);
                          questlevel *= 10;
                          questlevel += c - '0';
                          c = reader.peek();
                      }
  
                      if (questlevel >= STRONG_MAX_LEVEL)
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage Quest level %u too big", questlevel);
                          return false;
                      }
  
                      if (c != '|')
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage Invalid quest link structure");
                          return false;
                      }
                  }
                  else if (strcmp(buffer, "talent") == 0)
                  {
                      // talent links are always supposed to be blue
                      if (color != CHAT_LINK_COLOR_TALENT)
                          { return false; }
  
                      // read talent entry
                      reader.getline(buffer, 256, ':');
                      if (reader.eof())                       // : must be
                          { return false; }
  
                      TalentEntry const* talentInfo = sTalentStore.LookupEntry(atoi(buffer));
                      if (!talentInfo)
                          { return false; }
  
                      linkedSpell = sSpellStore.LookupEntry(talentInfo->RankID[0]);
                      if (!linkedSpell)
                          { return false; }
  
                      char c = reader.peek();
                      // skillpoints? whatever, drop it
                      while (c != '|' && c != '\0')
                      {
                          reader.ignore(1);
                          c = reader.peek();
                      }
                  }
                  else if (strcmp(buffer, "spell") == 0)
                  {
                      if (color != CHAT_LINK_COLOR_SPELL)
                          { return false; }
  
                      uint32 spellid = 0;
                      // read spell entry
                      char c = reader.peek();
                      while (c >= '0' && c <= '9')
                      {
                          reader.ignore(1);
                          spellid *= 10;
                          spellid += c - '0';
                          c = reader.peek();
                      }
                      linkedSpell = sSpellStore.LookupEntry(spellid);
                      if (!linkedSpell)
                          { return false; }
                  }
                  else if (strcmp(buffer, "enchant") == 0)
                  {
                      if (color != CHAT_LINK_COLOR_ENCHANT)
                          { return false; }
  
                      uint32 spellid = 0;
                      // read spell entry
                      char c = reader.peek();
                      while (c >= '0' && c <= '9')
                      {
                          reader.ignore(1);
                          spellid *= 10;
                          spellid += c - '0';
                          c = reader.peek();
                      }
                      linkedSpell = sSpellStore.LookupEntry(spellid);
                      if (!linkedSpell)
                          { return false; }
                  }
                  else
                  {
                      DEBUG_LOG("ChatHandler::isValidChatMessage user sent unsupported link type '%s'", buffer);
                      return false;
                  }
                  break;
              case 'h':
                  // if h is next element in sequence, this one must contain the linked text :)
                  if (*validSequenceIterator == 'h')
                  {
                      // links start with '['
                      if (reader.get() != '[')
                      {
                          DEBUG_LOG("ChatHandler::isValidChatMessage link caption doesn't start with '['");
                          return false;
                      }
                      reader.getline(buffer, 256, ']');
                      if (reader.eof())                       // ] must be
                          { return false; }
  
                      // verify the link name
                      if (linkedSpell)
                      {
                          // spells with that flag have a prefix of "$PROFESSION: "
                          if (linkedSpell->HasAttribute(SPELL_ATTR_TRADESPELL))
                          {
                              // lookup skillid
                              SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(linkedSpell->Id);
                              if (bounds.first == bounds.second)
                              {
                                  return false;
                              }
  
                              SkillLineAbilityEntry const* skillInfo = bounds.first->second;
  
                              if (!skillInfo)
                              {
                                  return false;
                              }
  
                              SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId);
                              if (!skillLine)
                              {
                                  return false;
                              }
  
                              for (uint8 i = 0; i < MAX_LOCALE; ++i)
                              {
                                  uint32 skillLineNameLength = strlen(skillLine->name[i]);
                                  if (skillLineNameLength > 0 && strncmp(skillLine->name[i], buffer, skillLineNameLength) == 0)
                                  {
                                      // found the prefix, remove it to perform spellname validation below
                                      // -2 = strlen(": ")
                                      uint32 spellNameLength = strlen(buffer) - skillLineNameLength - 2;
                                      memmove(buffer, buffer + skillLineNameLength + 2, spellNameLength + 1);
                                  }
                              }
                          }
                          bool foundName = false;
                          for (uint8 i = 0; i < MAX_LOCALE; ++i)
                          {
                              if (*linkedSpell->SpellName[i] && strcmp(linkedSpell->SpellName[i], buffer) == 0)
                              {
                                  foundName = true;
                                  break;
                              }
                          }
                          if (!foundName)
                              { return false; }
                      }
                      else if (linkedQuest)
                      {
                          if (linkedQuest->GetTitle() != buffer)
                          {
                              QuestLocale const* ql = sObjectMgr.GetQuestLocale(linkedQuest->GetQuestId());
  
                              if (!ql)
                              {
                                  DEBUG_LOG("ChatHandler::isValidChatMessage default questname didn't match and there is no locale");
                                  return false;
                              }
  
                              bool foundName = false;
                              for (uint8 i = 0; i < ql->Title.size(); ++i)
                              {
                                  if (ql->Title[i] == buffer)
                                  {
                                      foundName = true;
                                      break;
                                  }
                              }
                              if (!foundName)
                              {
                                  DEBUG_LOG("ChatHandler::isValidChatMessage no quest locale title matched");
                                  return false;
                              }
                          }
                      }
                      else if (linkedItem)
                      {
                          char* const* suffix = itemSuffix ? itemSuffix->nameSuffix : (itemProperty ? itemProperty->nameSuffix : NULL);
  
                          std::string expectedName = std::string(linkedItem->Name1);
                          if (suffix)
                          {
                              expectedName += " ";
                              expectedName += suffix[LOCALE_enUS];
                          }
  
                          if (expectedName != buffer)
                          {
                              ItemLocale const* il = sObjectMgr.GetItemLocale(linkedItem->ItemId);
  
                              bool foundName = false;
                              for (uint8 i = LOCALE_koKR; i < MAX_LOCALE; ++i)
                              {
                                  int8 dbIndex = sObjectMgr.GetIndexForLocale(LocaleConstant(i));
                                  if (dbIndex == -1 || il == NULL || (size_t)dbIndex >= il->Name.size())
                                      // using strange database/client combinations can lead to this case
                                      { expectedName = linkedItem->Name1; }
                                  else
                                      { expectedName = il->Name[dbIndex]; }
                                  if (suffix)
                                  {
                                      expectedName += " ";
                                      expectedName += suffix[i];
                                  }
                                  if (expectedName == buffer)
                                  {
                                      foundName = true;
                                      break;
                                  }
                              }
                              if (!foundName)
                              {
                                  DEBUG_LOG("ChatHandler::isValidChatMessage linked item name wasn't found in any localization");
                                  return false;
                              }
                          }
                      }
                      // that place should never be reached - if nothing linked has been set in |H
                      // it will return false before
                      else
                          { return false; }
                  }
                  break;
              case 'r':
              case '|':
                  // no further payload
                  break;
              default:
                  DEBUG_LOG("ChatHandler::isValidChatMessage got invalid command |%c", commandChar);
                  return false;
          }
      }
  
      // check if every opened sequence was also closed properly
      if (validSequence != validSequenceIterator)
          { DEBUG_LOG("ChatHandler::isValidChatMessage EOF in active sequence"); }
  
      return validSequence == validSequenceIterator;
  }
  
  Player* ChatHandler::getSelectedPlayer()
  {
      if (!m_session)
          { return NULL; }
  
      ObjectGuid guid  = m_session->GetPlayer()->GetSelectionGuid();
  
      if (!guid)
          { return m_session->GetPlayer(); }
  
      return sObjectMgr.GetPlayer(guid);
  }
  
  Unit* ChatHandler::getSelectedUnit()
  {
      if (!m_session)
          { return NULL; }
  
      ObjectGuid guid = m_session->GetPlayer()->GetSelectionGuid();
  
      if (!guid)
          { return m_session->GetPlayer(); }
  
      // can be selected player at another map
      return ObjectAccessor::GetUnit(*m_session->GetPlayer(), guid);
  }
  
  Creature* ChatHandler::getSelectedCreature()
  {
      if (!m_session)
          { return NULL; }
  
      return m_session->GetPlayer()->GetMap()->GetAnyTypeCreature(m_session->GetPlayer()->GetSelectionGuid());
  }
  
  /**
   * Function skip all whitespaces in args string
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   *             allowed NULL string pointer stored in *args
   */
  void ChatHandler::SkipWhiteSpaces(char** args)
  {
      if (!*args)
          { return; }
  
      while (isWhiteSpace(**args))
          { ++(*args); }
  }
  
  /**
   * Function extract to val arg signed integer value or fail
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param val  return extracted value if function success, in fail case original value unmodified
   * @return     true if value extraction successful
   */
  bool  ChatHandler::ExtractInt32(char** args, int32& val)
  {
      if (!*args || !** args)
          { return false; }
  
      char* tail = *args;
  
      long valRaw = strtol(*args, &tail, 10);
  
      if (tail != *args && isWhiteSpace(*tail))
          { *(tail++) = '\0'; }
      else if (tail && *tail)                                 // some not whitespace symbol
          { return false; }                                       // args not modified and can be re-parsed
  
      if (valRaw < std::numeric_limits<int32>::min() || valRaw > std::numeric_limits<int32>::max())
          { return false; }
  
      // value successfully extracted
      val = int32(valRaw);
      *args = tail;
      return true;
  }
  
  /**
   * Function extract to val arg optional signed integer value or use default value. Fail if extracted not signed integer.
   *
   * @param args    variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param val     return extracted value if function success, in fail case original value unmodified
   * @param defVal  default value used if no data for extraction in args
   * @return        true if value extraction successful
   */
  bool  ChatHandler::ExtractOptInt32(char** args, int32& val, int32 defVal)
  {
      if (!*args || !** args)
      {
          val = defVal;
          return true;
      }
  
      return ExtractInt32(args, val);
  }
  
  /**
   * Function extract to val arg unsigned integer value or fail
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param val  return extracted value if function success, in fail case original value unmodified
   * @param base set used base for extracted value format (10 for decimal, 16 for hex, etc), 0 let auto select by system internal function
   * @return     true if value extraction successful
   */
  bool  ChatHandler::ExtractUInt32Base(char** args, uint32& val, uint32 base)
  {
      if (!*args || !** args)
          { return false; }
  
      char* tail = *args;
  
      unsigned long valRaw = strtoul(*args, &tail, base);
  
      if (tail != *args && isWhiteSpace(*tail))
          { *(tail++) = '\0'; }
      else if (tail && *tail)                                 // some not whitespace symbol
          { return false; }                                       // args not modified and can be re-parsed
  
      if (valRaw > std::numeric_limits<uint32>::max())
          { return false; }
  
      // value successfully extracted
      val = uint32(valRaw);
      *args = tail;
  
      SkipWhiteSpaces(args);
      return true;
  }
  
  /**
   * Function extract to val arg optional unsigned integer value or use default value. Fail if extracted not unsigned integer.
   *
   * @param args    variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param val     return extracted value if function success, in fail case original value unmodified
   * @param defVal  default value used if no data for extraction in args
   * @return        true if value extraction successful
   */
  bool  ChatHandler::ExtractOptUInt32(char** args, uint32& val, uint32 defVal)
  {
      if (!*args || !** args)
      {
          val = defVal;
          return true;
      }
  
      return ExtractUInt32(args, val);
  }
  
  /**
   * Function extract to val arg float value or fail
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param val  return extracted value if function success, in fail case original value unmodified
   * @return     true if value extraction successful
   */
  bool  ChatHandler::ExtractFloat(char** args, float& val)
  {
      if (!*args || !** args)
          { return false; }
  
      char* tail = *args;
  
      double valRaw = strtod(*args, &tail);
  
      if (tail != *args && isWhiteSpace(*tail))
          { *(tail++) = '\0'; }
      else if (tail && *tail)                                 // some not whitespace symbol
          { return false; }                                       // args not modified and can be re-parsed
  
      // value successfully extracted
      val = float(valRaw);
      *args = tail;
  
      SkipWhiteSpaces(args);
      return true;
  }
  
  /**
   * Function extract to val arg optional float value or use default value. Fail if extracted not float.
   *
   * @param args    variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param val     return extracted value if function success, in fail case original value unmodified
   * @param defVal  default value used if no data for extraction in args
   * @return        true if value extraction successful
   */
  bool  ChatHandler::ExtractOptFloat(char** args, float& val, float defVal)
  {
      if (!*args || !** args)
      {
          val = defVal;
          return true;
      }
  
      return ExtractFloat(args, val);
  }
  
  /**
   * Function extract name-like string (from non-numeric or special symbol until whitespace)
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param lit  optional explicit literal requirement. function fail if literal is not starting substring of lit.
   *             Note: function in same way fail if no any literal or literal not fit in this case. Need additional check for select specific fail case
   * @return     name/number-like string without whitespaces, or NULL if args empty or not appropriate content.
   */
  char* ChatHandler::ExtractLiteralArg(char** args, char const* lit /*= NULL*/)
  {
      if (!*args || !** args)
          { return NULL; }
  
      char* head = *args;
  
      // reject quoted string or link (|-started text)
      switch (head[0])
      {
              // reject quoted string
          case '[': case '\'': case '"':
              return NULL;
              // reject link (|-started text)
          case '|':
              // client replace all | by || in raw text
              if (head[1] != '|')
                  { return NULL; }
              ++head;                                         // skip one |
              break;
          default: break;
      }
  
      if (lit)
      {
          int l = strlen(lit);
  
          int largs = 0;
          while (head[largs] && !isWhiteSpace(head[largs]))
              { ++largs; }
  
          if (largs < l)
              { l = largs; }
  
          int diff = strncmp(head, lit, l);
  
          if (diff != 0)
              { return NULL; }
  
          if (head[l] && !isWhiteSpace(head[l]))
              { return NULL; }
  
          char* arg = head;
  
          if (head[l])
          {
              head[l] = '\0';
  
              head += l + 1;
  
              *args = head;
          }
          else
              { *args = head + l; }
  
          SkipWhiteSpaces(args);
          return arg;
      }
  
      char* name = strtok(head, " ");
  
      char* tail = strtok(NULL, "");
  
      *args = tail ? tail : (char*)"";                        // *args don't must be NULL
  
      SkipWhiteSpaces(args);
  
      return name;
  }
  
  /**
   * Function extract quote-like string (any characters guarded by some special character, in our cases ['")
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param asis control save quote string wrappers
   * @return     quote-like string, or NULL if args empty or not appropriate content.
   */
  char* ChatHandler::ExtractQuotedArg(char** args, bool asis /*= false*/)
  {
      if (!*args || !** args)
          { return NULL; }
  
      if (**args != '\'' &&**  args != '"' &&**  args != '[')
          { return NULL; }
  
      char guard = (*args)[0];
  
      if (guard == '[')
          { guard = ']'; }
  
      char* tail = (*args) + 1;                               // start scan after first quote symbol
      char* head = asis ? *args : tail;                       // start arg
  
      while (*tail && *tail != guard)
          { ++tail; }
  
      if (!*tail || (tail[1] && !isWhiteSpace(tail[1])))      // fail
          { return NULL; }
  
      if (!tail[1])                                           // quote is last char in string
      {
          if (!asis)
              { *tail = '\0'; }
      }
      else                                                    // quote isn't last char
      {
          if (asis)
              { ++tail; }
  
          *tail = '\0';
      }
  
      *args = tail + 1;
  
      SkipWhiteSpaces(args);
  
      return head;
  }
  
  /**
   * Function extract quote-like string or literal if quote not detected
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param asis control save quote string wrappers
   * @return     quote/literal string, or NULL if args empty or not appropriate content.
   */
  char* ChatHandler::ExtractQuotedOrLiteralArg(char** args, bool asis /*= false*/)
  {
      char* arg = ExtractQuotedArg(args, asis);
      if (!arg)
          { arg = ExtractLiteralArg(args); }
      return arg;
  }
  
  /**
   * Function extract on/off literals as boolean values
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param val  return extracted value if function success, in fail case original value unmodified
   * @return     true at success
   */
  bool  ChatHandler::ExtractOnOff(char** args, bool& value)
  {
      char* arg = ExtractLiteralArg(args);
      if (!arg)
          { return false; }
  
      if (strncmp(arg, "on", 3) == 0)
          { value = true; }
      else if (strncmp(arg, "off", 4) == 0)
          { value = false; }
      else
          { return false; }
  
      return true;
  }
  
  /**
   * Function extract shift-link-like string (any characters guarded by | and |h|r with some additional internal structure check)
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   *
   * @param linkTypes  optional NULL-terminated array of link types, shift-link must fit one from link type from array if provided or extraction fail
   *
   * @param found_idx  if not NULL then at return index in linkTypes that fit shift-link type, if extraction fail then non modified
   *
   * @param keyPair    if not NULL then pointer to 2-elements array for return start and end pointer for found key
   *                   if extraction fail then non modified
   *
   * @param somethingPair then pointer to 2-elements array for return start and end pointer if found.
   *                   if not NULL then shift-link must have data field, if extraction fail then non modified
   *
   * @return     shift-link-like string, or NULL if args empty or not appropriate content.
   */
  char* ChatHandler::ExtractLinkArg(char** args, char const* const* linkTypes /*= NULL*/, int* foundIdx /*= NULL*/, char** keyPair /*= NULL*/, char** somethingPair /*= NULL*/)
  {
      if (!*args || !** args)
          { return NULL; }
  
      // skip if not linked started or encoded single | (doubled by client)
      if ((*args)[0] != '|' || (*args)[1] == '|')
          { return NULL; }
  
      // |color|Hlinktype:key:data...|h[name]|h|r
  
      char* head = *args;
  
      // [name] Shift-click form |color|linkType:key|h[name]|h|r
      // or
      // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r
      // or
      // [name] Shift-click form |linkType:key|h[name]|h|r
  
      // |color|Hlinktype:key:data...|h[name]|h|r
  
      char* tail = (*args) + 1;                               // skip |
  
      if (*tail != 'H')                                       // skip color part, some links can not have color part
      {
          while (*tail && *tail != '|')
              { ++tail; }
  
          if (!*tail)
              { return NULL; }
  
          // |Hlinktype:key:data...|h[name]|h|r
  
          ++tail;                                             // skip |
      }
  
      // Hlinktype:key:data...|h[name]|h|r
  
      if (*tail != 'H')
          { return NULL; }
  
      int linktype_idx = 0;
  
      if (linkTypes)                                          // check link type if provided
      {
          // check linktypes (its include H in name)
          for (; linkTypes[linktype_idx]; ++linktype_idx)
          {
              // exactly string with follow : or |
              int l = strlen(linkTypes[linktype_idx]);
              if (strncmp(tail, linkTypes[linktype_idx], l) == 0 &&
                  (tail[l] == ':' || tail[l] == '|'))
                  { break; }
          }
  
          // is search fail?
          if (!linkTypes[linktype_idx])                       // NULL terminator in last element
              { return NULL; }
  
          tail += strlen(linkTypes[linktype_idx]);            // skip linktype string
  
          // :key:data...|h[name]|h|r
  
          if (*tail != ':')
              { return NULL; }
      }
      else
      {
          while (*tail && *tail != ':')                       // skip linktype string
              { ++tail; }
  
          if (!*tail)
              { return NULL; }
      }
  
      ++tail;
  
      // key:data...|h[name]|h|r
      char* keyStart = tail;                                  // remember key start for return
      char* keyEnd   = tail;                                  // key end for truncate, will updated
  
      while (*tail && *tail != '|' && *tail != ':')
          { ++tail; }
  
      if (!*tail)
          { return NULL; }
  
      keyEnd = tail;                                          // remember key end for truncate
  
      // |h[name]|h|r or :something...|h[name]|h|r
  
      char* somethingStart = tail + 1;
      char* somethingEnd   = tail + 1;                        // will updated later if need
  
      if (*tail == ':' && somethingPair)                      // optional data extraction
      {
          // :something...|h[name]|h|r
          ++tail;
  
          // something|h[name]|h|r or something:something2...|h[name]|h|r
  
          while (*tail && *tail != '|' && *tail != ':')
              { ++tail; }
  
          if (!*tail)
              { return NULL; }
  
          somethingEnd = tail;                                // remember data end for truncate
      }
  
      // |h[name]|h|r or :something2...|h[name]|h|r
  
      while (*tail && (*tail != '|' || *(tail + 1) != 'h'))   // skip ... part if exist
          { ++tail; }
  
      if (!*tail)
          { return NULL; }
  
      // |h[name]|h|r
  
      tail += 2;                                              // skip |h
  
      // [name]|h|r
      if (!*tail || *tail != '[')
          { return NULL; }
  
      while (*tail && (*tail != ']' || *(tail + 1) != '|'))   // skip name part
          { ++tail; }
  
      tail += 2;                                              // skip ]|
  
      // h|r
      if (!*tail || *tail != 'h'  || *(tail + 1) != '|')
          { return NULL; }
  
      tail += 2;                                              // skip h|
  
      // r
      if (!*tail || *tail != 'r' || (*(tail + 1) && !isWhiteSpace(*(tail + 1))))
          { return NULL; }
  
      ++tail;                                                 // skip r
  
      // success
  
      if (*tail)                                              // truncate all link string
          { *(tail++) = '\0'; }
  
      if (foundIdx)
          { *foundIdx = linktype_idx; }
  
      if (keyPair)
      {
          keyPair[0] = keyStart;
          keyPair[1] = keyEnd;
      }
  
      if (somethingPair)
      {
          somethingPair[0] = somethingStart;
          somethingPair[1] = somethingEnd;
      }
  
      *args = tail;
  
      SkipWhiteSpaces(args);
  
      return head;
  }
  
  /**
   * Function extract name/number/quote/shift-link-like string
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   * @param asis control save quote string wrappers
   * @return     extracted arg string, or NULL if args empty or not appropriate content.
   */
  char* ChatHandler::ExtractArg(char** args, bool asis /*= false*/)
  {
      if (!*args || !** args)
          { return NULL; }
  
      char* arg = ExtractQuotedOrLiteralArg(args, asis);
      if (!arg)
          { arg = ExtractLinkArg(args); }
  
      return arg;
  }
  
  /**
   * Function extract name/quote/number/shift-link-like string, and return it if args have more non-whitespace data
   *
   * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   *             if args have only single arg then args still pointing to this arg (unmodified pointer)
   * @return     extracted string, or NULL if args empty or not appropriate content or have single arg totally.
   */
  char* ChatHandler::ExtractOptNotLastArg(char** args)
  {
      char* arg = ExtractArg(args, true);
  
      // have more data
      if (*args &&**  args)
          { return arg; }
  
      // optional name not found
      *args = arg ? arg : (char*)"";                          // *args don't must be NULL
  
      return NULL;
  }
  
  /**
   * Function extract data from shift-link "|color|LINKTYPE:RETURN:SOMETHING1|h[name]|h|r if linkType == LINKTYPE
   * It also extract literal/quote if not shift-link in args
   *
   * @param args       variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   *                   if args have sift link with linkType != LINKTYPE then args still pointing to this arg (unmodified pointer)
   *
   * @param linkType   shift-link must fit by link type to this arg value or extraction fail
   *
   * @param something1 if not NULL then shift-link must have data field and it returned into this arg
   *                   if extraction fail then non modified
   *
   * @return           extracted key, or NULL if args empty or not appropriate content or not fit to linkType.
   */
  char* ChatHandler::ExtractKeyFromLink(char** text, char const* linkType, char** something1 /*= NULL*/)
  {
      char const* linkTypes[2];
      linkTypes[0] = linkType;
      linkTypes[1] = NULL;
  
      int foundIdx;
  
      return ExtractKeyFromLink(text, linkTypes, &foundIdx, something1);
  }
  
  /**
   * Function extract data from shift-link "|color|LINKTYPE:RETURN:SOMETHING1|h[name]|h|r if LINKTYPE in linkTypes array
   * It also extract literal/quote if not shift-link in args
   *
   * @param args       variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   *                   if args have sift link with linkType != LINKTYPE then args still pointing to this arg (unmodified pointer)
   *
   * @param linkTypes  NULL-terminated array of link types, shift-link must fit one from link type from array or extraction fail
   *
   * @param found_idx  if not NULL then at return index in linkTypes that fit shift-link type, for non-link case return -1
   *                   if extraction fail then non modified
   *
   * @param something1 if not NULL then shift-link must have data field and it returned into this arg
   *                   if extraction fail then non modified
   *
   * @return           extracted key, or NULL if args empty or not appropriate content or not fit to linkType.
   */
  char* ChatHandler::ExtractKeyFromLink(char** text, char const* const* linkTypes, int* found_idx, char** something1 /*= NULL*/)
  {
      // skip empty
      if (!*text || !** text)
          { return NULL; }
  
      // return non link case
      char* arg = ExtractQuotedOrLiteralArg(text);
      if (arg)
      {
          if (found_idx)
              { *found_idx = -1; }                                // special index case
  
          return arg;
      }
  
      char* keyPair[2];
      char* somethingPair[2];
  
      arg = ExtractLinkArg(text, linkTypes, found_idx, keyPair, something1 ? somethingPair : NULL);
      if (!arg)
          { return NULL; }
  
      *keyPair[1] = '\0';                                     // truncate key string
  
      if (something1)
      {
          *somethingPair[1] = '\0';                           // truncate data string
          *something1 = somethingPair[0];
      }
  
      return keyPair[0];
  }
  
  /**
   * Function extract uint32 key from shift-link "|color|LINKTYPE:RETURN|h[name]|h|r if linkType == LINKTYPE
   * It also extract direct number if not shift-link in args
   *
   * @param args       variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   *                   if args have sift link with linkType != LINKTYPE then args still pointing to this arg (unmodified pointer)
   *
   * @param linkType   shift-link must fit by link type to this arg value or extraction fail
   *
   * @param value      store result value at success return, not modified at fail
   *
   * @return           true if extraction succesful
   */
  bool ChatHandler::ExtractUint32KeyFromLink(char** text, char const* linkType, uint32& value)
  {
      char* arg = ExtractKeyFromLink(text, linkType);
      if (!arg)
          { return false; }
  
      return ExtractUInt32(&arg, value);
  }
  
  GameObject* ChatHandler::GetGameObjectWithGuid(uint32 lowguid, uint32 entry)
  {
      if (!m_session)
          { return NULL; }
  
      Player* pl = m_session->GetPlayer();
  
      return pl->GetMap()->GetGameObject(ObjectGuid(HIGHGUID_GAMEOBJECT, entry, lowguid));
  }
  
  enum SpellLinkType
  {
      SPELL_LINK_RAW     = -1,                                // non-link case
      SPELL_LINK_SPELL   = 0,
      SPELL_LINK_TALENT  = 1,
      SPELL_LINK_ENCHANT = 2,
  };
  
  static char const* const spellKeys[] =
  {
      "Hspell",                                               // normal spell
      "Htalent",                                              // talent spell
      "Henchant",                                             // enchanting recipe spell
      NULL
  };
  
  uint32 ChatHandler::ExtractSpellIdFromLink(char** text)
  {
      // number or [name] Shift-click form |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r
      // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
      // number or [name] Shift-click form |color|Htalent:talent_id,rank|h[name]|h|r
      int type;
      char* param1_str = NULL;
      char* idS = ExtractKeyFromLink(text, spellKeys, &type, &param1_str);
      if (!idS)
          { return 0; }
  
      uint32 id;
      if (!ExtractUInt32(&idS, id))
          { return 0; }
  
      switch (type)
      {
          case SPELL_LINK_RAW:
          case SPELL_LINK_SPELL:
          case SPELL_LINK_ENCHANT:
              return id;
          case SPELL_LINK_TALENT:
          {
              // talent
              TalentEntry const* talentEntry = sTalentStore.LookupEntry(id);
              if (!talentEntry)
                  { return 0; }
  
              int32 rank;
              if (!ExtractInt32(&param1_str, rank))
                  { return 0; }
  
              if (rank < 0)                                   // unlearned talent have in shift-link field -1 as rank
                  { rank = 0; }
  
              return rank < MAX_TALENT_RANK ? talentEntry->RankID[rank] : 0;
          }
      }
  
      // unknown type?
      return 0;
  }
  
  GameTele const* ChatHandler::ExtractGameTeleFromLink(char** text)
  {
      // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
      char* cId = ExtractKeyFromLink(text, "Htele");
      if (!cId)
          { return NULL; }
  
      // id case (explicit or from shift link)
      uint32 id;
      if (ExtractUInt32(&cId, id))
          { return sObjectMgr.GetGameTele(id); }
      else
          { return sObjectMgr.GetGameTele(cId); }
  }
  
  enum GuidLinkType
  {
      GUID_LINK_RAW        = -1,                              // non-link case
      GUID_LINK_PLAYER     = 0,
      GUID_LINK_CREATURE   = 1,
      GUID_LINK_GAMEOBJECT = 2,
  };
  
  static char const* const guidKeys[] =
  {
      "Hplayer",
      "Hcreature",
      "Hgameobject",
      NULL
  };
  
  ObjectGuid ChatHandler::ExtractGuidFromLink(char** text)
  {
      int type = 0;
  
      // |color|Hcreature:creature_guid|h[name]|h|r
      // |color|Hgameobject:go_guid|h[name]|h|r
      // |color|Hplayer:name|h[name]|h|r
      char* idS = ExtractKeyFromLink(text, guidKeys, &type);
      if (!idS)
          { return ObjectGuid(); }
  
      switch (type)
      {
          case GUID_LINK_RAW:
          case GUID_LINK_PLAYER:
          {
              std::string name = idS;
              if (!normalizePlayerName(name))
                  { return ObjectGuid(); }
  
              if (Player* player = sObjectMgr.GetPlayer(name.c_str()))
                  { return player->GetObjectGuid(); }
  
              return sObjectMgr.GetPlayerGuidByName(name);
          }
          case GUID_LINK_CREATURE:
          {
              uint32 lowguid;
              if (!ExtractUInt32(&idS, lowguid))
                  { return ObjectGuid(); }
  
              if (CreatureData const* data = sObjectMgr.GetCreatureData(lowguid))
                  { return data->GetObjectGuid(lowguid); }
              else
                  { return ObjectGuid(); }
          }
          case GUID_LINK_GAMEOBJECT:
          {
              uint32 lowguid;
              if (!ExtractUInt32(&idS, lowguid))
                  { return ObjectGuid(); }
  
              if (GameObjectData const* data = sObjectMgr.GetGOData(lowguid))
                  { return ObjectGuid(HIGHGUID_GAMEOBJECT, data->id, lowguid); }
              else
                  { return ObjectGuid(); }
          }
      }
  
      // unknown type?
      return ObjectGuid();
  }
  
  enum LocationLinkType
  {
      LOCATION_LINK_RAW               = -1,                   // non-link case
      LOCATION_LINK_PLAYER            = 0,
      LOCATION_LINK_TELE              = 1,
      LOCATION_LINK_TAXINODE          = 2,
      LOCATION_LINK_CREATURE          = 3,
      LOCATION_LINK_GAMEOBJECT        = 4,
      LOCATION_LINK_CREATURE_ENTRY    = 5,
      LOCATION_LINK_GAMEOBJECT_ENTRY  = 6,
      LOCATION_LINK_AREATRIGGER       = 7,
      LOCATION_LINK_AREATRIGGER_TARGET = 8,
  };
  
  static char const* const locationKeys[] =
  {
      "Htele",
      "Htaxinode",
      "Hplayer",
      "Hcreature",
      "Hgameobject",
      "Hcreature_entry",
      "Hgameobject_entry",
      "Hareatrigger",
      "Hareatrigger_target",
      NULL
  };
  
  bool ChatHandler::ExtractLocationFromLink(char** text, uint32& mapid, float& x, float& y, float& z)
  {
      int type = 0;
  
      // |color|Hplayer:name|h[name]|h|r
      // |color|Htele:id|h[name]|h|r
      // |color|Htaxinode:id|h[name]|h|r
      // |color|Hcreature:creature_guid|h[name]|h|r
      // |color|Hgameobject:go_guid|h[name]|h|r
      // |color|Hcreature_entry:creature_id|h[name]|h|r
      // |color|Hgameobject_entry:go_id|h[name]|h|r
      // |color|Hareatrigger:id|h[name]|h|r
      // |color|Hareatrigger_target:id|h[name]|h|r
      char* idS = ExtractKeyFromLink(text, locationKeys, &type);
      if (!idS)
          { return false; }
  
      switch (type)
      {
          case LOCATION_LINK_RAW:
          case LOCATION_LINK_PLAYER:
          {
              std::string name = idS;
              if (!normalizePlayerName(name))
                  { return false; }
  
              if (Player* player = sObjectMgr.GetPlayer(name.c_str()))
              {
                  mapid = player->GetMapId();
                  x = player->GetPositionX();
                  y = player->GetPositionY();
                  z = player->GetPositionZ();
                  return true;
              }
  
              if (ObjectGuid guid = sObjectMgr.GetPlayerGuidByName(name))
              {
                  // to point where player stay (if loaded)
                  float o;
                  bool in_flight;
                  return Player::LoadPositionFromDB(guid, mapid, x, y, z, o, in_flight);
              }
  
              return false;
          }
          case LOCATION_LINK_TELE:
          {
              uint32 id;
              if (!ExtractUInt32(&idS, id))
                  { return false; }
  
              GameTele const* tele = sObjectMgr.GetGameTele(id);
              if (!tele)
                  { return false; }
              mapid = tele->mapId;
              x = tele->position_x;
              y = tele->position_y;
              z = tele->position_z;
              return true;
          }
          case LOCATION_LINK_TAXINODE:
          {
              uint32 id;
              if (!ExtractUInt32(&idS, id))
                  { return false; }
  
              TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
              if (!node)
                  { return false; }
              mapid = node->map_id;
              x = node->x;
              y = node->y;
              z = node->z;
              return true;
          }
          case LOCATION_LINK_CREATURE:
          {
              uint32 lowguid;
              if (!ExtractUInt32(&idS, lowguid))
                  { return false; }
  
              if (CreatureData const* data = sObjectMgr.GetCreatureData(lowguid))
              {
                  mapid = data->mapid;
                  x = data->posX;
                  y = data->posY;
                  z = data->posZ;
                  return true;
              }
              else
                  { return false; }
          }
          case LOCATION_LINK_GAMEOBJECT:
          {
              uint32 lowguid;
              if (!ExtractUInt32(&idS, lowguid))
                  { return false; }
  
              if (GameObjectData const* data = sObjectMgr.GetGOData(lowguid))
              {
                  mapid = data->mapid;
                  x = data->posX;
                  y = data->posY;
                  z = data->posZ;
                  return true;
              }
              else
                  { return false; }
          }
          case LOCATION_LINK_CREATURE_ENTRY:
          {
              uint32 id;
              if (!ExtractUInt32(&idS, id))
                  { return false; }
  
              if (ObjectMgr::GetCreatureTemplate(id))
              {
                  FindCreatureData worker(id, m_session ? m_session->GetPlayer() : NULL);
  
                  sObjectMgr.DoCreatureData(worker);
  
                  if (CreatureDataPair const* dataPair = worker.GetResult())
                  {
                      mapid = dataPair->second.mapid;
                      x = dataPair->second.posX;
                      y = dataPair->second.posY;
                      z = dataPair->second.posZ;
                      return true;
                  }
                  else
                      { return false; }
              }
              else
                  { return false; }
          }
          case LOCATION_LINK_GAMEOBJECT_ENTRY:
          {
              uint32 id;
              if (!ExtractUInt32(&idS, id))
                  { return false; }
  
              if (ObjectMgr::GetGameObjectInfo(id))
              {
                  FindGOData worker(id, m_session ? m_session->GetPlayer() : NULL);
  
                  sObjectMgr.DoGOData(worker);
  
                  if (GameObjectDataPair const* dataPair = worker.GetResult())
                  {
                      mapid = dataPair->second.mapid;
                      x = dataPair->second.posX;
                      y = dataPair->second.posY;
                      z = dataPair->second.posZ;
                      return true;
                  }
                  else
                      { return false; }
              }
              else
                  { return false; }
          }
          case LOCATION_LINK_AREATRIGGER:
          {
              uint32 id;
              if (!ExtractUInt32(&idS, id))
                  { return false; }
  
              AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(id);
              if (!atEntry)
              {
                  PSendSysMessage(LANG_COMMAND_GOAREATRNOTFOUND, id);
                  SetSentErrorMessage(true);
                  return false;
              }
  
              mapid = atEntry->mapid;
              x = atEntry->x;
              y = atEntry->y;
              z = atEntry->z;
              return true;
          }
          case LOCATION_LINK_AREATRIGGER_TARGET:
          {
              uint32 id;
              if (!ExtractUInt32(&idS, id))
                  { return false; }
  
              if (!sAreaTriggerStore.LookupEntry(id))
              {
                  PSendSysMessage(LANG_COMMAND_GOAREATRNOTFOUND, id);
                  SetSentErrorMessage(true);
                  return false;
              }
  
              AreaTrigger const* at = sObjectMgr.GetAreaTrigger(id);
              if (!at)
              {
                  PSendSysMessage(LANG_AREATRIGER_NOT_HAS_TARGET, id);
                  SetSentErrorMessage(true);
                  return false;
              }
  
              mapid = at->target_mapId;
              x = at->target_X;
              y = at->target_Y;
              z = at->target_Z;
              return true;
          }
      }
  
      // unknown type?
      return false;
  }
  
  std::string ChatHandler::ExtractPlayerNameFromLink(char** text)
  {
      // |color|Hplayer:name|h[name]|h|r
      char* name_str = ExtractKeyFromLink(text, "Hplayer");
      if (!name_str)
          { return ""; }
  
      std::string name = name_str;
      if (!normalizePlayerName(name))
          { return ""; }
  
      return name;
  }
  
  /**
   * Function extract at least one from request player data (pointer/guid/name) from args name/shift-link or selected player if no args
   *
   * @param args        variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces)
   *
   * @param player      optional arg   One from 3 optional args must be provided at least (or more).
   * @param player_guid optional arg   For function success only one from provided args need get result
   * @param player_name optional arg   But if early arg get value then all later args will have its (if requested)
   *                                   if player_guid requested and not found then name also will not found
   *                                   So at success can be returned 2 cases: (player/guid/name) or (guid/name)
   *
   * @return           true if extraction successful
   */
  bool ChatHandler::ExtractPlayerTarget(char** args, Player** player /*= NULL*/, ObjectGuid* player_guid /*= NULL*/, std::string* player_name /*= NULL*/)
  {
      if (*args &&**  args)
      {
          std::string name = ExtractPlayerNameFromLink(args);
          if (name.empty())
          {
              SendSysMessage(LANG_PLAYER_NOT_FOUND);
              SetSentErrorMessage(true);
              return false;
          }
  
          Player* pl = sObjectMgr.GetPlayer(name.c_str());
  
          // if allowed player pointer
          if (player)
              { *player = pl; }
  
          // if need guid value from DB (in name case for check player existence)
          ObjectGuid guid = !pl && (player_guid || player_name) ? sObjectMgr.GetPlayerGuidByName(name) : ObjectGuid();
  
          // if allowed player guid (if no then only online players allowed)
          if (player_guid)
              { *player_guid = pl ? pl->GetObjectGuid() : guid; }
  
          if (player_name)
              { *player_name = pl || guid ? name : ""; }
      }
      else
      {
          Player* pl = getSelectedPlayer();
          // if allowed player pointer
          if (player)
              { *player = pl; }
          // if allowed player guid (if no then only online players allowed)
          if (player_guid)
              { *player_guid = pl ? pl->GetObjectGuid() : ObjectGuid(); }
  
          if (player_name)
              { *player_name = pl ? pl->GetName() : ""; }
      }
  
      // some from req. data must be provided (note: name is empty if player not exist)
      if ((!player || !*player) && (!player_guid || !*player_guid) && (!player_name || player_name->empty()))
      {
          SendSysMessage(LANG_PLAYER_NOT_FOUND);
          SetSentErrorMessage(true);
          return false;
      }
  
      return true;
  }
  
  uint32 ChatHandler::ExtractAccountId(char** args, std::string* accountName /*= NULL*/, Player** targetIfNullArg /*= NULL*/)
  {
      uint32 account_id = 0;
  
      ///- Get the account name from the command line
      char* account_str = ExtractLiteralArg(args);
  
      if (!account_str)
      {
          if (!targetIfNullArg)
              { return 0; }
  
          /// only target player different from self allowed (if targetPlayer!=NULL then not console)
          Player* targetPlayer = getSelectedPlayer();
          if (!targetPlayer)
              { return 0; }
  
          account_id = targetPlayer->GetSession()->GetAccountId();
  
          if (accountName)
              { sAccountMgr.GetName(account_id, *accountName); }
  
          if (targetIfNullArg)
              { *targetIfNullArg = targetPlayer; }
  
          return account_id;
      }
  
      std::string account_name;
  
      if (ExtractUInt32(&account_str, account_id))
      {
          if (!sAccountMgr.GetName(account_id, account_name))
          {
              PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, account_str);
              SetSentErrorMessage(true);
              return 0;
          }
      }
      else
      {
          account_name = account_str;
          if (!AccountMgr::normalizeString(account_name))
          {
              PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, account_name.c_str());
              SetSentErrorMessage(true);
              return 0;
          }
  
          account_id = sAccountMgr.GetId(account_name);
          if (!account_id)
          {
              PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, account_name.c_str());
              SetSentErrorMessage(true);
              return 0;
          }
      }
  
      if (accountName)
          { *accountName = account_name; }
  
      if (targetIfNullArg)
          { *targetIfNullArg = NULL; }
  
      return account_id;
  }
  
  struct RaceMaskName
  {
      char const* literal;
      uint32 raceMask;
  };
  
  static RaceMaskName const raceMaskNames[] =
  {
      // races
      { "human", (1 << (RACE_HUMAN - 1))   },
      { "orc", (1 << (RACE_ORC - 1))     },
      { "dwarf", (1 << (RACE_DWARF - 1))   },
      { "nightelf", (1 << (RACE_NIGHTELF - 1))},
      { "undead", (1 << (RACE_UNDEAD - 1))  },
      { "tauren", (1 << (RACE_TAUREN - 1))  },
      { "gnome", (1 << (RACE_GNOME - 1))   },
      { "troll", (1 << (RACE_TROLL - 1))   },
      { "bloodelf", (1 << (RACE_BLOODELF - 1))},
      { "draenei", (1 << (RACE_DRAENEI - 1)) },
  
      // masks
      { "alliance", RACEMASK_ALLIANCE },
      { "horde",    RACEMASK_HORDE },
      { "all", RACEMASK_ALL_PLAYABLE },
  
      // terminator
      { NULL, 0 }
  };
  
  bool ChatHandler::ExtractRaceMask(char** text, uint32& raceMask, char const** maskName /*=NULL*/)
  {
      if (ExtractUInt32(text, raceMask))
      {
          if (maskName)
              { *maskName = "custom mask"; }
      }
      else
      {
          for (RaceMaskName const* itr = raceMaskNames; itr->literal; ++itr)
          {
              if (ExtractLiteralArg(text, itr->literal))
              {
                  raceMask = itr->raceMask;
  
                  if (maskName)
                      { *maskName = itr->literal; }
                  break;
              }
          }
  
          if (!raceMask)
              { return false; }
      }
  
      return true;
  }
  
  std::string ChatHandler::GetNameLink(Player* chr) const
  {
      return playerLink(chr->GetName());
  }
  
  bool ChatHandler::needReportToTarget(Player* chr) const
  {
      Player* pl = m_session->GetPlayer();
      return pl != chr && pl->IsVisibleGloballyFor(chr);
  }
  
  LocaleConstant ChatHandler::GetSessionDbcLocale() const
  {
      return m_session->GetSessionDbcLocale();
  }
  
  int ChatHandler::GetSessionDbLocaleIndex() const
  {
      return m_session->GetSessionDbLocaleIndex();
  }
  
  const char* CliHandler::GetMangosString(int32 entry) const
  {
      return sObjectMgr.GetMangosStringForDBCLocale(entry);
  }
  
  uint32 CliHandler::GetAccountId() const
  {
      return m_accountId;
  }
  
  AccountTypes CliHandler::GetAccessLevel() const
  {
      return m_loginAccessLevel;
  }
  
  bool CliHandler::isAvailable(ChatCommand const& cmd) const
  {
      // skip non-console commands in console case
      if (!cmd.AllowConsole)
          { return false; }
  
      // normal case
      return GetAccessLevel() >= (AccountTypes)cmd.SecurityLevel;
  }
  
  void CliHandler::SendSysMessage(const char* str)
  {
      m_print(m_callbackArg, str);
      m_print(m_callbackArg, "\r\n");
  }
  
  std::string CliHandler::GetNameLink() const
  {
      return GetMangosString(LANG_CONSOLE_COMMAND);
  }
  
  bool CliHandler::needReportToTarget(Player* /*chr*/) const
  {
      return true;
  }
  
  LocaleConstant CliHandler::GetSessionDbcLocale() const
  {
      return sWorld.GetDefaultDbcLocale();
  }
  
  int CliHandler::GetSessionDbLocaleIndex() const
  {
      return sObjectMgr.GetDBCLocaleIndex();
  }
  
  // Check/ Output if a NPC or GO (by guid) is part of a pool or game event
  template <typename T>
  void ChatHandler::ShowNpcOrGoSpawnInformation(uint32 guid)
  {
      if (uint16 pool_id = sPoolMgr.IsPartOfAPool<T>(guid))
      {
          uint16 top_pool_id = sPoolMgr.IsPartOfTopPool<Pool>(pool_id);
          if (!top_pool_id || top_pool_id == pool_id)
              { PSendSysMessage(LANG_NPC_GO_INFO_POOL, pool_id); }
          else
              { PSendSysMessage(LANG_NPC_GO_INFO_TOP_POOL, pool_id, top_pool_id); }
  
          if (int16 event_id = sGameEventMgr.GetGameEventId<Pool>(top_pool_id))
          {
              GameEventMgr::GameEventDataMap const& events = sGameEventMgr.GetEventMap();
              GameEventData const& eventData = events[std::abs(event_id)];
  
              if (event_id > 0)
                  { PSendSysMessage(LANG_NPC_GO_INFO_POOL_GAME_EVENT_S, top_pool_id, std::abs(event_id), eventData.description.c_str()); }
              else
                  { PSendSysMessage(LANG_NPC_GO_INFO_POOL_GAME_EVENT_D, top_pool_id, std::abs(event_id), eventData.description.c_str()); }
          }
      }
      else if (int16 event_id = sGameEventMgr.GetGameEventId<T>(guid))
      {
          GameEventMgr::GameEventDataMap const& events = sGameEventMgr.GetEventMap();
          GameEventData const& eventData = events[std::abs(event_id)];
  
          if (event_id > 0)
              { PSendSysMessage(LANG_NPC_GO_INFO_GAME_EVENT_S, std::abs(event_id), eventData.description.c_str()); }
          else
              { PSendSysMessage(LANG_NPC_GO_INFO_GAME_EVENT_D, std::abs(event_id), eventData.description.c_str()); }
      }
  }
  
  // Prepare ShortString for a NPC or GO (by guid) with pool or game event IDs
  template <typename T>
  std::string ChatHandler::PrepareStringNpcOrGoSpawnInformation(uint32 guid)
  {
      std::string str = "";
      if (uint16 pool_id = sPoolMgr.IsPartOfAPool<T>(guid))
      {
          uint16 top_pool_id = sPoolMgr.IsPartOfTopPool<T>(guid);
          if (int16 event_id = sGameEventMgr.GetGameEventId<Pool>(top_pool_id))
          {
              char buffer[100];
              const char* format = GetMangosString(LANG_NPC_GO_INFO_POOL_EVENT_STRING);
              sprintf(buffer, format, pool_id, event_id);
              str = buffer;
          }
          else
          {
              char buffer[100];
              const char* format = GetMangosString(LANG_NPC_GO_INFO_POOL_STRING);
              sprintf(buffer, format, pool_id);
              str = buffer;
          }
      }
      else if (int16 event_id = sGameEventMgr.GetGameEventId<T>(guid))
      {
          char buffer[100];
          const char* format = GetMangosString(LANG_NPC_GO_INFO_EVENT_STRING);
          sprintf(buffer, format, event_id);
          str = buffer;
      }
  
      return str;
  }
  
  void ChatHandler::LogCommand(char const* fullcmd)
  {
      // chat case
      if (m_session)
      {
          Player* p = m_session->GetPlayer();
          ObjectGuid sel_guid = p->GetSelectionGuid();
          sLog.outCommand(GetAccountId(), "Command: %s [Player: %s (Account: %u) X: %f Y: %f Z: %f Map: %u Selected: %s]",
                          fullcmd, p->GetName(), GetAccountId(), p->GetPositionX(), p->GetPositionY(), p->GetPositionZ(), p->GetMapId(),
                          sel_guid.GetString().c_str());
      }
      else                                        // 0 account -> console
      {
          sLog.outCommand(GetAccountId(), "Command: %s [Account: %u from %s]",
                          fullcmd, GetAccountId(), GetAccountId() ? "RA-connection" : "Console");
      }
  }
  
  void ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg msgtype, char const* message, Language language /*= LANG_UNIVERSAL*/, ChatTagFlags chatTag /*= CHAT_TAG_NONE*/,
                                    ObjectGuid const& senderGuid /*= ObjectGuid()*/, char const* senderName /*= NULL*/,
                                    ObjectGuid const& targetGuid /*= ObjectGuid()*/, char const* targetName /*= NULL*/,
                                    char const* channelName /*= NULL*/)
  {
      bool isGM = chatTag & CHAT_TAG_GM;
  
      data.Initialize(isGM ? SMSG_GM_MESSAGECHAT : SMSG_MESSAGECHAT);
      data << uint8(msgtype);
      data << uint32(language);
      data << ObjectGuid(senderGuid);
      data << uint32(0);                                              // 2.1.0
  
      switch (msgtype)
      {
          case CHAT_MSG_MONSTER_SAY:
          case CHAT_MSG_MONSTER_PARTY:
          case CHAT_MSG_MONSTER_YELL:
          case CHAT_MSG_MONSTER_WHISPER:
          case CHAT_MSG_RAID_BOSS_WHISPER:
          case CHAT_MSG_RAID_BOSS_EMOTE:
          case CHAT_MSG_MONSTER_EMOTE:
              MANGOS_ASSERT(senderName);
              data << uint32(strlen(senderName) + 1);
              data << senderName;
              data << ObjectGuid(targetGuid);                         // Unit Target
              if (targetGuid && !targetGuid.IsPlayer() && !targetGuid.IsPet())
              {
                  data << uint32(strlen(targetName) + 1);             // target name length
                  data << targetName;                                 // target name
              }
              MANGOS_ASSERT(message);
              data << uint32(strlen(message) + 1);
              data << message;
              data << uint8(chatTag);
              break;
          case CHAT_MSG_BG_SYSTEM_NEUTRAL:
          case CHAT_MSG_BG_SYSTEM_ALLIANCE:
          case CHAT_MSG_BG_SYSTEM_HORDE:
              data << ObjectGuid(targetGuid);                         // Unit Target
              if (targetGuid && !targetGuid.IsPlayer())
              {
                  MANGOS_ASSERT(targetName);
                  data << uint32(strlen(targetName) + 1);             // target name length
                  data << targetName;                                 // target name
              }
              MANGOS_ASSERT(message);
              data << uint32(strlen(message) + 1);
              data << message;
              data << uint8(chatTag);
              break;
          default:
              if (msgtype == CHAT_MSG_CHANNEL)
              {
                  MANGOS_ASSERT(channelName);
                  data << channelName;
              }
              data << ObjectGuid(targetGuid);
              MANGOS_ASSERT(message);
              data << uint32(strlen(message) + 1);
              data << message;
              data << uint8(chatTag);
              if (isGM)
              {
                  MANGOS_ASSERT(senderName);
                  data << uint32(strlen(senderName) + 1);
                  data << senderName;
              }
              break;
      }
  }
  
  // Instantiate template for helper function
  template void ChatHandler::ShowNpcOrGoSpawnInformation<Creature>(uint32 guid);
  template void ChatHandler::ShowNpcOrGoSpawnInformation<GameObject>(uint32 guid);
  
  template std::string ChatHandler::PrepareStringNpcOrGoSpawnInformation<Creature>(uint32 guid);
  template std::string ChatHandler::PrepareStringNpcOrGoSpawnInformation<GameObject>(uint32 guid);
  