/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * 
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

using Framework.Constants;
using Framework.Database;
using Game.Arenas;
using Game.BattleFields;
using Game.BattleGrounds;
using Game.DataStorage;
using Game.Network.Packets;
using Game.PvP;
using System;
using System.Collections.Generic;

namespace Game.Entities
{
    public partial class Player
    {
        //PvP
        public void UpdateHonorFields()
        {
            // called when rewarding honor and at each save
            long now = Time.UnixTime;
            long today = (Time.UnixTime / Time.Day) * Time.Day;

            if (m_lastHonorUpdateTime < today)
            {
                long yesterday = today - Time.Day;

                // update yesterday's contribution
                if (m_lastHonorUpdateTime >= yesterday)
                {
                    // this is the first update today, reset today's contribution
                    ushort killsToday = GetUInt16Value(PlayerFields.Kills, 0);
                    SetUInt16Value(PlayerFields.Kills, 0, 0);
                    SetUInt16Value(PlayerFields.Kills, 1, killsToday);

                }
                else
                {
                    // no honor/kills yesterday or today, reset
                    SetUInt32Value(PlayerFields.Kills, 0);
                }
            }

            m_lastHonorUpdateTime = now;
        }
        public bool RewardHonor(Unit victim, uint groupsize, int honor = -1, bool pvptoken = false)
        {
            // do not reward honor in arenas, but enable onkill spellproc
            if (InArena())
            {
                if (!victim || victim == this || !victim.IsTypeId(TypeId.Player))
                    return false;

                if (GetBGTeam() == victim.ToPlayer().GetBGTeam())
                    return false;

                return true;
            }

            // 'Inactive' this aura prevents the player from gaining honor points and BattlegroundTokenizer
            if (HasAura(BattlegroundConst.SpellAuraPlayerInactive))
                return false;

            ObjectGuid victim_guid = ObjectGuid.Empty;
            uint victim_rank = 0;

            // need call before fields update to have chance move yesterday data to appropriate fields before today data change.
            UpdateHonorFields();

            // do not reward honor in arenas, but return true to enable onkill spellproc
            if (InBattleground() && GetBattleground() && GetBattleground().isArena())
                return true;

            // Promote to float for calculations
            float honor_f = honor;

            if (honor_f <= 0)
            {
                if (!victim || victim == this || victim.HasAuraType(AuraType.NoPvpCredit))
                    return false;

                victim_guid = victim.GetGUID();
                Player plrVictim = victim.ToPlayer();
                if (plrVictim)
                {
                    if (GetTeam() == plrVictim.GetTeam() && !Global.WorldMgr.IsFFAPvPRealm())
                        return false;

                    byte k_level = (byte)getLevel();
                    byte k_grey = (byte)Formulas.GetGrayLevel(k_level);
                    byte v_level = (byte)victim.getLevel();

                    if (v_level <= k_grey)
                        return false;

                    // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
                    //  [0]      Just name
                    //  [1..14]  Alliance honor titles and player name
                    //  [15..28] Horde honor titles and player name
                    //  [29..38] Other title and player name
                    //  [39+]    Nothing
                    uint victim_title = victim.GetUInt32Value(PlayerFields.ChosenTitle);
                    // Get Killer titles, CharTitlesEntry.bit_index
                    // Ranks:
                    //  title[1..14]  . rank[5..18]
                    //  title[15..28] . rank[5..18]
                    //  title[other]  . 0
                    if (victim_title == 0)
                        victim_guid.Clear();                        // Don't show HK: <rank> message, only log.
                    else if (victim_title < 15)
                        victim_rank = victim_title + 4;
                    else if (victim_title < 29)
                        victim_rank = victim_title - 14 + 4;
                    else
                        victim_guid.Clear();                        // Don't show HK: <rank> message, only log.

                    honor_f = (float)Math.Ceiling(Formulas.hk_honor_at_level_f(k_level) * (v_level - k_grey) / (k_level - k_grey));

                    // count the number of playerkills in one day
                    ApplyModUInt16Value(PlayerFields.Kills, 0, 1, true);
                    // and those in a lifetime
                    ApplyModUInt32Value(PlayerFields.LifetimeHonorableKills, 1, true);
                    UpdateCriteria(CriteriaTypes.EarnHonorableKill);
                    UpdateCriteria(CriteriaTypes.HkClass, (uint)victim.GetClass());
                    UpdateCriteria(CriteriaTypes.HkRace, (uint)victim.GetRace());
                    UpdateCriteria(CriteriaTypes.HonorableKillAtArea, GetAreaId());
                    UpdateCriteria(CriteriaTypes.HonorableKill, 1, 0, 0, victim);
                }
                else
                {
                    if (!victim.ToCreature().IsRacialLeader())
                        return false;

                    honor_f = 100.0f;                               // ??? need more info
                    victim_rank = 19;                               // HK: Leader
                }
            }

            if (victim != null)
            {
                if (groupsize > 1)
                    honor_f /= groupsize;

                // apply honor multiplier from aura (not stacking-get highest)
                MathFunctions.AddPct(ref honor_f, GetMaxPositiveAuraModifier(AuraType.ModHonorGainPct));
                honor_f += _restMgr.GetRestBonusFor(RestTypes.Honor, (uint)honor_f);
            }

            honor_f *= WorldConfig.GetFloatValue(WorldCfg.RateHonor);
            // Back to int now
            honor = (int)honor_f;
            // honor - for show honor points in log
            // victim_guid - for show victim name in log
            // victim_rank [1..4]  HK: <dishonored rank>
            // victim_rank [5..19] HK: <alliance\horde rank>
            // victim_rank [0, 20+] HK: <>
            PvPCredit data = new PvPCredit();
            data.Honor = honor;
            data.OriginalHonor = honor;
            data.Target = victim_guid;
            data.Rank = victim_rank;

            SendPacket(data);

            AddHonorXP((uint)honor);

            if (InBattleground() && honor > 0)
            {
                Battleground bg = GetBattleground();
                if (bg != null)
                {
                    bg.UpdatePlayerScore(this, ScoreType.BonusHonor, (uint)honor, false); //false: prevent looping
                }
            }

            if (WorldConfig.GetBoolValue(WorldCfg.PvpTokenEnable) && pvptoken)
            {
                if (!victim || victim == this || victim.HasAuraType(AuraType.NoPvpCredit))
                    return true;

                if (victim.IsTypeId(TypeId.Player))
                {
                    // Check if allowed to receive it in current map
                    int MapType = WorldConfig.GetIntValue(WorldCfg.PvpTokenMapType);
                    if ((MapType == 1 && !InBattleground() && !IsFFAPvP())
                        || (MapType == 2 && !IsFFAPvP())
                        || (MapType == 3 && !InBattleground()))
                        return true;

                    uint itemId = WorldConfig.GetUIntValue(WorldCfg.PvpTokenId);
                    uint count = WorldConfig.GetUIntValue(WorldCfg.PvpTokenCount);

                    if (AddItem(itemId, count))
                        SendSysMessage("You have been awarded a token for slaying another player.");
                }
            }

            return true;
        }

        void _InitHonorLevelOnLoadFromDB(uint honor, uint honorLevel, uint prestigeLevel)
        {
            SetUInt32Value(PlayerFields.HonorLevel, honorLevel);
            SetUInt32Value(PlayerFields.Prestige, prestigeLevel);
            UpdateHonorNextLevel();

            AddHonorXP(honor);
            if (CanPrestige())
                Prestige();
        }

        void RewardPlayerWithRewardPack(uint rewardPackID)
        {
            RewardPlayerWithRewardPack(CliDB.RewardPackStorage.LookupByKey(rewardPackID));
        }

        void RewardPlayerWithRewardPack(RewardPackRecord rewardPackEntry)
        {
            if (rewardPackEntry == null)
                return;

            CharTitlesRecord charTitlesEntry = CliDB.CharTitlesStorage.LookupByKey(rewardPackEntry.TitleID);
            if (charTitlesEntry != null)
                SetTitle(charTitlesEntry);

            ModifyMoney(rewardPackEntry.Money);
            var rewardPackXItems = Global.DB2Mgr.GetRewardPackItemsByRewardID(rewardPackEntry.Id);
            if (rewardPackXItems != null)
            {
                foreach (RewardPackXItemRecord rewardPackXItem in rewardPackXItems)
                    AddItem(rewardPackXItem.ItemID, rewardPackXItem.Amount);
            }
        }

        public void AddHonorXP(uint xp)
        {
            uint currentHonorXP = GetUInt32Value(PlayerFields.Honor);
            uint nextHonorLevelXP = GetUInt32Value(PlayerFields.HonorNextLevel);
            uint newHonorXP = currentHonorXP + xp;
            uint honorLevel = GetHonorLevel();

            if (xp < 1 || getLevel() < PlayerConst.LevelMinHonor || IsMaxHonorLevelAndPrestige())
                return;

            while (newHonorXP >= nextHonorLevelXP)
            {
                newHonorXP -= nextHonorLevelXP;

                if (honorLevel < PlayerConst.MaxHonorLevel)
                    SetHonorLevel((byte)(honorLevel + 1));

                honorLevel = GetHonorLevel();
                nextHonorLevelXP = GetUInt32Value(PlayerFields.HonorNextLevel);
            }

            SetUInt32Value(PlayerFields.Honor, IsMaxHonorLevelAndPrestige() ? 0 : newHonorXP);
        }

        void SetHonorLevel(byte level)
        {
            byte oldHonorLevel = (byte)GetHonorLevel();
            byte prestige = (byte)GetPrestigeLevel();
            if (level == oldHonorLevel)
                return;

            uint rewardPackID = Global.DB2Mgr.GetRewardPackIDForPvpRewardByHonorLevelAndPrestige(level, prestige);
            RewardPlayerWithRewardPack(rewardPackID);

            SetUInt32Value(PlayerFields.HonorLevel, level);
            UpdateHonorNextLevel();

            UpdateCriteria(CriteriaTypes.HonorLevelReached);

            // This code is here because no link was found between those items and this reward condition in the db2 files.
            // Interesting CriteriaTree found: Tree ids: 51140, 51156 (criteria id 31773, modifier tree id 37759)
            if (level == 50 && prestige == 1)
            {
                if (GetTeam() == Team.Alliance)
                    AddItem(138992, 1);
                else
                    AddItem(138996, 1);
            }

            if (CanPrestige())
                Prestige();
        }

        public void Prestige()
        {
            SetUInt32Value(PlayerFields.Prestige, GetPrestigeLevel() + 1);
            SetUInt32Value(PlayerFields.HonorLevel, 1);
            UpdateHonorNextLevel();

            UpdateCriteria(CriteriaTypes.PrestigeReached);
        }

        public bool CanPrestige()
        {
            if (GetSession().GetExpansion() >= Expansion.Legion && getLevel() >= PlayerConst.LevelMinHonor && GetHonorLevel() >= PlayerConst.MaxHonorLevel && GetPrestigeLevel() < Global.DB2Mgr.GetMaxPrestige())
                return true;

            return false;
        }

        bool IsMaxPrestige()
        {
            return GetPrestigeLevel() == Global.DB2Mgr.GetMaxPrestige();
        }

        void UpdateHonorNextLevel()
        {
            uint prestige = Math.Min(16 - 1, GetPrestigeLevel());
            SetUInt32Value(PlayerFields.HonorNextLevel, (uint)CliDB.HonorLevelGameTable.GetRow(GetHonorLevel()).Prestige[prestige]);
        }

        public uint GetPrestigeLevel() { return GetUInt32Value(PlayerFields.Prestige); }
        public uint GetHonorLevel() { return GetUInt32Value(PlayerFields.HonorLevel); }
        public bool IsMaxHonorLevelAndPrestige() { return IsMaxPrestige() && GetHonorLevel() == PlayerConst.MaxHonorLevel; }


        //BGs
        public Battleground GetBattleground()
        {
            if (GetBattlegroundId() == 0)
                return null;

            return Global.BattlegroundMgr.GetBattleground(GetBattlegroundId(), m_bgData.bgTypeID);
        }

        public bool InBattlegroundQueue()
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId != BattlegroundQueueTypeId.None)
                    return true;
            return false;
        }

        public BattlegroundQueueTypeId GetBattlegroundQueueTypeId(uint index)
        {
            if (index < SharedConst.MaxPlayerBGQueues)
                return m_bgBattlegroundQueueID[index].bgQueueTypeId;

            return BattlegroundQueueTypeId.None;
        }

        public uint GetBattlegroundQueueIndex(BattlegroundQueueTypeId bgQueueTypeId)
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
                    return i;
            return SharedConst.MaxPlayerBGQueues;
        }

        public bool IsInvitedForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId)
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
                    return m_bgBattlegroundQueueID[i].invitedToInstance != 0;
            return false;
        }

        public bool InBattlegroundQueueForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId)
        {
            return GetBattlegroundQueueIndex(bgQueueTypeId) < SharedConst.MaxPlayerBGQueues;
        }

        public void SetBattlegroundId(uint val, BattlegroundTypeId bgTypeId)
        {
            m_bgData.bgInstanceID = val;
            m_bgData.bgTypeID = bgTypeId;
        }

        public uint AddBattlegroundQueueId(BattlegroundQueueTypeId val)
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
            {
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId == BattlegroundQueueTypeId.None || m_bgBattlegroundQueueID[i].bgQueueTypeId == val)
                {
                    m_bgBattlegroundQueueID[i].bgQueueTypeId = val;
                    m_bgBattlegroundQueueID[i].invitedToInstance = 0;
                    m_bgBattlegroundQueueID[i].joinTime = Time.GetMSTime();
                    return i;
                }
            }
            return SharedConst.MaxPlayerBGQueues;
        }

        public bool HasFreeBattlegroundQueueId()
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId == BattlegroundQueueTypeId.None)
                    return true;
            return false;
        }

        public void RemoveBattlegroundQueueId(BattlegroundQueueTypeId val)
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
            {
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId == val)
                {
                    m_bgBattlegroundQueueID[i].bgQueueTypeId = BattlegroundQueueTypeId.None;
                    m_bgBattlegroundQueueID[i].invitedToInstance = 0;
                    m_bgBattlegroundQueueID[i].joinTime = 0;
                    return;
                }
            }
        }

        public void SetInviteForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId, uint instanceId)
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
                    m_bgBattlegroundQueueID[i].invitedToInstance = instanceId;
        }

        public bool IsInvitedForBattlegroundInstance(uint instanceId)
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
                if (m_bgBattlegroundQueueID[i].invitedToInstance == instanceId)
                    return true;
            return false;
        }

        public WorldLocation GetBattlegroundEntryPoint() { return m_bgData.joinPos; }

        public bool InBattleground() { return m_bgData.bgInstanceID != 0; }
        public uint GetBattlegroundId() { return m_bgData.bgInstanceID; }
        public BattlegroundTypeId GetBattlegroundTypeId() { return m_bgData.bgTypeID; }

        public uint GetBattlegroundQueueJoinTime(BattlegroundQueueTypeId bgQueueTypeId)
        {
            for (byte i = 0; i < SharedConst.MaxPlayerBGQueues; ++i)
                if (m_bgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId)
                    return m_bgBattlegroundQueueID[i].joinTime;
            return 0;
        }

        public bool CanUseBattlegroundObject(GameObject gameobject)
        {
            // It is possible to call this method with a null pointer, only skipping faction check.
            if (gameobject)
            {
                FactionTemplateRecord playerFaction = GetFactionTemplateEntry();
                FactionTemplateRecord faction = CliDB.FactionTemplateStorage.LookupByKey(gameobject.GetUInt32Value(GameObjectFields.Faction));

                if (playerFaction != null && faction != null && !playerFaction.IsFriendlyTo(faction))
                    return false;
            }

            // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet
            // Note: Mount, stealth and invisibility will be removed when used
            return (!isTotalImmune() &&                            // Damage immune
            !HasAura(BattlegroundConst.SpellRecentlyDroppedFlag) &&       // Still has recently held flag debuff
            IsAlive());                                    // Alive
        }

        public bool CanCaptureTowerPoint()
        {
            return (!HasStealthAura() &&                            // not stealthed
                    !HasInvisibilityAura() &&                       // not invisible
                    IsAlive());                                     // live player
        }

        public void SetBattlegroundEntryPoint()
        {
            // Taxi path store
            if (!m_taxi.empty())
            {
                m_bgData.mountSpell = 0;
                m_bgData.taxiPath[0] = m_taxi.GetTaxiSource();
                m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination();

                // On taxi we don't need check for dungeon
                m_bgData.joinPos = new WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
            }
            else
            {
                m_bgData.ClearTaxiPath();

                // Mount spell id storing
                if (IsMounted())
                {
                    var auras = GetAuraEffectsByType(AuraType.Mounted);
                    if (!auras.Empty())
                        m_bgData.mountSpell = auras[0].GetId();
                }
                else
                    m_bgData.mountSpell = 0;

                // If map is dungeon find linked graveyard
                if (GetMap().IsDungeon())
                {
                    WorldSafeLocsRecord entry = Global.ObjectMgr.GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam());
                    if (entry != null)
                        m_bgData.joinPos = new WorldLocation(entry.MapID, entry.Loc.X, entry.Loc.Y, entry.Loc.Z, 0.0f);
                    else
                        Log.outError(LogFilter.Player, "SetBattlegroundEntryPoint: Dungeon map {0} has no linked graveyard, setting home location as entry point.", GetMapId());
                }
                // If new entry point is not BG or arena set it
                else if (!GetMap().IsBattlegroundOrArena())
                    m_bgData.joinPos = new WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
            }

            if (m_bgData.joinPos.GetMapId() == 0xFFFFFFFF) // In error cases use homebind position
                m_bgData.joinPos = new WorldLocation(GetHomebind());
        }

        public void SetBGTeam(Team team)
        {
            m_bgData.bgTeam = (uint)team;
            SetByteValue(PlayerFields.Bytes4, PlayerFieldOffsets.Bytes4OffsetArenaFaction, (byte)(team == Team.Alliance ? 1 : 0));
        }

        public Team GetBGTeam()
        {
            return m_bgData.bgTeam != 0 ? (Team)m_bgData.bgTeam : GetTeam();
        }

        public void LeaveBattleground(bool teleportToEntryPoint = true)
        {
            Battleground bg = GetBattleground();
            if (bg)
            {
                bg.RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);

                // call after remove to be sure that player resurrected for correct cast
                if (bg.isBattleground() && !IsGameMaster() && WorldConfig.GetBoolValue(WorldCfg.BattlegroundCastDeserter))
                {
                    if (bg.GetStatus() == BattlegroundStatus.InProgress || bg.GetStatus() == BattlegroundStatus.WaitJoin)
                    {
                        //lets check if player was teleported from BG and schedule delayed Deserter spell cast
                        if (IsBeingTeleportedFar())
                        {
                            ScheduleDelayedOperation(PlayerDelayedOperations.SpellCastDeserter);
                            return;
                        }

                        CastSpell(this, 26013, true);               // Deserter
                    }
                }
            }
        }

        public bool CanJoinToBattleground(Battleground bg)
        {
            // check Deserter debuff
            if (HasAura(26013))
                return false;

            if (bg.isArena() && !GetSession().HasPermission(RBACPermissions.JoinArenas))
                return false;

            if (bg.IsRandom() && !GetSession().HasPermission(RBACPermissions.JoinRandomBg))
                return false;

            if (!GetSession().HasPermission(RBACPermissions.JoinNormalBg))
                return false;

            return true;
        }

        public void ClearAfkReports() { m_bgData.bgAfkReporter.Clear(); }

        bool CanReportAfkDueToLimit()
        {
            // a player can complain about 15 people per 5 minutes
            if (m_bgData.bgAfkReportedCount++ >= 15)
                return false;

            return true;
        }

        /// <summary>
        /// This player has been blamed to be inactive in a Battleground
        /// </summary>
        /// <param name="reporter"></param>
        public void ReportedAfkBy(Player reporter)
        {
            ReportPvPPlayerAFKResult reportAfkResult = new ReportPvPPlayerAFKResult();
            reportAfkResult.Offender = GetGUID();
            Battleground bg = GetBattleground();
            // Battleground also must be in progress!
            if (!bg || bg != reporter.GetBattleground() || GetTeam() != reporter.GetTeam() || bg.GetStatus() != BattlegroundStatus.InProgress)
            {
                reporter.SendPacket(reportAfkResult);
                return;
            }

            // check if player has 'Idle' or 'Inactive' debuff
            if (!m_bgData.bgAfkReporter.Contains(reporter.GetGUID()) && !HasAura(43680) && !HasAura(43681) && reporter.CanReportAfkDueToLimit())
            {
                m_bgData.bgAfkReporter.Add(reporter.GetGUID());
                // by default 3 players have to complain to apply debuff
                if (m_bgData.bgAfkReporter.Count >= WorldConfig.GetIntValue(WorldCfg.BattlegroundReportAfk))
                {
                    // cast 'Idle' spell
                    CastSpell(this, 43680, true);
                    m_bgData.bgAfkReporter.Clear();
                    reportAfkResult.NumBlackMarksOnOffender = (byte)m_bgData.bgAfkReporter.Count;
                    reportAfkResult.NumPlayersIHaveReported = reporter.m_bgData.bgAfkReportedCount;
                    reportAfkResult.Result = ReportPvPPlayerAFKResult.ResultCode.Success;
                }
            }

            reporter.SendPacket(reportAfkResult);
        }

        public bool GetRandomWinner() { return m_IsBGRandomWinner; }
        public void SetRandomWinner(bool isWinner)
        {
            m_IsBGRandomWinner = isWinner;
            if (m_IsBGRandomWinner)
            {
                PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_BATTLEGROUND_RANDOM);
                stmt.AddValue(0, GetGUID().GetCounter());
                DB.Characters.Execute(stmt);
            }
        }

        public bool GetBGAccessByLevel(BattlegroundTypeId bgTypeId)
        {
            // get a template bg instead of running one
            Battleground bg = Global.BattlegroundMgr.GetBattlegroundTemplate(bgTypeId);
            if (!bg)
                return false;

            // limit check leel to dbc compatible level range
            uint level = getLevel();
            if (level > WorldConfig.GetIntValue(WorldCfg.MaxPlayerLevel))
                level = WorldConfig.GetUIntValue(WorldCfg.MaxPlayerLevel);

            if (level < bg.GetMinLevel() || level > bg.GetMaxLevel())
                return false;

            return true;
        }

        void SendBGWeekendWorldStates()
        {
            foreach (var bl in CliDB.BattlemasterListStorage.Values)
            {
                if (bl.HolidayWorldState != 0)
                {
                    if (Global.BattlegroundMgr.IsBGWeekend((BattlegroundTypeId)bl.Id))
                        SendUpdateWorldState(bl.HolidayWorldState, 1);
                    else
                        SendUpdateWorldState(bl.HolidayWorldState, 0);
                }
            }
        }

        public void SendPvpRewards()
        {
            //WorldPacket packet(SMSG_REQUEST_PVP_REWARDS_RESPONSE, 24);
            //SendPacket(packet);
        }

        //Battlefields
        void SendBattlefieldWorldStates()
        {
            // Send misc stuff that needs to be sent on every login, like the battle timers.
            if (WorldConfig.GetBoolValue(WorldCfg.WintergraspEnable))
            {
                BattleField wg = Global.BattleFieldMgr.GetBattlefieldByBattleId(1);//Wintergrasp battle
                if (wg != null)
                {
                    SendUpdateWorldState(3801, (uint)(wg.IsWarTime() ? 0 : 1));
                    uint timer = wg.IsWarTime() ? 0 : (wg.GetTimer() / 1000); // 0 - Time to next battle
                    SendUpdateWorldState(4354, (uint)(Time.UnixTime + timer));
                }
            }
        }

        //Arenas
        public void SetArenaTeamInfoField(byte slot, ArenaTeamInfoType type, uint value)
        {
            SetUInt32Value(PlayerFields.ArenaTeamInfo11 + (slot * (int)ArenaTeamInfoType.End) + (int)type, value);
        }

        public void SetInArenaTeam(uint ArenaTeamId, byte slot, byte type)
        {
            SetArenaTeamInfoField(slot, ArenaTeamInfoType.Id, ArenaTeamId);
            SetArenaTeamInfoField(slot, ArenaTeamInfoType.Type, type);
        }

        public static uint GetArenaTeamIdFromDB(ObjectGuid guid, byte type)
        {
            PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ARENA_TEAM_ID_BY_PLAYER_GUID);
            stmt.AddValue(0, guid.GetCounter());
            stmt.AddValue(1, type);
            SQLResult result = DB.Characters.Query(stmt);

            if (result.IsEmpty())
                return 0;

            return result.Read<uint>(0);
        }

        public static void LeaveAllArenaTeams(ObjectGuid guid)
        {
            PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PLAYER_ARENA_TEAMS);
            stmt.AddValue(0, guid.GetCounter());
            SQLResult result = DB.Characters.Query(stmt);

            if (result.IsEmpty())
                return;

            do
            {
                uint arenaTeamId = result.Read<uint>(0);
                if (arenaTeamId != 0)
                {
                    ArenaTeam arenaTeam = Global.ArenaTeamMgr.GetArenaTeamById(arenaTeamId);
                    if (arenaTeam != null)
                        arenaTeam.DelMember(guid, true);
                }
            }
            while (result.NextRow());
        }
        public uint GetArenaTeamId(byte slot) { return GetUInt32Value(PlayerFields.ArenaTeamInfo11 + (slot * (int)ArenaTeamInfoType.End) + (int)ArenaTeamInfoType.Id); }
        public uint GetArenaPersonalRating(byte slot) { return GetUInt32Value(PlayerFields.ArenaTeamInfo11 + (slot * (int)ArenaTeamInfoType.End) + (int)ArenaTeamInfoType.PersonalRating); }
        public void SetArenaTeamIdInvited(uint ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; }
        public uint GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; }
        public uint GetRBGPersonalRating() { return 0; }

        //OutdoorPVP
        public bool IsOutdoorPvPActive()
        {
            return IsAlive() && !HasInvisibilityAura() && !HasStealthAura() && IsPvP() && !HasUnitMovementFlag(MovementFlag.Flying) && !IsInFlight();
        }
        public OutdoorPvP GetOutdoorPvP()
        {
            return Global.OutdoorPvPMgr.GetOutdoorPvPToZoneId(GetZoneId());
        }
    }
}
