/**
 * 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-2022 MaNGOS <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.
 */

#ifndef MANGOS_H_BATTLEGROUND
#define MANGOS_H_BATTLEGROUND

#include "Common.h"
#include "SharedDefines.h"
#include "Map.h"
#include "ByteBuffer.h"
#include "ObjectGuid.h"

// magic event-numbers
#define BG_EVENT_NONE 255
// those generic events should get a high event id
#define BG_EVENT_DOOR 254


class Creature;
class GameObject;
class Group;
class Player;
class WorldPacket;
class BattleGroundMap;

struct WorldSafeLocsEntry;

/**
 * @brief
 *
 */
struct BattleGroundEventIdx
{
    uint8 event1; /**< TODO */
    uint8 event2; /**< TODO */
};

/**
 * @brief
 *
 */
enum BattleGroundSounds
{
    SOUND_HORDE_WINS                = 8454,
    SOUND_ALLIANCE_WINS             = 8455,
    SOUND_BG_START                  = 3439
};

/**
 * @brief
 *
 */
enum BattleGroundQuests
{
    SPELL_WS_QUEST_REWARD           = 43483,
    SPELL_AB_QUEST_REWARD           = 43484,
    SPELL_AV_QUEST_REWARD           = 43475,
    SPELL_AV_QUEST_KILLED_BOSS      = 23658,
    SPELL_AB_QUEST_REWARD_4_BASES   = 24061,
    SPELL_AB_QUEST_REWARD_5_BASES   = 24064
};

/**
 * @brief
 *
 */
enum BattleGroundMarks
{
    SPELL_WS_MARK_LOSER             = 24950,
    SPELL_WS_MARK_WINNER            = 24951,
    SPELL_AB_MARK_LOSER             = 24952,
    SPELL_AB_MARK_WINNER            = 24953,
    SPELL_AV_MARK_LOSER             = 24954,
    SPELL_AV_MARK_WINNER            = 24955,
};

/**
 * @brief
 *
 */
enum BattleGroundMarksCount
{
    ITEM_WINNER_COUNT               = 3,
    ITEM_LOSER_COUNT                = 1
};

/**
 * @brief
 *
 */
enum BattleGroundTimeIntervals
{
    CHECK_PLAYER_POSITION_INVERVAL  = 1000,                 // ms
    RESURRECTION_INTERVAL           = 30000,                // ms
    INVITATION_REMIND_TIME          = 60000,                // ms
    INVITE_ACCEPT_WAIT_TIME         = 80000,                // ms
    TIME_TO_AUTOREMOVE              = 120000,               // ms
    MAX_OFFLINE_TIME                = 300,                  // secs
    RESPAWN_ONE_DAY                 = 86400,                // secs
    RESPAWN_IMMEDIATELY             = 0,                    // secs
    BUFF_RESPAWN_TIME               = 180                   // secs
};

/**
 * @brief
 *
 */
enum BattleGroundStartTimeIntervals
{
    BG_START_DELAY_2M               = 120000,               // ms (2 minutes)
    BG_START_DELAY_1M               = 60000,                // ms (1 minute)
    BG_START_DELAY_30S              = 30000,                // ms (30 seconds)
    BG_START_DELAY_NONE             = 0,                    // ms
};

/**
 * @brief
 *
 */
enum BattleGroundBuffObjects
{
    BG_OBJECTID_SPEEDBUFF_ENTRY     = 179871,
    BG_OBJECTID_REGENBUFF_ENTRY     = 179904,
    BG_OBJECTID_BERSERKERBUFF_ENTRY = 179905
};

const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENBUFF_ENTRY, BG_OBJECTID_BERSERKERBUFF_ENTRY }; /**< TODO */

/**
 * @brief
 *
 */
enum BattleGroundStatus
{
    STATUS_NONE         = 0,                                // first status, should mean bg is not instance
    STATUS_WAIT_QUEUE   = 1,                                // means bg is empty and waiting for queue
    STATUS_WAIT_JOIN    = 2,                                // this means, that BG has already started and it is waiting for more players
    STATUS_IN_PROGRESS  = 3,                                // means bg is running
    STATUS_WAIT_LEAVE   = 4                                 // means some faction has won BG and it is ending
};

/**
 * @brief
 *
 */
struct BattleGroundPlayer
{
    time_t  OfflineRemoveTime;                              /**< for tracking and removing offline players from queue after 5 minutes */
    Team    PlayerTeam;                                     /**< Player's team */
};

/**
 * @brief
 *
 */
struct BattleGroundObjectInfo
{
/**
 * @brief
 *
 */
    BattleGroundObjectInfo() : object(NULL), timer(0), spellid(0) {}

    GameObject*  object; /**< TODO */
    int32       timer; /**< TODO */
    uint32      spellid; /**< TODO */
};

/**
 * @brief handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time
 *
 */
enum BattleGroundQueueTypeId
{
    BATTLEGROUND_QUEUE_NONE     = 0,
    BATTLEGROUND_QUEUE_AV       = 1,
    BATTLEGROUND_QUEUE_WS       = 2,
    BATTLEGROUND_QUEUE_AB       = 3,
};
#define MAX_BATTLEGROUND_QUEUE_TYPES 4

/**
 * @brief
 *
 */
enum BattleGroundBracketId                                  // bracketId for level ranges
{
    BG_BRACKET_ID_TEMPLATE       = -1,                      // used to mark bg as template
    BG_BRACKET_ID_FIRST          = 0,                       // brackets start from specific BG min level and each include 10 levels range
    BG_BRACKET_ID_LAST           = 5                        // so for start level 10 will be 10-19, 20-29, ...  all greater max bg level included in last breaket
};

#define MAX_BATTLEGROUND_BRACKETS  6

/**
 * @brief
 *
 */
enum ScoreType
{
    SCORE_KILLING_BLOWS         = 1,
    SCORE_DEATHS                = 2,
    SCORE_HONORABLE_KILLS       = 3,
    SCORE_BONUS_HONOR           = 4,
    // WS
    SCORE_FLAG_CAPTURES         = 7,
    SCORE_FLAG_RETURNS          = 8,
    // AB
    SCORE_BASES_ASSAULTED       = 9,
    SCORE_BASES_DEFENDED        = 10,
    // AV
    SCORE_GRAVEYARDS_ASSAULTED  = 11,
    SCORE_GRAVEYARDS_DEFENDED   = 12,
    SCORE_TOWERS_ASSAULTED      = 13,
    SCORE_TOWERS_DEFENDED       = 14,
    SCORE_SECONDARY_OBJECTIVES  = 15
};

/**
 * @brief
 *
 */
enum BattleGroundStartingEvents
{
    BG_STARTING_EVENT_NONE  = 0x00,
    BG_STARTING_EVENT_1     = 0x01,
    BG_STARTING_EVENT_2     = 0x02,
    BG_STARTING_EVENT_3     = 0x04,
    BG_STARTING_EVENT_4     = 0x08
};

/**
 * @brief
 *
 */
enum BattleGroundStartingEventsIds
{
    BG_STARTING_EVENT_FIRST     = 0,
    BG_STARTING_EVENT_SECOND    = 1,
    BG_STARTING_EVENT_THIRD     = 2,
    BG_STARTING_EVENT_FOURTH    = 3
};
#define BG_STARTING_EVENT_COUNT 4

/**
 * @brief
 *
 */
enum BattleGroundJoinError
{
    BG_JOIN_ERR_OK = 0,
    BG_JOIN_ERR_OFFLINE_MEMBER = 1,
    BG_JOIN_ERR_GROUP_TOO_MANY = 2,
    BG_JOIN_ERR_MIXED_FACTION = 3,
    BG_JOIN_ERR_MIXED_LEVELS = 4,
    // BG_JOIN_ERR_MIXED_ARENATEAM = 5,
    BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE = 6,
    BG_JOIN_ERR_GROUP_DESERTER = 7,
    BG_JOIN_ERR_ALL_QUEUES_USED = 8,
    BG_JOIN_ERR_GROUP_NOT_ENOUGH = 9
};

/**
 * @brief
 *
 */
class BattleGroundScore
{
    public:
        /**
         * @brief
         *
         */
        BattleGroundScore() : KillingBlows(0), Deaths(0), HonorableKills(0),
            DishonorableKills(0), BonusHonor(0)
        {}
        /**
         * @brief virtual destructor is used when deleting score from scores map
         *
         */
        virtual ~BattleGroundScore() {}

        uint32 GetKillingBlows() const      { return KillingBlows; }
        uint32 GetDeaths() const            { return Deaths; }
        uint32 GetHonorableKills() const    { return HonorableKills; }
        uint32 GetBonusHonor() const        { return BonusHonor; }
        uint32 GetDamageDone() const        { return 0; }
        uint32 GetHealingDone() const       { return 0; }

        virtual uint32 GetAttr1() const     { return 0; }
        virtual uint32 GetAttr2() const     { return 0; }
        virtual uint32 GetAttr3() const     { return 0; }
        virtual uint32 GetAttr4() const     { return 0; }
        virtual uint32 GetAttr5() const     { return 0; }
        uint32 KillingBlows; /**< TODO */
        uint32 Deaths; /**< TODO */
        uint32 HonorableKills; /**< TODO */
        uint32 DishonorableKills; /**< TODO */
        uint32 BonusHonor; /**< TODO */
};

/**
 * @brief This class is used to:
 * 1. Add player to battleground
 * 2. Remove player from battleground
 * 3. some certain cases, same for all battlegrounds
 * 4. It has properties same for all battlegrounds
 *
 */
class BattleGround
{
        friend class BattleGroundMgr;

    public:
        /**
         * @brief Construction
         *
         */
        BattleGround();
        /**
         * @brief
         *
         */
        virtual ~BattleGround();
        /**
         * @brief
         *
         * @param diff
         */
        virtual void Update(uint32 diff);                   // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version
        /**
         * @brief
         *
         */
        virtual void Reset();                               // resets all common properties for battlegrounds, must be implemented and called in BG subclass
        /**
         * @brief
         *
         */
        virtual void StartingEventCloseDoors() {}
        /**
         * @brief
         *
         */
        virtual void StartingEventOpenDoors() {}

        /* Battleground */
        // Get methods:
        /**
         * @brief
         *
         * @return const char
         */
        char const* GetName() const         { return m_Name; }
        /**
         * @brief
         *
         * @return BattleGroundTypeId
         */
        BattleGroundTypeId GetTypeID() const
        {
            return m_TypeID;
        }
        /**
         * @brief
         *
         * @return BattleGroundBracketId
         */
        BattleGroundBracketId GetBracketId() const { return m_BracketId; }
        // the instanceId check is also used to determine a bg-template
        // that's why the m_map hack is here..
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetInstanceID()              { return m_Map ? GetBgMap()->GetInstanceId() : 0; }
        /**
         * @brief
         *
         * @return BattleGroundStatus
         */
        BattleGroundStatus GetStatus() const { return m_Status; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetClientInstanceID() const  { return m_ClientInstanceID; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetStartTime() const         { return m_StartTime; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetEndTime() const           { return m_EndTime; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetMaxPlayers() const        { return m_MaxPlayers; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetMinPlayers() const        { return m_MinPlayers; }

        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetMinLevel() const          { return m_LevelMin; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetMaxLevel() const          { return m_LevelMax; }

        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; }

        /**
         * @brief
         *
         * @return int32
         */
        int32 GetStartDelayTime() const     { return m_StartDelayTime; }
        /**
         * @brief
         *
         * @return Team
         */
        Team GetWinner() const              { return m_Winner; }
        /**
         * @brief
         *
         * @return Team
         */
        virtual Team GetPrematureWinner();
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetBattlemasterEntry() const;
        /**
         * @brief
         *
         * @param kills
         * @return uint32
         */
        uint32 GetBonusHonorFromKill(uint32 kills) const;

        // Set methods:
        /**
         * @brief
         *
         * @param Name
         */
        void SetName(char const* Name)      { m_Name = Name; }
        /**
         * @brief
         *
         * @param TypeID
         */
        void SetTypeID(BattleGroundTypeId TypeID) { m_TypeID = TypeID; }
        /**
         * @brief
         *
         * @param ID
         */
        void SetBracketId(BattleGroundBracketId ID) { m_BracketId = ID; }
        /**
         * @brief
         *
         * @param Status
         */
        void SetStatus(BattleGroundStatus Status) { m_Status = Status; }
        /**
         * @brief
         *
         * @param InstanceID
         */
        void SetClientInstanceID(uint32 InstanceID) { m_ClientInstanceID = InstanceID; }
        /**
         * @brief
         *
         * @param Time
         */
        void SetStartTime(uint32 Time)      { m_StartTime = Time; }
        /**
         * @brief
         *
         * @param Time
         */
        void SetEndTime(uint32 Time)        { m_EndTime = Time; }
        /**
         * @brief
         *
         * @param MaxPlayers
         */
        void SetMaxPlayers(uint32 MaxPlayers) { m_MaxPlayers = MaxPlayers; }
        /**
         * @brief
         *
         * @param MinPlayers
         */
        void SetMinPlayers(uint32 MinPlayers) { m_MinPlayers = MinPlayers; }
        /**
         * @brief
         *
         * @param min
         * @param max
         */
        void SetLevelRange(uint32 min, uint32 max) { m_LevelMin = min; m_LevelMax = max; }
        /**
         * @brief
         *
         * @param winner
         */
        void SetWinner(Team winner)         { m_Winner = winner; }

        /**
         * @brief
         *
         * @param diff
         */
        void ModifyStartDelayTime(int diff) { m_StartDelayTime -= diff; }
        /**
         * @brief
         *
         * @param Time
         */
        void SetStartDelayTime(int Time)    { m_StartDelayTime = Time; }

        /**
         * @brief
         *
         * @param MaxPlayers
         */
        void SetMaxPlayersPerTeam(uint32 MaxPlayers) { m_MaxPlayersPerTeam = MaxPlayers; }
        /**
         * @brief
         *
         * @param MinPlayers
         */
        void SetMinPlayersPerTeam(uint32 MinPlayers) { m_MinPlayersPerTeam = MinPlayers; }

        /**
         * @brief
         *
         */
        void AddToBGFreeSlotQueue();                        // this queue will be useful when more battlegrounds instances will be available
        /**
         * @brief
         *
         */
        void RemoveFromBGFreeSlotQueue();                   // this method could delete whole BG instance, if another free is available

        /**
         * @brief
         *
         * @param team
         */
        void DecreaseInvitedCount(Team team)      { (team == ALLIANCE) ? --m_InvitedAlliance : --m_InvitedHorde; }
        /**
         * @brief
         *
         * @param team
         */
        void IncreaseInvitedCount(Team team)      { (team == ALLIANCE) ? ++m_InvitedAlliance : ++m_InvitedHorde; }
        /**
         * @brief
         *
         * @param team
         * @return uint32
         */
        uint32 GetInvitedCount(Team team) const
        {
            if (team == ALLIANCE)
            {
                return m_InvitedAlliance;
            }
            else
            {
                return m_InvitedHorde;
            }
        }
        /**
         * @brief
         *
         * @return bool
         */
        bool HasFreeSlots() const;
        /**
         * @brief
         *
         * @param team
         * @return uint32
         */
        uint32 GetFreeSlotsForTeam(Team team) const;

        /**
         * @brief
         *
         */
        typedef std::map<ObjectGuid, BattleGroundPlayer> BattleGroundPlayerMap;
        /**
         * @brief
         *
         * @return const BattleGroundPlayerMap
         */
        BattleGroundPlayerMap const& GetPlayers() const { return m_Players; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetPlayersSize() const { return m_Players.size(); }

        /**
         * @brief
         *
         */
        typedef std::map<ObjectGuid, BattleGroundScore*> BattleGroundScoreMap;
        /**
         * @brief
         *
         * @return BattleGroundScoreMap::const_iterator
         */
        BattleGroundScoreMap::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); }
        /**
         * @brief
         *
         * @return BattleGroundScoreMap::const_iterator
         */
        BattleGroundScoreMap::const_iterator GetPlayerScoresEnd() const { return m_PlayerScores.end(); }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetPlayerScoresSize() const { return m_PlayerScores.size(); }

        /**
         * @brief
         *
         */
        void StartBattleGround();

        /* Location */
        /**
         * @brief
         *
         * @param MapID
         */
        void SetMapId(uint32 MapID) { m_MapId = MapID; }
        /**
         * @brief
         *
         * @return uint32
         */
        uint32 GetMapId() const { return m_MapId; }

        /* Map pointers */
        /**
         * @brief
         *
         * @param map
         */
        void SetBgMap(BattleGroundMap* map) { m_Map = map; }
        /**
         * @brief
         *
         * @return BattleGroundMap
         */
        BattleGroundMap* GetBgMap()
        {
            MANGOS_ASSERT(m_Map);
            return m_Map;
        }

        /**
         * @brief
         *
         * @param team
         * @param X
         * @param Y
         * @param Z
         * @param O
         */
        void SetTeamStartLoc(Team team, float X, float Y, float Z, float O);
        /**
         * @brief
         *
         * @param team
         * @param X
         * @param Y
         * @param Z
         * @param O
         */
        void GetTeamStartLoc(Team team, float& X, float& Y, float& Z, float& O) const
        {
            PvpTeamIndex idx = GetTeamIndexByTeamId(team);
            X = m_TeamStartLocX[idx];
            Y = m_TeamStartLocY[idx];
            Z = m_TeamStartLocZ[idx];
            O = m_TeamStartLocO[idx];
        }

        void SetStartMaxDist(float startMaxDist) { m_startMaxDist = startMaxDist; }
        float GetStartMaxDist() const { return m_startMaxDist; }

        /* Packet Transfer */
        // method that should fill worldpacket with actual world states (not yet implemented for all battlegrounds!)
        /**
         * @brief
         *
         * @param
         * @param
         */
        virtual void FillInitialWorldStates(WorldPacket& /*data*/, uint32& /*count*/) {}
        /**
         * @brief
         *
         * @param team
         * @param packet
         * @param sender
         * @param self
         */
        void SendPacketToTeam(Team team, WorldPacket* packet, Player* sender = NULL, bool self = true);
        /**
         * @brief
         *
         * @param packet
         */
        void SendPacketToAll(WorldPacket* packet);

        template<class Do>
        /**
         * @brief
         *
         * @param _do
         */
        void BroadcastWorker(Do& _do);

        /**
         * @brief
         *
         * @param SoundID
         * @param team
         */
        void PlaySoundToTeam(uint32 SoundID, Team team);
        /**
         * @brief
         *
         * @param SoundID
         */
        void PlaySoundToAll(uint32 SoundID);
        /**
         * @brief
         *
         * @param SpellID
         * @param team
         */
        void CastSpellOnTeam(uint32 SpellID, Team team);
        /**
         * @brief
         *
         * @param Honor
         * @param team
         */
        void RewardHonorToTeam(uint32 Honor, Team team);
        /**
         * @brief
         *
         * @param faction_id
         * @param Reputation
         * @param team
         */
        void RewardReputationToTeam(uint32 faction_id, uint32 Reputation, Team team);
        /**
         * @brief
         *
         * @param plr
         * @param count
         */
        void RewardMark(Player* plr, uint32 count);
        /**
         * @brief
         *
         * @param plr
         * @param mark
         * @param count
         */
        void SendRewardMarkByMail(Player* plr, uint32 mark, uint32 count);
        /**
         * @brief
         *
         * @param plr
         * @param item_id
         * @param count
         */
        void RewardItem(Player* plr, uint32 item_id, uint32 count);
        /**
         * @brief
         *
         * @param plr
         */
        void RewardQuestComplete(Player* plr);
        /**
         * @brief
         *
         * @param plr
         * @param spell_id
         */
        void RewardSpellCast(Player* plr, uint32 spell_id);
        /**
         * @brief
         *
         * @param Field
         * @param Value
         */
        void UpdateWorldState(uint32 Field, uint32 Value);
        /**
         * @brief
         *
         * @param Field
         * @param Value
         * @param Source
         */
        void UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player* Source);
        /**
         * @brief
         *
         * @param winner
         */
        virtual void EndBattleGround(Team winner);
        /**
         * @brief
         *
         * @param plr
         */
        void BlockMovement(Player* plr);

        /**
         * @brief
         *
         * @param entry
         * @param type
         * @param source
         */
        void SendMessageToAll(int32 entry, ChatMsg type, Player const* source = NULL);
        /**
         * @brief
         *
         * @param entry
         * @param language
         * @param guid
         */
        void SendYellToAll(int32 entry, uint32 language, ObjectGuid guid);
        /**
         * @brief
         *
         * @param entry
         * @param type
         * @param source...
         */
        void PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...);

        // specialized version with 2 string id args
        /**
         * @brief
         *
         * @param entry
         * @param type
         * @param source
         * @param strId1
         * @param strId2
         */
        void SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 strId1 = 0, int32 strId2 = 0);
        /**
         * @brief
         *
         * @param entry
         * @param language
         * @param guid
         * @param arg1
         * @param arg2
         */
        void SendYell2ToAll(int32 entry, uint32 language, ObjectGuid guid, int32 arg1, int32 arg2);

        /* Raid Group */
        /**
         * @brief
         *
         * @param team
         * @return Group
         */
        Group* GetBgRaid(Team team) const { return m_BgRaids[GetTeamIndexByTeamId(team)]; }
        /**
         * @brief
         *
         * @param team
         * @param bg_raid
         */
        void SetBgRaid(Team team, Group* bg_raid);

        /**
         * @brief
         *
         * @param Source
         * @param type
         * @param value
         */
        virtual void UpdatePlayerScore(Player* Source, uint32 type, uint32 value);

        /**
         * @brief
         *
         * @param team
         * @return BattleGroundTeamIndex
         */
        static PvpTeamIndex GetTeamIndexByTeamId(Team team) { return team == ALLIANCE ? TEAM_INDEX_ALLIANCE : TEAM_INDEX_HORDE; }
        /**
         * @brief
         *
         * @param team
         * @return uint32
         */
        uint32 GetPlayersCountByTeam(Team team) const { return m_PlayersCount[GetTeamIndexByTeamId(team)]; }
        /**
         * @brief
         *
         * @param team
         * @return uint32
         */
        uint32 GetAlivePlayersCountByTeam(Team team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases
        /**
         * @brief
         *
         * @param team
         * @param remove
         */
        void UpdatePlayersCountByTeam(Team team, bool remove)
        {
            if (remove)
            {
                --m_PlayersCount[GetTeamIndexByTeamId(team)];
            }
            else
            {
                ++m_PlayersCount[GetTeamIndexByTeamId(team)];
            }
        }

        /* Triggers handle */
        // must be implemented in BG subclass
        /**
         * @brief
         *
         * @param
         * @param uint32
         */
        virtual bool HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) { return false;  }
        // must be implemented in BG subclass if need AND call base class generic code
        /**
         * @brief
         *
         * @param player
         * @param killer
         */
        virtual void HandleKillPlayer(Player* player, Player* killer);
        /**
         * @brief
         *
         * @param
         * @param
         */
        virtual void HandleKillUnit(Creature* /*unit*/, Player* /*killer*/) {}

        // Process Capture event
        /**
         * @brief
         *
         * @param uint32
         * @param
         * @return bool
         */
        virtual bool HandleEvent(uint32 /*eventId*/, GameObject* /*go*/) { return false; }

        // Called when a gameobject is created
        /**
         * @brief
         *
         * @param
         */
        virtual void HandleGameObjectCreate(GameObject* /*go*/) {}

        /* Battleground events */
        /**
         * @brief
         *
         * @param
         */
        virtual void EventPlayerDroppedFlag(Player* /*player*/) {}
        /**
         * @brief
         *
         * @param
         * @param
         */
        virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {}
        /**
         * @brief
         *
         * @param
         */
        virtual void EventPlayerCapturedFlag(Player* /*player*/) {}
        /**
         * @brief
         *
         * @param player
         */
        void EventPlayerLoggedIn(Player* player);
        /**
         * @brief
         *
         * @param player
         */
        void EventPlayerLoggedOut(Player* player);

        /* Death related */
        /**
         * @brief
         *
         * @param player
         * @return const WorldSafeLocsEntry
         */
        virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player);

        /**
         * @brief
         *
         * @param plr
         */
        virtual void AddPlayer(Player* plr);                // must be implemented in BG subclass

        /**
         * @brief
         *
         * @param plr
         * @param plr_guid
         * @param team
         */
        void AddOrSetPlayerToCorrectBgGroup(Player* plr, ObjectGuid plr_guid, Team team);

        /**
         * @brief
         *
         * @param guid
         * @param Transport
         * @param SendPacket
         */
        virtual void RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool SendPacket);
        // can be extended in in BG subclass

        /* event related */
        // called when a creature gets added to map (NOTE: only triggered if
        // a player activates the cell of the creature)
        /**
         * @brief
         *
         * @param
         */
        void OnObjectDBLoad(Creature* /*creature*/);
        /**
         * @brief
         *
         * @param
         */
        void OnObjectDBLoad(GameObject* /*obj*/);
        // (de-)spawns creatures and gameobjects from an event
        /**
         * @brief
         *
         * @param event1
         * @param event2
         * @param spawn
         */
        void SpawnEvent(uint8 event1, uint8 event2, bool spawn);
        /**
         * @brief
         *
         * @param event1
         * @param event2
         * @return bool
         */
        bool IsActiveEvent(uint8 event1, uint8 event2)
        {
            if (m_ActiveEvents.find(event1) == m_ActiveEvents.end())
            {
                return false;
            }
            return m_ActiveEvents[event1] == event2;
        }
        /**
         * @brief
         *
         * @param event1
         * @param event2
         * @return ObjectGuid
         */
        ObjectGuid GetSingleCreatureGuid(uint8 event1, uint8 event2);

        /**
         * @brief
         *
         * @param event1
         * @param event2
         */
        void OpenDoorEvent(uint8 event1, uint8 event2 = 0);
        /**
         * @brief
         *
         * @param event1
         * @param event2
         * @return bool
         */
        bool IsDoor(uint8 event1, uint8 event2);

        /**
         * @brief
         *
         * @param go_guid
         */
        void HandleTriggerBuff(ObjectGuid go_guid);

        /**
         * @brief
         *
         * @param guid
         * @param respawntime
         */
        void SpawnBGObject(ObjectGuid guid, uint32 respawntime);
        /**
         * @brief
         *
         * @param guid
         * @param respawntime
         */
        void SpawnBGCreature(ObjectGuid guid, uint32 respawntime);

        /**
         * @brief
         *
         * @param guid
         */
        void DoorOpen(ObjectGuid guid);
        /**
         * @brief
         *
         * @param guid
         */
        void DoorClose(ObjectGuid guid);

        /**
         * @brief
         *
         * @param
         * @return bool
         */
        virtual bool HandlePlayerUnderMap(Player* /*plr*/) { return false; }

        // since arenas can be AvA or Hvh, we have to get the "temporary" team of a player
        /**
         * @brief
         *
         * @param guid
         * @return Team
         */
        Team GetPlayerTeam(ObjectGuid guid);
        /**
         * @brief
         *
         * @param team
         * @return Team
         */
        static Team GetOtherTeam(Team team) { return team ? ((team == ALLIANCE) ? HORDE : ALLIANCE) : TEAM_NONE; }
        /**
         * @brief
         *
         * @param teamIdx
         * @return BattleGroundTeamIndex
         */
        static PvpTeamIndex GetOtherTeamIndex(PvpTeamIndex teamIdx) { return teamIdx == TEAM_INDEX_ALLIANCE ? TEAM_INDEX_HORDE : TEAM_INDEX_ALLIANCE; }
        /**
         * @brief
         *
         * @param guid
         * @return bool
         */
        bool IsPlayerInBattleGround(ObjectGuid guid);

        /* virtual score-array - get's used in bg-subclasses */
        int32 m_TeamScores[PVP_TEAM_COUNT];

        /**
         * @brief
         *
         */
        struct EventObjects
        {
            GuidVector gameobjects; /**< TODO */
            GuidVector creatures; /**< TODO */
        };

        // cause we create it dynamicly i use a map - to avoid resizing when
        // using vector - also it contains 2*events concatenated with PAIR32
        // this is needed to avoid overhead of a 2dimensional std::map
        std::map<uint32, EventObjects> m_EventObjects; /**< TODO */
        // this must be filled first in BattleGroundXY::Reset().. else
        // creatures will get added wrong
        // door-events are automaticly added - but _ALL_ other must be in this vector
        std::map<uint8, uint8> m_ActiveEvents; /**< TODO */


    protected:
        /**
         * @brief Ends a battleground
         *
         * This method is called, when BG can not spawn its own spirit guide, or
         * something is wrong, It correctly ends BattleGround
         */
        void EndNow();
        /**
         * @brief
         *
         * @param plr
         */
        void PlayerAddedToBGCheckIfBGIsRunning(Player* plr);

        /* Scorekeeping */

        BattleGroundScoreMap m_PlayerScores;                /**< Player scores */
        // must be implemented in BG subclass
        /**
         * @brief
         *
         * @param
         * @param ObjectGuid
         */
        virtual void RemovePlayer(Player* /*player*/, ObjectGuid /*guid*/) {}

        /* Player lists, those need to be accessible by inherited classes */
        BattleGroundPlayerMap  m_Players; /**< TODO */

        /*
        these are important variables used for starting messages
        */
        uint8 m_Events; /**< TODO */
        BattleGroundStartTimeIntervals  m_StartDelayTimes[BG_STARTING_EVENT_COUNT]; /**< TODO */
        // this must be filled in constructors!
        uint32 m_StartMessageIds[BG_STARTING_EVENT_COUNT]; /**< TODO */

        bool   m_BuffChange; /**< TODO */

    private:
        /* Battleground */
        BattleGroundTypeId m_TypeID; /**< TODO */
        BattleGroundStatus m_Status; /**< TODO */
        uint32 m_ClientInstanceID;                          /**< the instance-id which is sent to the client and without any other internal use */
        uint32 m_StartTime; /**< TODO */
        uint32 m_validStartPositionTimer;
        int32 m_EndTime;                                    /**< it is set to 120000 when bg is ending and it decreases itself */
        BattleGroundBracketId m_BracketId; /**< TODO */
        bool   m_InBGFreeSlotQueue;                         /**< used to make sure that BG is only once inserted into the BattleGroundMgr.BGFreeSlotQueue[bgTypeId] deque */
        Team   m_Winner;                                    /**< 0=alliance, 1=horde, 2=none */
        int32  m_StartDelayTime; /**< TODO */
        bool   m_PrematureCountDown; /**< TODO */
        uint32 m_PrematureCountDownTimer; /**< TODO */
        char const* m_Name; /**< TODO */

        /* Player lists */
        /**
         * @brief
         *
         */
        typedef std::deque<ObjectGuid> OfflineQueue;
        OfflineQueue m_OfflineQueue;                        /**< Player GUID */

        /* Invited counters are useful for player invitation to BG - do not allow, if BG is started to one faction to have 2 more players than another faction */
        /* Invited counters will be changed only when removing already invited player from queue, removing player from battleground and inviting player to BG */
        /* Invited players counters*/
        uint32 m_InvitedAlliance; /**< TODO */
        uint32 m_InvitedHorde; /**< TODO */

        /* Raid Group */
        Group* m_BgRaids[PVP_TEAM_COUNT];                   /**< 0 - alliance, 1 - horde */

        /* Players count by team */
        uint32 m_PlayersCount[PVP_TEAM_COUNT]; /**< TODO */

        /* Limits */
        uint32 m_LevelMin; /**< TODO */
        uint32 m_LevelMax; /**< TODO */
        uint32 m_MaxPlayersPerTeam; /**< TODO */
        uint32 m_MaxPlayers; /**< TODO */
        uint32 m_MinPlayersPerTeam; /**< TODO */
        uint32 m_MinPlayers; /**< TODO */

        /* Start location */
        uint32 m_MapId; /**< TODO */
        BattleGroundMap* m_Map; /**< TODO */
        float m_startMaxDist;
        float m_TeamStartLocX[PVP_TEAM_COUNT]; /**< TODO */
        float m_TeamStartLocY[PVP_TEAM_COUNT]; /**< TODO */
        float m_TeamStartLocZ[PVP_TEAM_COUNT]; /**< TODO */
        float m_TeamStartLocO[PVP_TEAM_COUNT]; /**< TODO */
};

// helper functions for world state list fill
/**
 * @brief
 *
 * @param data
 * @param count
 * @param state
 * @param value
 */
inline void FillInitialWorldState(ByteBuffer& data, uint32& count, uint32 state, uint32 value)
{
    data << uint32(state);
    data << uint32(value);
    ++count;
}

/**
 * @brief
 *
 * @param data
 * @param count
 * @param state
 * @param value
 */
inline void FillInitialWorldState(ByteBuffer& data, uint32& count, uint32 state, int32 value)
{
    data << uint32(state);
    data << int32(value);
    ++count;
}

/**
 * @brief
 *
 * @param data
 * @param count
 * @param state
 * @param value
 */
inline void FillInitialWorldState(ByteBuffer& data, uint32& count, uint32 state, bool value)
{
    data << uint32(state);
    data << uint32(value ? 1 : 0);
    ++count;
}

/**
 * @brief
 *
 */
struct WorldStatePair
{
    uint32 state; /**< TODO */
    uint32 value; /**< TODO */
};

/**
 * @brief
 *
 * @param data
 * @param count
 * @param array
 */
inline void FillInitialWorldState(ByteBuffer& data, uint32& count, WorldStatePair const* array)
{
    for (WorldStatePair const* itr = array; itr->state; ++itr)
    {
        data << uint32(itr->state);
        data << uint32(itr->value);
        ++count;
    }
}

#endif
