/*
 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
 *
 * 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, see .
 */
#include "ReputationMgr.h"
#include "CharacterPackets.h"
#include "DatabaseEnv.h"
#include "DB2Stores.h"
#include "Language.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ReputationPackets.h"
#include "ScriptMgr.h"
#include "World.h"
#include "WorldSession.h"
uint32 const ReputationRankStrIndex[MAX_REPUTATION_RANK] =
{
    LANG_REP_HATED,    LANG_REP_HOSTILE, LANG_REP_UNFRIENDLY, LANG_REP_NEUTRAL,
    LANG_REP_FRIENDLY, LANG_REP_HONORED, LANG_REP_REVERED,    LANG_REP_EXALTED
};
std::set const ReputationMgr::ReputationRankThresholds =
{
    -42000,
    // Hated
    -6000,
    // Hostile
    -3000,
    // Unfriendly
    0,
    // Neutral
    3000,
    // Friendly
    9000,
    // Honored
    21000,
    // Revered
    42000
    // Exalted
};
const int32 ReputationMgr::Reputation_Cap = 42000;
const int32 ReputationMgr::Reputation_Bottom = -42000;
template
static int32 ReputationToRankHelper(std::set const& thresholds, int32 standing, F thresholdExtractor)
{
    auto itr = thresholds.begin();
    auto end = thresholds.end();
    int32 rank = -1;
    while (itr != end && standing >= thresholdExtractor(*itr))
    {
        ++rank;
        ++itr;
    }
    return rank;
}
ReputationRank ReputationMgr::ReputationToRank(FactionEntry const* factionEntry, int32 standing)
{
    int32 rank = MIN_REPUTATION_RANK;
    if (DB2Manager::FriendshipRepReactionSet const* friendshipReactions = sDB2Manager.GetFriendshipRepReactions(factionEntry->FriendshipRepID))
        rank = ReputationToRankHelper(*friendshipReactions, standing, [](FriendshipRepReactionEntry const* frr) { return frr->ReactionThreshold; });
    else
        rank = ReputationToRankHelper(ReputationRankThresholds, standing, [](int32 threshold) { return threshold; });
    return ReputationRank(rank);
}
FactionState const* ReputationMgr::GetState(FactionEntry const* factionEntry) const
{
    return factionEntry->CanHaveReputation() ? GetState(factionEntry->ReputationIndex) : nullptr;
}
bool ReputationMgr::IsAtWar(uint32 faction_id) const
{
    FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
    if (!factionEntry)
    {
        TC_LOG_ERROR("misc", "ReputationMgr::IsAtWar: Can't get AtWar flag of %s for unknown faction (faction id) #%u.", _player->GetName().c_str(), faction_id);
        return false;
    }
    return IsAtWar(factionEntry);
}
bool ReputationMgr::IsAtWar(FactionEntry const* factionEntry) const
{
    if (!factionEntry)
        return false;
    if (FactionState const* factionState = GetState(factionEntry))
        return factionState->Flags.HasFlag(ReputationFlags::AtWar);
    return false;
}
int32 ReputationMgr::GetReputation(uint32 faction_id) const
{
    FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
    if (!factionEntry)
    {
        TC_LOG_ERROR("misc", "ReputationMgr::GetReputation: Can't get reputation of %s for unknown faction (faction id) #%u.", _player->GetName().c_str(), faction_id);
        return 0;
    }
    return GetReputation(factionEntry);
}
int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const
{
    int32 dataIndex = GetFactionDataIndexForRaceAndClass(factionEntry);
    if (dataIndex < 0)
        return 0;
    return factionEntry->ReputationBase[dataIndex];
}
int32 ReputationMgr::GetMinReputation(FactionEntry const* factionEntry) const
{
    if (DB2Manager::FriendshipRepReactionSet const* friendshipReactions = sDB2Manager.GetFriendshipRepReactions(factionEntry->FriendshipRepID))
        return (*friendshipReactions->begin())->ReactionThreshold;
    return *ReputationRankThresholds.begin();
}
int32 ReputationMgr::GetMaxReputation(FactionEntry const* factionEntry) const
{
    if (ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(factionEntry->ID))
    {
        // has reward quest, cap is just before threshold for another quest reward
        // for example: if current reputation is 12345 and questa are given every 10000 and player has unclaimed reward
        // then cap will be 19999
        // otherwise cap is one theshold level larger
        // if current reputation is 12345 and questa are given every 10000 and player does NOT have unclaimed reward
        // then cap will be 29999
        int32 reputation = GetReputation(factionEntry);
        int32 cap = reputation + paragonReputation->LevelThreshold - reputation % paragonReputation->LevelThreshold - 1;
        if (_player->GetQuestStatus(paragonReputation->QuestID) == QUEST_STATUS_NONE)
            cap += paragonReputation->LevelThreshold;
        return cap;
    }
    if (DB2Manager::FriendshipRepReactionSet const* friendshipReactions = sDB2Manager.GetFriendshipRepReactions(factionEntry->FriendshipRepID))
        return (*friendshipReactions->rbegin())->ReactionThreshold;
    int32 dataIndex = GetFactionDataIndexForRaceAndClass(factionEntry);
    if (dataIndex >= 0)
        return factionEntry->ReputationMax[dataIndex];
    return *ReputationRankThresholds.rbegin();
}
int32 ReputationMgr::GetReputation(FactionEntry const* factionEntry) const
{
    // Faction without recorded reputation. Just ignore.
    if (!factionEntry)
        return 0;
    if (FactionState const* state = GetState(factionEntry))
        return GetBaseReputation(factionEntry) + state->Standing;
    return 0;
}
ReputationRank ReputationMgr::GetRank(FactionEntry const* factionEntry) const
{
    int32 reputation = GetReputation(factionEntry);
    return ReputationToRank(factionEntry, reputation);
}
ReputationRank ReputationMgr::GetBaseRank(FactionEntry const* factionEntry) const
{
    int32 reputation = GetBaseReputation(factionEntry);
    return ReputationToRank(factionEntry, reputation);
}
std::string ReputationMgr::GetReputationRankName(FactionEntry const* factionEntry) const
{
    ReputationRank rank = GetRank(factionEntry);
    if (!factionEntry->FriendshipRepID)
        return sObjectMgr->GetTrinityString(ReputationRankStrIndex[GetRank(factionEntry)], _player->GetSession()->GetSessionDbcLocale());
    if (DB2Manager::FriendshipRepReactionSet const* friendshipReactions = sDB2Manager.GetFriendshipRepReactions(factionEntry->FriendshipRepID))
    {
        auto itr = friendshipReactions->begin();
        std::advance(itr, uint32(rank));
        return (*itr)->Reaction[_player->GetSession()->GetSessionDbcLocale()];
    }
    return "";
}
ReputationRank const* ReputationMgr::GetForcedRankIfAny(FactionTemplateEntry const* factionTemplateEntry) const
{
    return GetForcedRankIfAny(factionTemplateEntry->Faction);
}
int32 ReputationMgr::GetParagonLevel(uint32 paragonFactionId) const
{
    return GetParagonLevel(sFactionStore.LookupEntry(paragonFactionId));
}
int32 ReputationMgr::GetParagonLevel(FactionEntry const* paragonFactionEntry) const
{
    if (!paragonFactionEntry)
        return 0;
    if (ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(paragonFactionEntry->ID))
        return GetReputation(paragonFactionEntry) / paragonReputation->LevelThreshold;
    return 0;
}
void ReputationMgr::ApplyForceReaction(uint32 faction_id, ReputationRank rank, bool apply)
{
    if (apply)
        _forcedReactions[faction_id] = rank;
    else
        _forcedReactions.erase(faction_id);
}
ReputationFlags ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) const
{
    ReputationFlags flags = [&]()
    {
        int32 dataIndex = GetFactionDataIndexForRaceAndClass(factionEntry);
        if (dataIndex < 0)
            return ReputationFlags::None;
        return static_cast(factionEntry->ReputationFlags[dataIndex]);
    }();
    if (sDB2Manager.GetParagonReputation(factionEntry->ID))
        flags |= ReputationFlags::ShowPropagated;
    return flags;
}
void ReputationMgr::SendForceReactions()
{
    WorldPackets::Reputation::SetForcedReactions setForcedReactions;
    setForcedReactions.Reactions.resize(_forcedReactions.size());
    std::size_t i = 0;
    for (ForcedReactions::const_iterator itr = _forcedReactions.begin(); itr != _forcedReactions.end(); ++itr)
    {
        WorldPackets::Reputation::ForcedReaction& forcedReaction = setForcedReactions.Reactions[i++];
        forcedReaction.Faction = int32(itr->first);
        forcedReaction.Reaction = int32(itr->second);
    }
    _player->SendDirectMessage(setForcedReactions.Write());
}
void ReputationMgr::SendState(FactionState const* faction)
{
    WorldPackets::Reputation::SetFactionStanding setFactionStanding;
    setFactionStanding.ReferAFriendBonus = 0.0f;
    setFactionStanding.BonusFromAchievementSystem = 0.0f;
    if (faction)
        setFactionStanding.Faction.emplace_back(int32(faction->ReputationListID), faction->Standing);
    for (FactionStateList::iterator itr = _factions.begin(); itr != _factions.end(); ++itr)
    {
        if (itr->second.needSend)
        {
            itr->second.needSend = false;
            if (!faction || itr->second.ReputationListID != faction->ReputationListID)
                setFactionStanding.Faction.emplace_back(int32(itr->second.ReputationListID), itr->second.Standing);
        }
    }
    setFactionStanding.ShowVisual = _sendFactionIncreased;
    _player->SendDirectMessage(setFactionStanding.Write());
    _sendFactionIncreased = false; // Reset
}
void ReputationMgr::SendInitialReputations()
{
    WorldPackets::Reputation::InitializeFactions initFactions;
    for (FactionStateList::iterator itr = _factions.begin(); itr != _factions.end(); ++itr)
    {
        initFactions.FactionFlags[itr->first] = itr->second.Flags.AsUnderlyingType();
        initFactions.FactionStandings[itr->first] = itr->second.Standing;
        /// @todo faction bonus
        itr->second.needSend = false;
    }
    _player->SendDirectMessage(initFactions.Write());
}
void ReputationMgr::SendVisible(FactionState const* faction, bool visible) const
{
    if (_player->GetSession()->PlayerLoading())
        return;
    // make faction visible/not visible in reputation list at client
    WorldPackets::Character::SetFactionVisible packet(visible);
    packet.FactionIndex = faction->ReputationListID;
    _player->SendDirectMessage(packet.Write());
}
void ReputationMgr::Initialize()
{
    _factions.clear();
    _visibleFactionCount = 0;
    _honoredFactionCount = 0;
    _reveredFactionCount = 0;
    _exaltedFactionCount = 0;
    _sendFactionIncreased = false;
    for (FactionEntry const* factionEntry : sFactionStore)
    {
        if (factionEntry->CanHaveReputation())
        {
            FactionState newFaction;
            newFaction.ID = factionEntry->ID;
            newFaction.ReputationListID = factionEntry->ReputationIndex;
            newFaction.Standing = 0;
            newFaction.Flags = GetDefaultStateFlags(factionEntry);
            newFaction.needSend = true;
            newFaction.needSave = true;
            if (newFaction.Flags.HasFlag(ReputationFlags::Visible))
                ++_visibleFactionCount;
            if (!factionEntry->FriendshipRepID)
                UpdateRankCounters(REP_HOSTILE, GetBaseRank(factionEntry));
            _factions[newFaction.ReputationListID] = newFaction;
        }
    }
}
bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental, bool spillOverOnly, bool noSpillover)
{
    sScriptMgr->OnPlayerReputationChange(_player, factionEntry->ID, standing, incremental);
    bool res = false;
    if (!noSpillover)
    {
        // if spillover definition exists in DB, override DBC
        if (RepSpilloverTemplate const* repTemplate = sObjectMgr->GetRepSpilloverTemplate(factionEntry->ID))
        {
            for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
            {
                if (repTemplate->faction[i])
                {
                    if (_player->GetReputationRank(repTemplate->faction[i]) <= ReputationRank(repTemplate->faction_rank[i]))
                    {
                        // bonuses are already given, so just modify standing by rate
                        int32 spilloverRep = int32(standing * repTemplate->faction_rate[i]);
                        SetOneFactionReputation(sFactionStore.AssertEntry(repTemplate->faction[i]), spilloverRep, incremental);
                    }
                }
            }
        }
        else
        {
            float spillOverRepOut = float(standing);
            // check for sub-factions that receive spillover
            std::vector const* flist = sDB2Manager.GetFactionTeamList(factionEntry->ID);
            // if has no sub-factions, check for factions with same parent
            if (!flist && factionEntry->ParentFactionID && factionEntry->ParentFactionMod[1] != 0.0f)
            {
                spillOverRepOut *= factionEntry->ParentFactionMod[1];
                if (FactionEntry const* parent = sFactionStore.LookupEntry(factionEntry->ParentFactionID))
                {
                    FactionStateList::iterator parentState = _factions.find(parent->ReputationIndex);
                    // some team factions have own reputation standing, in this case do not spill to other sub-factions
                    if (parentState != _factions.end() && parentState->second.Flags.HasFlag(ReputationFlags::HeaderShowsBar))
                    {
                        SetOneFactionReputation(parent, int32(spillOverRepOut), incremental);
                    }
                    else    // spill to "sister" factions
                    {
                        flist = sDB2Manager.GetFactionTeamList(factionEntry->ParentFactionID);
                    }
                }
            }
            if (flist)
            {
                // Spillover to affiliated factions
                for (std::vector::const_iterator itr = flist->begin(); itr != flist->end(); ++itr)
                {
                    if (FactionEntry const* factionEntryCalc = sFactionStore.LookupEntry(*itr))
                    {
                        if (factionEntryCalc == factionEntry || GetRank(factionEntryCalc) > ReputationRank(factionEntryCalc->ParentFactionCap[0]))
                            continue;
                        int32 spilloverRep = int32(spillOverRepOut * factionEntryCalc->ParentFactionMod[0]);
                        if (spilloverRep != 0 || !incremental)
                            res = SetOneFactionReputation(factionEntryCalc, spilloverRep, incremental);
                    }
                }
            }
        }
    }
    // spillover done, update faction itself
    FactionStateList::iterator faction = _factions.find(factionEntry->ReputationIndex);
    if (faction != _factions.end())
    {
        FactionEntry const* primaryFactionToModify = factionEntry;
        if (incremental && standing > 0 && CanGainParagonReputationForFaction(factionEntry))
        {
            primaryFactionToModify = sFactionStore.AssertEntry(factionEntry->ParagonFactionID);
            faction = _factions.find(primaryFactionToModify->ReputationIndex);
        }
        if (faction != _factions.end())
        {
            // if we update spillover only, do not update main reputation (rank exceeds creature reward rate)
            if (!spillOverOnly)
                res = SetOneFactionReputation(primaryFactionToModify, standing, incremental);
            // only this faction gets reported to client, even if it has no own visible standing
            SendState(&faction->second);
        }
    }
    return res;
}
bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing, bool incremental)
{
    FactionStateList::iterator itr = _factions.find(factionEntry->ReputationIndex);
    if (itr != _factions.end())
    {
        int32 BaseRep = GetBaseReputation(factionEntry);
        if (incremental)
        {
            // int32 *= float cause one point loss?
            standing = int32(floor((float)standing * sWorld->getRate(RATE_REPUTATION_GAIN) + 0.5f));
            standing += itr->second.Standing + BaseRep;
        }
        if (standing > GetMaxReputation(factionEntry))
            standing = GetMaxReputation(factionEntry);
        else if (standing < GetMinReputation(factionEntry))
            standing = GetMinReputation(factionEntry);
        ReputationRank old_rank = ReputationToRank(factionEntry, itr->second.Standing + BaseRep);
        ReputationRank new_rank = ReputationToRank(factionEntry, standing);
        int32 oldStanding = itr->second.Standing + BaseRep;
        int32 newStanding = standing - BaseRep;
        _player->ReputationChanged(factionEntry, newStanding - itr->second.Standing);
        itr->second.Standing = newStanding;
        itr->second.needSend = true;
        itr->second.needSave = true;
        SetVisible(&itr->second);
        if (new_rank <= REP_HOSTILE)
            SetAtWar(&itr->second, true);
        if (new_rank > old_rank)
            _sendFactionIncreased = true;
        ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(factionEntry->ID);
        if (paragonReputation)
        {
            int32 oldParagonLevel = oldStanding / paragonReputation->LevelThreshold;
            int32 newParagonLevel = standing / paragonReputation->LevelThreshold;
            if (oldParagonLevel != newParagonLevel)
                if (Quest const* paragonRewardQuest = sObjectMgr->GetQuestTemplate(paragonReputation->QuestID))
                    _player->AddQuestAndCheckCompletion(paragonRewardQuest, nullptr);
        }
        if (!factionEntry->FriendshipRepID && !paragonReputation)
            UpdateRankCounters(old_rank, new_rank);
        _player->UpdateCriteria(CriteriaType::TotalFactionsEncountered, factionEntry->ID);
        _player->UpdateCriteria(CriteriaType::ReputationGained,         factionEntry->ID);
        _player->UpdateCriteria(CriteriaType::TotalExaltedFactions,     factionEntry->ID);
        _player->UpdateCriteria(CriteriaType::TotalReveredFactions,     factionEntry->ID);
        _player->UpdateCriteria(CriteriaType::TotalHonoredFactions,     factionEntry->ID);
        return true;
    }
    return false;
}
void ReputationMgr::SetVisible(FactionTemplateEntry const* factionTemplateEntry)
{
    if (!factionTemplateEntry->Faction)
        return;
    if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->Faction))
        // Never show factions of the opposing team
        if (!(factionEntry->ReputationRaceMask[1].HasRace(_player->GetRace()) && factionEntry->ReputationBase[1] == Reputation_Bottom))
            SetVisible(factionEntry);
}
void ReputationMgr::SetVisible(FactionEntry const* factionEntry)
{
    if (!factionEntry->CanHaveReputation())
        return;
    FactionStateList::iterator itr = _factions.find(factionEntry->ReputationIndex);
    if (itr == _factions.end())
        return;
    SetVisible(&itr->second);
}
void ReputationMgr::SetVisible(FactionState* faction)
{
    // always invisible or hidden faction can't be make visible
    if (faction->Flags.HasFlag(ReputationFlags::Hidden))
        return;
    if (faction->Flags.HasFlag(ReputationFlags::Header) && !faction->Flags.HasFlag(ReputationFlags::HeaderShowsBar))
        return;
    if (sDB2Manager.GetParagonReputation(faction->ID))
        return;
    // already set
    if (faction->Flags.HasFlag(ReputationFlags::Visible))
        return;
    faction->Flags |= ReputationFlags::Visible;
    faction->needSend = true;
    faction->needSave = true;
    ++_visibleFactionCount;
    SendVisible(faction);
}
void ReputationMgr::SetAtWar(RepListID repListID, bool on)
{
    FactionStateList::iterator itr = _factions.find(repListID);
    if (itr == _factions.end())
        return;
    // always invisible or hidden faction can't change war state
    if (itr->second.Flags.HasFlag(ReputationFlags::Hidden | ReputationFlags::Header))
        return;
    SetAtWar(&itr->second, on);
}
void ReputationMgr::SetAtWar(FactionState* faction, bool atWar) const
{
    // Do not allow to declare war to our own faction. But allow for rival factions (eg Aldor vs Scryer).
    if (atWar && faction->Flags.HasFlag(ReputationFlags::Peaceful) && GetRank(sFactionStore.AssertEntry(faction->ID)) > REP_HATED)
        return;
    // already set
    if (faction->Flags.HasFlag(ReputationFlags::AtWar) == atWar)
        return;
    if (atWar)
        faction->Flags |= ReputationFlags::AtWar;
    else
        faction->Flags &= ~ReputationFlags::AtWar;
    faction->needSend = true;
    faction->needSave = true;
}
void ReputationMgr::SetInactive(RepListID repListID, bool on)
{
    FactionStateList::iterator itr = _factions.find(repListID);
    if (itr == _factions.end())
        return;
    SetInactive(&itr->second, on);
}
void ReputationMgr::SetInactive(FactionState* faction, bool inactive) const
{
    // always invisible or hidden faction can't be inactive
    if (faction->Flags.HasFlag(ReputationFlags::Hidden | ReputationFlags::Header) || !faction->Flags.HasFlag(ReputationFlags::Visible))
        return;
    // already set
    if (faction->Flags.HasFlag(ReputationFlags::Inactive) == inactive)
        return;
    if (inactive)
        faction->Flags |= ReputationFlags::Inactive;
    else
        faction->Flags &= ~ReputationFlags::Inactive;
    faction->needSend = true;
    faction->needSave = true;
}
void ReputationMgr::LoadFromDB(PreparedQueryResult result)
{
    // Set initial reputations (so everything is nifty before DB data load)
    Initialize();
    //QueryResult* result = CharacterDatabase.PQuery("SELECT faction, standing, flags FROM character_reputation WHERE guid = '%u'", GetGUIDLow());
    if (result)
    {
        do
        {
            Field* fields = result->Fetch();
            FactionEntry const* factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt16());
            if (factionEntry && factionEntry->CanHaveReputation())
            {
                FactionState* faction = &_factions[factionEntry->ReputationIndex];
                // update standing to current
                faction->Standing = fields[1].GetInt32();
                // update counters
                if (!factionEntry->FriendshipRepID)
                {
                    int32 BaseRep = GetBaseReputation(factionEntry);
                    ReputationRank old_rank = ReputationToRank(factionEntry, BaseRep);
                    ReputationRank new_rank = ReputationToRank(factionEntry, BaseRep + faction->Standing);
                    UpdateRankCounters(old_rank, new_rank);
                }
                EnumFlag dbFactionFlags = static_cast(fields[2].GetUInt16());
                if (dbFactionFlags.HasFlag(ReputationFlags::Visible))
                    SetVisible(faction);                                    // have internal checks for forced invisibility
                if (dbFactionFlags.HasFlag(ReputationFlags::Inactive))
                    SetInactive(faction, true);                             // have internal checks for visibility requirement
                if (dbFactionFlags.HasFlag(ReputationFlags::AtWar))    // DB at war
                    SetAtWar(faction, true);                                // have internal checks for ReputationFlags::Peaceful
                else                                                        // DB not at war
                {
                    // allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN)
                    if (faction->Flags.HasFlag(ReputationFlags::Visible))
                        SetAtWar(faction, false);                           // have internal checks for ReputationFlags::Peaceful
                }
                // set atWar for hostile
                if (GetRank(factionEntry) <= REP_HOSTILE)
                    SetAtWar(faction, true);
                // reset changed flag if values similar to saved in DB
                if (faction->Flags == dbFactionFlags)
                {
                    faction->needSend = false;
                    faction->needSave = false;
                }
            }
        }
        while (result->NextRow());
    }
}
void ReputationMgr::SaveToDB(CharacterDatabaseTransaction trans)
{
    for (FactionStateList::iterator itr = _factions.begin(); itr != _factions.end(); ++itr)
    {
        if (itr->second.needSave)
        {
            CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_REPUTATION_BY_FACTION);
            stmt->setUInt64(0, _player->GetGUID().GetCounter());
            stmt->setUInt16(1, uint16(itr->second.ID));
            trans->Append(stmt);
            stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_REPUTATION_BY_FACTION);
            stmt->setUInt64(0, _player->GetGUID().GetCounter());
            stmt->setUInt16(1, uint16(itr->second.ID));
            stmt->setInt32(2, itr->second.Standing);
            stmt->setUInt16(3, itr->second.Flags.AsUnderlyingType());
            trans->Append(stmt);
            itr->second.needSave = false;
        }
    }
}
void ReputationMgr::UpdateRankCounters(ReputationRank old_rank, ReputationRank new_rank)
{
    if (old_rank >= REP_EXALTED)
        --_exaltedFactionCount;
    if (old_rank >= REP_REVERED)
        --_reveredFactionCount;
    if (old_rank >= REP_HONORED)
        --_honoredFactionCount;
    if (new_rank >= REP_EXALTED)
        ++_exaltedFactionCount;
    if (new_rank >= REP_REVERED)
        ++_reveredFactionCount;
    if (new_rank >= REP_HONORED)
        ++_honoredFactionCount;
}
int32 ReputationMgr::GetFactionDataIndexForRaceAndClass(FactionEntry const* factionEntry) const
{
    if (!factionEntry)
        return -1;
    uint8 race = _player->GetRace();
    uint32 classMask = _player->GetClassMask();
    for (int32 i = 0; i < 4; i++)
    {
        if ((factionEntry->ReputationRaceMask[i].HasRace(race) || (factionEntry->ReputationRaceMask[i].IsEmpty() && factionEntry->ReputationClassMask[i] != 0))
            && (factionEntry->ReputationClassMask[i] & classMask || factionEntry->ReputationClassMask[i] == 0))
            return i;
    }
    return -1;
}
bool ReputationMgr::CanGainParagonReputationForFaction(FactionEntry const* factionEntry) const
{
    if (!sFactionStore.LookupEntry(factionEntry->ParagonFactionID))
        return false;
    if (GetRank(factionEntry) != REP_EXALTED)
        return false;
    ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(factionEntry->ParagonFactionID);
    if (!paragonReputation)
        return false;
    Quest const* quest = sObjectMgr->GetQuestTemplate(paragonReputation->QuestID);
    if (!quest)
        return false;
    return _player->GetLevel() >= _player->GetQuestMinLevel(quest);
}