/*
 * Copyright (C) 2008-2018 TrinityCore 
 * Copyright (C) 2005-2009 MaNGOS 
 *
 * 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 "QuestDef.h"
#include "DB2Stores.h"
#include "Field.h"
#include "GameTables.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "QuestPackets.h"
#include "World.h"
Quest::Quest(Field* questRecord)
{
    EmoteOnIncomplete = 0;
    EmoteOnComplete = 0;
    _rewItemsCount = 0;
    _rewChoiceItemsCount = 0;
    _rewCurrencyCount = 0;
    ID = questRecord[0].GetUInt32();
    Type = questRecord[1].GetUInt8();
    Level = questRecord[2].GetInt32();
    PackageID = questRecord[3].GetUInt32();
    MinLevel = questRecord[4].GetInt32();
    QuestSortID = questRecord[5].GetInt16();
    QuestInfoID = questRecord[6].GetUInt16();
    SuggestedPlayers = questRecord[7].GetUInt8();
    NextQuestInChain = questRecord[8].GetUInt32();
    RewardXPDifficulty = questRecord[9].GetUInt32();
    RewardXPMultiplier = questRecord[10].GetFloat();
    RewardMoney = questRecord[11].GetUInt32();
    RewardMoneyDifficulty = questRecord[12].GetUInt32();
    RewardMoneyMultiplier = questRecord[13].GetFloat();
    RewardBonusMoney = questRecord[14].GetUInt32();
    for (uint32 i = 0; i < QUEST_REWARD_DISPLAY_SPELL_COUNT; ++i)
        RewardDisplaySpell[i] = questRecord[15 + i].GetUInt32();
    RewardSpell = questRecord[18].GetUInt32();
    RewardHonor = questRecord[19].GetUInt32();
    RewardKillHonor = questRecord[20].GetUInt32();
    SourceItemId = questRecord[21].GetUInt32();
    RewardArtifactXPDifficulty = questRecord[22].GetUInt32();
    RewardArtifactXPMultiplier = questRecord[23].GetFloat();
    RewardArtifactCategoryID = questRecord[24].GetUInt32();
    Flags = questRecord[25].GetUInt32();
    FlagsEx = questRecord[26].GetUInt32();
    for (uint32 i = 0; i < QUEST_ITEM_DROP_COUNT; ++i)
    {
        RewardItemId[i] = questRecord[27 + i * 4].GetUInt32();
        RewardItemCount[i] = questRecord[28 + i * 4].GetUInt32();
        ItemDrop[i] = questRecord[29 + i * 4].GetUInt32();
        ItemDropQuantity[i] = questRecord[30 + i * 4].GetUInt32();
        if (RewardItemId[i])
            ++_rewItemsCount;
    }
    for (uint32 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
    {
        RewardChoiceItemId[i] = questRecord[43 + i * 3].GetUInt32();
        RewardChoiceItemCount[i] = questRecord[44 + i * 3].GetUInt32();
        RewardChoiceItemDisplayId[i] = questRecord[45 + i * 3].GetUInt32();
        if (RewardChoiceItemId[i])
            ++_rewChoiceItemsCount;
    }
    POIContinent = questRecord[61].GetUInt32();
    POIx = questRecord[62].GetFloat();
    POIy = questRecord[63].GetFloat();
    POIPriority = questRecord[64].GetUInt32();
    RewardTitleId = questRecord[65].GetUInt32();
    RewardArenaPoints = questRecord[66].GetUInt32();
    RewardSkillId = questRecord[67].GetUInt32();
    RewardSkillPoints = questRecord[68].GetUInt32();
    QuestGiverPortrait = questRecord[69].GetUInt32();
    QuestTurnInPortrait = questRecord[70].GetUInt32();
    for (uint32 i = 0; i < QUEST_REWARD_REPUTATIONS_COUNT; ++i)
    {
        RewardFactionId[i] = questRecord[71 + i * 4].GetUInt32();
        RewardFactionValue[i] = questRecord[72 + i * 4].GetInt32();
        RewardFactionOverride[i] = questRecord[73 + i * 4].GetInt32();
        RewardFactionCapIn[i] = questRecord[74 + i * 4].GetUInt32();
    }
    RewardReputationMask = questRecord[91].GetUInt32();
    for (uint32 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i)
    {
        RewardCurrencyId[i] = questRecord[92 + i * 2].GetUInt32();
        RewardCurrencyCount[i] = questRecord[93 + i * 2].GetUInt32();
        if (RewardCurrencyId[i])
            ++_rewCurrencyCount;
    }
    SoundAccept = questRecord[100].GetUInt32();
    SoundTurnIn = questRecord[101].GetUInt32();
    AreaGroupID = questRecord[102].GetUInt32();
    LimitTime = questRecord[103].GetUInt32();
    AllowableRaces = questRecord[104].GetInt32();
    QuestRewardID = questRecord[105].GetUInt32();
    Expansion = questRecord[106].GetInt32();
    LogTitle = questRecord[107].GetString();
    LogDescription = questRecord[108].GetString();
    QuestDescription = questRecord[109].GetString();
    AreaDescription = questRecord[110].GetString();
    PortraitGiverText = questRecord[111].GetString();
    PortraitGiverName = questRecord[112].GetString();
    PortraitTurnInText = questRecord[113].GetString();
    PortraitTurnInName = questRecord[114].GetString();
    QuestCompletionLog = questRecord[115].GetString();
    for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
    {
        DetailsEmote[i] = 0;
        DetailsEmoteDelay[i] = 0;
        OfferRewardEmote[i] = 0;
        OfferRewardEmoteDelay[i] = 0;
    }
}
void Quest::LoadQuestDetails(Field* fields)
{
    for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
    {
        if (!sEmotesStore.LookupEntry(fields[1 + i].GetUInt16()))
        {
            TC_LOG_ERROR("sql.sql", "Table `quest_details` has non-existing Emote%i (%u) set for quest %u. Skipped.", 1+i, fields[1+i].GetUInt16(), fields[0].GetUInt32());
            continue;
        }
        DetailsEmote[i] = fields[1 + i].GetUInt16();
    }
    for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
        DetailsEmoteDelay[i] = fields[5 + i].GetUInt32();
}
void Quest::LoadQuestRequestItems(Field* fields)
{
    EmoteOnComplete = fields[1].GetUInt16();
    EmoteOnIncomplete = fields[2].GetUInt16();
    if (!sEmotesStore.LookupEntry(EmoteOnComplete))
        TC_LOG_ERROR("sql.sql", "Table `quest_request_items` has non-existing EmoteOnComplete (%u) set for quest %u.", EmoteOnComplete, fields[0].GetUInt32());
    if (!sEmotesStore.LookupEntry(EmoteOnIncomplete))
        TC_LOG_ERROR("sql.sql", "Table `quest_request_items` has non-existing EmoteOnIncomplete (%u) set for quest %u.", EmoteOnIncomplete, fields[0].GetUInt32());
    EmoteOnCompleteDelay = fields[3].GetUInt32();
    EmoteOnIncompleteDelay = fields[4].GetUInt32();
    RequestItemsText = fields[5].GetString();
}
void Quest::LoadQuestOfferReward(Field* fields)
{
    for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
    {
        if (!sEmotesStore.LookupEntry(fields[1 + i].GetUInt16()))
        {
            TC_LOG_ERROR("sql.sql", "Table `quest_offer_reward` has non-existing Emote%i (%u) set for quest %u. Skipped.", 1+i, fields[1+i].GetUInt16(), fields[0].GetUInt32());
            continue;
        }
        OfferRewardEmote[i] = fields[1 + i].GetUInt16();
    }
    for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
        OfferRewardEmoteDelay[i] = fields[5 + i].GetUInt32();
    OfferRewardText = fields[9].GetString();
}
void Quest::LoadQuestTemplateAddon(Field* fields)
{
    MaxLevel = fields[1].GetUInt8();
    AllowableClasses = fields[2].GetUInt32();
    SourceSpellID = fields[3].GetUInt32();
    PrevQuestID = fields[4].GetInt32();
    NextQuestID = fields[5].GetInt32();
    ExclusiveGroup = fields[6].GetInt32();
    RewardMailTemplateId = fields[7].GetUInt32();
    RewardMailDelay = fields[8].GetUInt32();
    RequiredSkillId = fields[9].GetUInt16();
    RequiredSkillPoints = fields[10].GetUInt16();
    RequiredMinRepFaction = fields[11].GetUInt16();
    RequiredMaxRepFaction = fields[12].GetUInt16();
    RequiredMinRepValue = fields[13].GetInt32();
    RequiredMaxRepValue = fields[14].GetInt32();
    SourceItemIdCount = fields[15].GetUInt8();
    RewardMailSenderEntry = fields[16].GetUInt32();
    SpecialFlags = fields[17].GetUInt8();
    ScriptId = sObjectMgr->GetScriptId(fields[18].GetString());
    if (SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT)
        Flags |= QUEST_FLAGS_AUTO_ACCEPT;
}
void Quest::LoadQuestObjective(Field* fields)
{
    QuestObjective obj;
    obj.ID = fields[0].GetUInt32();
    obj.QuestID = fields[1].GetUInt32();
    obj.Type = fields[2].GetUInt8();
    obj.StorageIndex = fields[3].GetInt8();
    obj.ObjectID = fields[4].GetInt32();
    obj.Amount = fields[5].GetInt32();
    obj.Flags = fields[6].GetUInt32();
    obj.Flags2 = fields[7].GetUInt32();
    obj.ProgressBarWeight = fields[8].GetFloat();
    obj.Description = fields[9].GetString();
    Objectives.push_back(obj);
}
void Quest::LoadQuestObjectiveVisualEffect(Field* fields)
{
    uint32 objID = fields[1].GetUInt32();
    for (QuestObjective& obj : Objectives)
    {
        if (obj.ID == objID)
        {
            uint8 effectIndex = fields[3].GetUInt8();
            if (effectIndex >= obj.VisualEffects.size())
                obj.VisualEffects.resize(effectIndex+1, 0);
            obj.VisualEffects[effectIndex] = fields[4].GetInt32();
            break;
        }
    }
}
uint32 Quest::XPValue(uint32 playerLevel) const
{
    if (playerLevel)
    {
        uint32 questLevel = uint32(Level == -1 ? playerLevel : Level);
        QuestXPEntry const* questXp = sQuestXPStore.LookupEntry(questLevel);
        if (!questXp || RewardXPDifficulty >= 10)
            return 0;
        float multiplier = 1.0f;
        if (questLevel != playerLevel)
            multiplier = sXpGameTable.GetRow(std::min(playerLevel, questLevel))->Divisor / sXpGameTable.GetRow(playerLevel)->Divisor;
        int32 diffFactor = 2 * (questLevel - playerLevel) + 20;
        if (diffFactor < 1)
            diffFactor = 1;
        else if (diffFactor > 10)
            diffFactor = 10;
        uint32 xp = diffFactor * questXp->Exp[RewardXPDifficulty] * RewardXPMultiplier / 10 * multiplier;
        if (xp <= 100)
            xp = 5 * ((xp + 2) / 5);
        else if (xp <= 500)
            xp = 10 * ((xp + 5) / 10);
        else if (xp <= 1000)
            xp = 25 * ((xp + 12) / 25);
        else
            xp = 50 * ((xp + 25) / 50);
        return xp;
    }
    return 0;
}
uint32 Quest::MoneyValue(uint8 playerLevel) const
{
    uint8 level = Level == -1 ? playerLevel : Level;
    if (QuestMoneyRewardEntry const* money = sQuestMoneyRewardStore.LookupEntry(level))
        return money->Money[GetRewMoneyDifficulty()] * GetMoneyMultiplier();
    else
        return 0;
}
void Quest::BuildQuestRewards(WorldPackets::Quest::QuestRewards& rewards, Player* player) const
{
    rewards.ChoiceItemCount         = GetRewChoiceItemsCount();
    rewards.ItemCount               = GetRewItemsCount();
    rewards.Money                   = player->GetQuestMoneyReward(this);
    rewards.XP                      = player->GetQuestXPReward(this);
    rewards.ArtifactCategoryID      = GetArtifactCategoryId();
    rewards.Title                   = GetRewTitle();
    rewards.FactionFlags            = GetRewardReputationMask();
    for (uint32 i = 0; i < QUEST_REWARD_DISPLAY_SPELL_COUNT; ++i)
        rewards.SpellCompletionDisplayID[i] = RewardDisplaySpell[i];
    rewards.SpellCompletionID       = GetRewSpell();
    rewards.SkillLineID             = GetRewardSkillId();
    rewards.NumSkillUps             = GetRewardSkillPoints();
    rewards.RewardID                = GetRewardId();
    for (uint32 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
    {
        rewards.ChoiceItems[i].ItemID = RewardChoiceItemId[i];
        rewards.ChoiceItems[i].Quantity = RewardChoiceItemCount[i];
    }
    for (uint32 i = 0; i < QUEST_REWARD_ITEM_COUNT; ++i)
    {
        rewards.ItemID[i] = RewardItemId[i];
        rewards.ItemQty[i] = RewardItemCount[i];
    }
    for (uint32 i = 0; i < QUEST_REWARD_REPUTATIONS_COUNT; ++i)
    {
        rewards.FactionID[i] = RewardFactionId[i];
        rewards.FactionValue[i] = RewardFactionValue[i];
        rewards.FactionOverride[i] = RewardFactionOverride[i];
        rewards.FactionCapIn[i] = RewardFactionCapIn[i];
    }
    for (uint32 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i)
    {
        rewards.CurrencyID[i] = RewardCurrencyId[i];
        rewards.CurrencyQty[i] = RewardCurrencyCount[i];
    }
}
uint32 Quest::GetRewMoneyMaxLevel() const
{
    // If Quest has flag to not give money on max level, it's 0
    if (HasFlag(QUEST_FLAGS_NO_MONEY_FROM_XP))
        return 0;
    // Else, return the rewarded copper sum modified by the rate
    return uint32(RewardBonusMoney * sWorld->getRate(RATE_MONEY_MAX_LEVEL_QUEST));
}
bool Quest::IsAutoAccept() const
{
    return !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) && HasFlag(QUEST_FLAGS_AUTO_ACCEPT);
}
bool Quest::IsAutoComplete() const
{
    return !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_COMPLETE) && Type == QUEST_TYPE_AUTOCOMPLETE;
}
bool Quest::IsRaidQuest(Difficulty difficulty) const
{
    switch (QuestInfoID)
    {
        case QUEST_INFO_RAID:
            return true;
        case QUEST_INFO_RAID_10:
            return difficulty == DIFFICULTY_10_N || difficulty == DIFFICULTY_10_HC;
        case QUEST_INFO_RAID_25:
            return difficulty == DIFFICULTY_25_N || difficulty == DIFFICULTY_25_HC;
        default:
            break;
    }
    if ((Flags & QUEST_FLAGS_RAID) != 0)
        return true;
    return false;
}
bool Quest::IsAllowedInRaid(Difficulty difficulty) const
{
    if (IsRaidQuest(difficulty))
        return true;
    return sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_RAID);
}
uint32 Quest::CalculateHonorGain(uint8 /*level*/) const
{
    uint32 honor = 0;
    /*if (GetRewHonorAddition() > 0 || GetRewHonorMultiplier() > 0.0f)
    {
        // values stored from 0.. for 1...
        TeamContributionPointsEntry const* tc = sTeamContributionPointsStore.LookupEntry(level);
        if (!tc)
            return 0;
        honor = uint32(tc->value * GetRewHonorMultiplier() * 0.1f);
        honor += GetRewHonorAddition();
    }*/
    return honor;
}
bool Quest::CanIncreaseRewardedQuestCounters() const
{
    // Dungeon Finder/Daily/Repeatable (if not weekly, monthly or seasonal) quests are never considered rewarded serverside.
    // This affects counters and client requests for completed quests.
    return (!IsDFQuest() && !IsDaily() && (!IsRepeatable() || IsWeekly() || IsMonthly() || IsSeasonal()));
}