#include "mod_quick_boost.h"
#include "Chat.h"
#include "Config.h"
#include "Creature.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ScriptedGossip.h"
#include "ScriptMgr.h"
#include "StringFormat.h"

namespace
{
    constexpr uint32 GOSSIP_ACTION_BOOST = 1;
    constexpr uint32 QUICK_BOOST_NPC_TEXT = 70100;
}

std::unordered_map> const QuickBoost::_classGear =
{
    { CLASS_WARRIOR, {
        { 3489,  EQUIPMENT_SLOT_MAINHAND }, // Thick War Axe
        { 821,   EQUIPMENT_SLOT_CHEST    }, // Riverpaw Leather Vest
        { 38513, EQUIPMENT_SLOT_LEGS     }, // Boarhide Leggings
        { 4789,  EQUIPMENT_SLOT_FEET     }, // Stable Boots
    } },
    { CLASS_PALADIN, {
        { 4569,  EQUIPMENT_SLOT_MAINHAND }, // Staunch Hammer
        { 1211,  EQUIPMENT_SLOT_CHEST    }, // Gnoll War Harness
        { 6553,  EQUIPMENT_SLOT_LEGS     }, // Bard's Trousers
        { 6557,  EQUIPMENT_SLOT_FEET     }, // Bard's Boots
    } },
    { CLASS_HUNTER, {
        { 11304, EQUIPMENT_SLOT_RANGED   }, // Fine Longbow
        { 6268,  EQUIPMENT_SLOT_CHEST    }, // Pioneer Tunic
        { 4242,  EQUIPMENT_SLOT_LEGS     }, // Embossed Leather Pants
        { 5968,  EQUIPMENT_SLOT_FEET     }, // Rugged Boots
    } },
    { CLASS_ROGUE, {
        { 4571,  EQUIPMENT_SLOT_MAINHAND }, // War Knife
        { 2311,  EQUIPMENT_SLOT_CHEST    }, // White Leather Jerkin
        { 9756,  EQUIPMENT_SLOT_LEGS     }, // Gypsy Trousers
        { 2309,  EQUIPMENT_SLOT_FEET     }, // Embossed Leather Boots
    } },
    { CLASS_PRIEST, {
        { 4575,  EQUIPMENT_SLOT_MAINHAND }, // Medicine Staff
        { 9748,  EQUIPMENT_SLOT_CHEST    }, // Simple Robe
        { 9747,  EQUIPMENT_SLOT_LEGS     }, // Simple Britches
        { 22991, EQUIPMENT_SLOT_FEET     }, // Apprentice Boots
    } },
    { CLASS_SHAMAN, {
        { 4570,  EQUIPMENT_SLOT_MAINHAND }, // Birchwood Maul
        { 2973,  EQUIPMENT_SLOT_CHEST    }, // Hunting Tunic
        { 2974,  EQUIPMENT_SLOT_LEGS     }, // Hunting Pants
        { 2307,  EQUIPMENT_SLOT_FEET     }, // Fine Leather Boots
    } },
    { CLASS_MAGE, {
        { 4566,  EQUIPMENT_SLOT_MAINHAND }, // Sturdy Quarterstaff
        { 6242,  EQUIPMENT_SLOT_CHEST    }, // Blue Linen Robe
        { 14097, EQUIPMENT_SLOT_LEGS     }, // Native Pants
        { 2569,  EQUIPMENT_SLOT_FEET     }, // Linen Boots
    } },
    { CLASS_WARLOCK, {
        { 1933,  EQUIPMENT_SLOT_MAINHAND }, // Staff of Conjuring
        { 6527,  EQUIPMENT_SLOT_CHEST    }, // Ancestral Robe
        { 3291,  EQUIPMENT_SLOT_LEGS     }, // Ancestral Woollies
        { 4312,  EQUIPMENT_SLOT_FEET     }, // Soft-soled Linen Boots
    } },
    { CLASS_DRUID, {
        { 1539,  EQUIPMENT_SLOT_MAINHAND }, // Gnarled Hermit's Staff
        { 3288,  EQUIPMENT_SLOT_CHEST    }, // Tribal Vest
        { 15303, EQUIPMENT_SLOT_LEGS     }, // Grizzly Pants
        { 15012, EQUIPMENT_SLOT_FEET     }, // Lupine Slippers
    } },
};

std::unordered_map const QuickBoost::_racePet =
{
    { RACE_HUMAN,         118   }, // Prowler (Elwynn Forest)
    { RACE_DWARF,         1196  }, // Ice Claw Bear (Dun Morogh)
    { RACE_GNOME,         1196  }, // Ice Claw Bear (Dun Morogh)
    { RACE_NIGHTELF,      2031  }, // Young Nightsaber (Teldrassil)
    { RACE_DRAENEI,       17345 }, // Brown Bear (Azuremyst Isle)
    { RACE_ORC,           3098  }, // Mottled Boar (Durotar)
    { RACE_TROLL,         3098  }, // Mottled Boar (Durotar)
    { RACE_TAUREN,        2958  }, // Prairie Wolf (Mulgore)
    { RACE_UNDEAD_PLAYER, 1765  }, // Worg (Tirisfal Glades)
    { RACE_BLOODELF,      15649 }, // Feral Dragonhawk Hatchling (Eversong Woods)
};

std::unordered_map const QuickBoost::_raceCapital =
{
    { RACE_HUMAN,         { 0,   -8833.38f, 628.628f,  94.0066f,  1.06535f } }, // Stormwind City
    { RACE_DWARF,         { 0,   -4918.88f, -940.406f, 501.564f,  5.42347f } }, // Ironforge
    { RACE_GNOME,         { 0,   -4918.88f, -940.406f, 501.564f,  5.42347f } }, // Ironforge
    { RACE_NIGHTELF,      { 1,   9949.56f,  2284.21f,  1341.4f,   1.59587f } }, // Darnassus
    { RACE_DRAENEI,       { 530, -3965.7f,  -11653.6f, -138.844f, 0.852154f} }, // The Exodar
    { RACE_ORC,           { 1,   1629.85f,  -4373.64f, 31.5573f,  3.69762f } }, // Orgrimmar
    { RACE_TROLL,         { 1,   1629.85f,  -4373.64f, 31.5573f,  3.69762f } }, // Orgrimmar
    { RACE_TAUREN,        { 1,   -1277.37f, 124.804f,  131.287f,  5.22274f } }, // Thunder Bluff
    { RACE_UNDEAD_PLAYER, { 0,   1584.14f,  240.308f,  -52.1534f, 0.041793f} }, // Undercity
    { RACE_BLOODELF,      { 530, 9487.69f,  -7279.2f,  14.2866f,  6.16478f } }, // Silvermoon City
};

void QuickBoost::LoadConfig()
{
    _enabled = sConfigMgr->GetOption("QuickBoost.Enable", true);
    _boostLevel = static_cast(sConfigMgr->GetOption("QuickBoost.Level", 15));
    _destroyStartingGear = sConfigMgr->GetOption("QuickBoost.DestroyStartingGear", true);
    _startingMoney = sConfigMgr->GetOption("QuickBoost.StartingMoney", 5000);
    _teleportToCapital = sConfigMgr->GetOption("QuickBoost.TeleportToCapital", true);
    _grantHunterPet = sConfigMgr->GetOption("QuickBoost.GrantHunterPet", true);
    _learnClassSpells = sConfigMgr->GetOption("QuickBoost.LearnClassSpells", true);
}

void QuickBoost::DestroyStartingGear(Player* player) const
{
    if (!_destroyStartingGear)
        return;

    for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
        if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
            player->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
}

void QuickBoost::EquipStarterGear(Player* player) const
{
    auto it = _classGear.find(player->getClass());
    if (it == _classGear.end())
        return;

    for (QuickBoostGearEntry const& gear : it->second)
    {
        uint16 dest = 0;
        if (player->CanEquipNewItem(gear.equipmentSlot, dest, gear.itemEntry, false) == EQUIP_ERR_OK)
            player->EquipNewItem(dest, gear.itemEntry, true);
    }
}

void QuickBoost::GiveStartingMoney(Player* player) const
{
    if (_startingMoney)
        player->ModifyMoney(static_cast(_startingMoney));
}

void QuickBoost::TeleportToCapital(Player* player) const
{
    if (!_teleportToCapital)
        return;

    auto it = _raceCapital.find(player->getRace());
    if (it == _raceCapital.end())
        return;

    QuickBoostLocation const& loc = it->second;
    player->TeleportTo(loc.mapId, loc.x, loc.y, loc.z, loc.o);
}

void QuickBoost::GrantHunterPet(Player* player) const
{
    if (!_grantHunterPet || player->getClass() != CLASS_HUNTER || player->IsExistPet())
        return;

    auto it = _racePet.find(player->getRace());
    if (it == _racePet.end())
        return;

    player->CreatePet(it->second);
}

void QuickBoost::LearnClassSpells(Player* player) const
{
    if (!_learnClassSpells)
        return;

    std::vector const& trainers = sObjectMgr->GetClassTrainers(player->getClass());

    // Repeat until a full pass teaches nothing new: learning a rank can unlock
    // the next rank's prerequisite, so a single pass can miss later ranks.
    bool hadNew;
    do
    {
        hadNew = false;
        for (Trainer::Trainer const* trainer : trainers)
        {
            if (!trainer->IsTrainerValidForPlayer(player))
                continue;

            for (Trainer::Spell const& trainerSpell : trainer->GetSpells())
            {
                if (!trainer->CanTeachSpell(player, &trainerSpell))
                    continue;

                if (trainerSpell.IsCastable())
                    player->CastSpell(player, trainerSpell.SpellId, true);
                else
                    player->learnSpell(trainerSpell.SpellId, false);

                hadNew = true;
            }
        }
    } while (hadNew);
}

void QuickBoost::ApplyBoost(Player* player) const
{
    player->GiveLevel(_boostLevel);

    DestroyStartingGear(player);
    EquipStarterGear(player);
    GiveStartingMoney(player);
    GrantHunterPet(player);
    LearnClassSpells(player);

    if (player->getPowerType() == POWER_MANA)
        player->SetPower(POWER_MANA, player->GetMaxPower(POWER_MANA));

    player->SetHealth(player->GetMaxHealth());
    player->SaveToDB(false, false);

    TeleportToCapital(player);
}

class QuickBoost_NPC : public CreatureScript
{
public:
    QuickBoost_NPC() : CreatureScript("QuickBoost_NPC") { }

    bool OnGossipHello(Player* player, Creature* creature) override
    {
        ClearGossipMenuFor(player);

        if (creature->IsQuestGiver())
            player->PrepareQuestMenu(creature->GetGUID());

        if (sQuickBoost->IsEnabled() && player->GetLevel() == 1)
        {
            AddGossipItemFor(player, GOSSIP_ICON_CHAT,
                Acore::StringFormat("Boost me to level {}!", sQuickBoost->GetBoostLevel()),
                GOSSIP_SENDER_MAIN, GOSSIP_ACTION_BOOST);
        }

        SendGossipMenuFor(player, QUICK_BOOST_NPC_TEXT, creature->GetGUID());
        return true;
    }

    bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 sender, uint32 action) override
    {
        if (sender != GOSSIP_SENDER_MAIN)
            return false;

        CloseGossipMenuFor(player);

        if (action == GOSSIP_ACTION_BOOST && sQuickBoost->IsEnabled() && player->GetLevel() == 1)
            sQuickBoost->ApplyBoost(player);

        return true;
    }
};

class QuickBoost_World : public WorldScript
{
public:
    QuickBoost_World() : WorldScript("QuickBoost_World") { }

    void OnBeforeConfigLoad(bool /*reload*/) override
    {
        sQuickBoost->LoadConfig();
    }
};

void AddSC_mod_quick_boost()
{
    new QuickBoost_NPC();
    new QuickBoost_World();
}