// Scripted by Xinef

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "Spell.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "Player.h"

enum eBonfire
{
    GO_MIDSUMMER_BONFIRE                = 181288,

    SPELL_STAMP_OUT_BONFIRE             = 45437,
    SPELL_LIGHT_BONFIRE                 = 29831,
};

class go_midsummer_bonfire : public GameObjectScript
{
public:
    go_midsummer_bonfire() : GameObjectScript("go_midsummer_bonfire") { }

    bool OnGossipSelect(Player* player, GameObject* go, uint32 /*sender*/, uint32 action)
    {
        player->CLOSE_GOSSIP_MENU();
        // we know that there is only one gossip.
        player->CastSpell(player, SPELL_STAMP_OUT_BONFIRE, true);
        return true;
    }
};

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

        struct npc_midsummer_bonfireAI : public ScriptedAI
        {
            npc_midsummer_bonfireAI(Creature* c) : ScriptedAI(c) 
            {
                me->IsAIEnabled = true;
                goGUID = 0;
                if (GameObject* go = me->SummonGameObject(GO_MIDSUMMER_BONFIRE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0))
                {
                    goGUID = go->GetGUID();
                    me->RemoveGameObject(go, false);
                }
            }
            
            uint64 goGUID;

            void SpellHit(Unit*, SpellInfo const* spellInfo)
            {
                if (!goGUID)
                    return;

                // Extinguish fire
                if (spellInfo->Id == SPELL_STAMP_OUT_BONFIRE)
                {
                    if (GameObject* go = ObjectAccessor::GetGameObject(*me, goGUID))
                        go->SetPhaseMask(2, true);
                }
                else if (spellInfo->Id == SPELL_LIGHT_BONFIRE)
                {
                    if (GameObject* go = ObjectAccessor::GetGameObject(*me, goGUID))
                    {
                        go->SetPhaseMask(1, true);
                        go->SendCustomAnim(1);
                    }
                }

            }
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return new npc_midsummer_bonfireAI(creature);
        }
};

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

        struct npc_midsummer_torch_targetAI : public ScriptedAI
        {
            npc_midsummer_torch_targetAI(Creature* c) : ScriptedAI(c) 
            {
                teleTimer = 0;
                startTimer = 1;
                posVec.clear();
                playerGUID = 0;
                me->CastSpell(me, 43313, true);
                counter = 0;
                maxCount = 0;
            }

            uint64 playerGUID;
            uint32 startTimer;
            uint32 teleTimer;
            std::vector<Position> posVec;
            uint8 counter;
            uint8 maxCount;

            void SetPlayerGUID(uint64 guid, uint8 cnt)
            {
                playerGUID = guid;
                maxCount = cnt;
            }

            bool CanBeSeen(Player const* seer)
            {
                return seer->GetGUID() == playerGUID;
            }
            
            void SpellHit(Unit* caster, SpellInfo const* spellInfo)
            {
                if (posVec.empty())
                    return;
                // Triggered spell from torch
                if (spellInfo->Id == 46054 && caster->GetTypeId() == TYPEID_PLAYER)
                {
                    me->CastSpell(me, 45724, true); // hit visual anim
                    if (++counter >= maxCount)
                    {
                        caster->CastSpell(caster, (caster->ToPlayer()->GetTeamId() ? 46651 : 45719), true); // quest complete spell
                        me->DespawnOrUnsummon(1);
                        return;
                    }

                    teleTimer = 1;
                }
            }

            void UpdateAI(uint32 diff)
            {
                if (startTimer)
                {
                    startTimer += diff;
                    if (startTimer >= 200)
                    {
                        startTimer = 0;
                        FillPositions();
                        SelectPosition();
                    }
                }
                if (teleTimer)
                {
                    teleTimer += diff;
                    if (teleTimer >= 750 && teleTimer < 10000)
                    {
                        teleTimer = 10000;
                        SelectPosition();
                    }
                    else if (teleTimer >= 10500)
                    {
                        if (Player* plr = ObjectAccessor::GetPlayer(*me, playerGUID))
                            plr->UpdateTriggerVisibility();

                        teleTimer = 0;
                    }
                }
            }

            void FillPositions()
            {
                std::list<GameObject*> gobjList;
                me->GetGameObjectListWithEntryInGrid(gobjList, 187708 /*TORCH_GO*/, 30.0f);
                for (std::list<GameObject*>::const_iterator itr = gobjList.begin(); itr != gobjList.end(); ++itr)
                {
                    Position pos;
                    pos.Relocate(*itr);
                    posVec.push_back(pos);
                }
            }

            void SelectPosition()
            {
                if (posVec.empty())
                    return;
                int8 num = urand(0, posVec.size()-1);
                Position pos;
                pos.Relocate(posVec.at(num));
                me->m_last_notify_position.Relocate(0.0f, 0.0f, 0.0f);
                me->m_last_notify_mstime = World::GetGameTimeMS() + 10000;

                me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
            }
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return new npc_midsummer_torch_targetAI(creature);
        }
};


///////////////////////////////
// SPELLS
///////////////////////////////

enum RibbonPole
{
    SPELL_RIBBON_POLE_CHANNEL_VISUAL    = 29172,
    SPELL_RIBBON_POLE_XP                = 29175,
    SPELL_RIBBON_POLE_FIREWORKS         = 46971,

    NPC_RIBBON_POLE_DEBUG_TARGET        = 17066,
};

class spell_midsummer_ribbon_pole : public SpellScriptLoader
{
public:
    spell_midsummer_ribbon_pole() : SpellScriptLoader("spell_midsummer_ribbon_pole") { }

    class spell_midsummer_ribbon_pole_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_midsummer_ribbon_pole_AuraScript)
        
        void HandleEffectPeriodic(AuraEffect const * aurEff)
        {
            PreventDefaultAction();
            if (Unit *target = GetTarget())
            {
                Creature* cr = target->FindNearestCreature(NPC_RIBBON_POLE_DEBUG_TARGET, 10.0f);
                if (!cr)
                {
                    target->RemoveAura(SPELL_RIBBON_POLE_CHANNEL_VISUAL);
                    SetDuration(1);
                    return;
                }

                if (Aura* aur = target->GetAura(SPELL_RIBBON_POLE_XP))
                    aur->SetDuration(std::min(aur->GetDuration()+3*MINUTE*IN_MILLISECONDS, 60*MINUTE*IN_MILLISECONDS));
                else
                    target->CastSpell(target, SPELL_RIBBON_POLE_XP, true);

                if (roll_chance_i(5))
                {
                    cr->Relocate(cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ()-6.5f);
                    cr->CastSpell(cr, SPELL_RIBBON_POLE_FIREWORKS, true);
                    cr->Relocate(cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ()+6.5f);
                }

                // Achievement
                if ((time(NULL) - GetApplyTime()) > 60 && target->GetTypeId() == TYPEID_PLAYER)
                    target->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, 58934, 0, target);
            }
        }

        void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            Unit* ar = GetTarget();
            ar->CastSpell(ar, SPELL_RIBBON_POLE_CHANNEL_VISUAL, true);
        }

        void Register()
        {
            OnEffectApply += AuraEffectApplyFn(spell_midsummer_ribbon_pole_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL);
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_midsummer_ribbon_pole_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
        }
    };

    AuraScript* GetAuraScript() const
    {
        return new spell_midsummer_ribbon_pole_AuraScript();
    }
};

class spell_midsummer_torch_quest : public SpellScriptLoader
{
public:
    spell_midsummer_torch_quest() : SpellScriptLoader("spell_midsummer_torch_quest") { }

    class spell_midsummer_torch_quest_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_midsummer_torch_quest_AuraScript)

        bool Load()
        {
            torchGUID = 0;
            return true;
        }

        uint64 torchGUID;
        
        void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            Unit* ar = GetTarget();
            if (Creature* cr = ar->SummonCreature(25535, ar->GetPositionX(), ar->GetPositionY(), ar->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 90000))
            {
                torchGUID = cr->GetGUID();
                CAST_AI(npc_midsummer_torch_target::npc_midsummer_torch_targetAI, cr->AI())->SetPlayerGUID(ar->GetGUID(), (GetId() == 45716 ? 8 : 20));
            }
        }

        void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            if (Creature* cr = ObjectAccessor::GetCreature(*GetTarget(), torchGUID))
                cr->DespawnOrUnsummon(1);
        }

        void Register()
        {
            OnEffectApply += AuraEffectApplyFn(spell_midsummer_torch_quest_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DETECT_AMORE, AURA_EFFECT_HANDLE_REAL);
            OnEffectRemove += AuraEffectRemoveFn(spell_midsummer_torch_quest_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DETECT_AMORE, AURA_EFFECT_HANDLE_REAL);
        }
    };

    AuraScript* GetAuraScript() const
    {
        return new spell_midsummer_torch_quest_AuraScript();
    }
};

enum flingTorch
{
    NPC_TORCH_TARGET                = 26188,

    SPELL_FLING_TORCH               = 45669,
    SPELL_FLING_TORCH_DUMMY         = 46747,
    SPELL_MISSED_TORCH              = 45676,
    SPELL_TORCH_COUNTER             = 45693,
};

class spell_midsummer_fling_torch : public SpellScriptLoader
{
    public:
    spell_midsummer_fling_torch() : SpellScriptLoader("spell_midsummer_fling_torch") {}

    class spell_midsummer_fling_torch_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_midsummer_fling_torch_SpellScript);

        bool handled;
        bool Load() { handled = false; return true; }

        void ThrowNextTorch(Unit* caster)
        {
            std::list<Creature*> crList;
            caster->GetCreaturesWithEntryInRange(crList, 100.0f, NPC_TORCH_TARGET);

            uint8 rand = urand(0, crList.size()-1);
            Position pos;
            pos.Relocate(0.0f, 0.0f, 0.0f);
            for (std::list<Creature*>::const_iterator itr = crList.begin(); itr != crList.end(); ++itr, --rand)
            {
                if (caster->GetDistance(*itr) < 5)
                {
                    if (!rand)
                        rand++;
                    continue;
                }

                if (!rand)
                {
                    pos.Relocate(*itr);
                    break;
                }
            }

            // we have any pos
            if (pos.GetPositionX())
                caster->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), SPELL_FLING_TORCH, true);
        }

        void HandleFinish()
        {
            Unit* caster = GetCaster();
            if (!caster || !caster->ToPlayer()) // caster cant be null, but meh :p
                return;

            if (GetSpellInfo()->Id != SPELL_FLING_TORCH_DUMMY)
            {
                if (!handled)
                    if (const WorldLocation* loc = GetExplTargetDest())
                    {
                        caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_MISSED_TORCH, true);
                        caster->RemoveAurasDueToSpell(SPELL_TORCH_COUNTER);
                    }
                return;
            }

            ThrowNextTorch(caster);
        }

        void HandleScript(SpellEffIndex effIndex)
        {
            PreventHitDefaultEffect(effIndex);
            if (Player* target = GetHitPlayer())
            {
                if (target->GetGUID() != GetCaster()->GetGUID())
                    return;

                handled = true;
                if (Aura* aur = target->GetAura(SPELL_TORCH_COUNTER))
                {
                    aur->ModStackAmount(1);
                    uint8 count = 4;
                    if (target->GetQuestStatus(target->GetTeamId() ? 11925 : 11924) == QUEST_STATUS_INCOMPLETE) // More Torch Catching quests
                        count = 10;

                    if (aur->GetStackAmount() >= count)
                    {
                        //target->CastSpell(target, 46711, true); // Set Flag: all torch returning quests are complete
                        target->CastSpell(target, (target->GetTeamId() ? 46654 : 46081), true); // Quest completion
                        aur->SetDuration(1);
                        return;
                    }
                }
                else
                    target->CastSpell(target, SPELL_TORCH_COUNTER, true);

                ThrowNextTorch(GetCaster());
            }
        }

        void Register()
        {
            AfterCast += SpellCastFn(spell_midsummer_fling_torch_SpellScript::HandleFinish);
            if (m_scriptSpellId == 45671)
                OnEffectHitTarget += SpellEffectFn(spell_midsummer_fling_torch_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
        }
    };

    SpellScript* GetSpellScript() const
    {
        return new spell_midsummer_fling_torch_SpellScript();
    }
};

enum eJuggle
{
    SPELL_JUGGLE_SELF           = 45638,
    SPELL_JUGGLE_SLOW           = 45792,
    SPELL_JUGGLE_MED            = 45806,
    SPELL_JUGGLE_FAST           = 45816,

    SPELL_TORCH_CHECK           = 45644,
    SPELL_GIVE_TORCH            = 45280,
    QUEST_CHECK                 = 11937,
};

class spell_midsummer_juggling_torch : public SpellScriptLoader
{
    public:
    spell_midsummer_juggling_torch() : SpellScriptLoader("spell_midsummer_juggling_torch") {}

    class spell_midsummer_juggling_torch_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_midsummer_juggling_torch_SpellScript);

        bool handled;
        bool Load() { handled = false; return true; }
        void HandleFinish()
        {
            Unit* caster = GetCaster();
            if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
                return;

            if (const WorldLocation* loc = GetExplTargetDest())
            {
                if (loc->GetExactDist(caster) < 3.0f)
                    caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_SELF, true);
                else if (loc->GetExactDist(caster) < 10.0f)
                    caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_SLOW, true);
                else if (loc->GetExactDist(caster) < 25.0f)
                    caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_MED, true);
                else
                    caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_FAST, true);
            }
            else
                caster->CastSpell(caster, SPELL_JUGGLE_SELF, true);
        }

        void HandleDummy(SpellEffIndex effIndex)
        {
            PreventHitDefaultEffect(effIndex);
            Unit* caster = GetCaster();
            if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
                return;

            if (Player* target = GetHitPlayer())
                if (!handled && target->GetQuestRewardStatus(target->GetTeamId() == TEAM_ALLIANCE ? 11657 : 11923))
                {
                    handled = true;
                    caster->CastSpell(target, SPELL_GIVE_TORCH, true);
                }
        }

        void Register()
        {
            if (m_scriptSpellId == SPELL_TORCH_CHECK)
                OnEffectHitTarget += SpellEffectFn(spell_midsummer_juggling_torch_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
            else
                AfterCast += SpellCastFn(spell_midsummer_juggling_torch_SpellScript::HandleFinish);
        }
    };

    SpellScript* GetSpellScript() const
    {
        return new spell_midsummer_juggling_torch_SpellScript();
    }
};

void AddSC_event_midsummer_scripts()
{
    // NPCs
    new go_midsummer_bonfire();
    new npc_midsummer_bonfire();
    new npc_midsummer_torch_target();

    // Spells
    new spell_midsummer_ribbon_pole();
    new spell_midsummer_torch_quest();
    new spell_midsummer_fling_torch();
    new spell_midsummer_juggling_torch();
}