/*
 * Originally written by Xinef - Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-AGPL
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "magtheridons_lair.h"
#include "SpellInfo.h"

enum Yells
{
    SAY_TAUNT                       = 0,
    SAY_FREE                        = 1,
    SAY_AGGRO                       = 2,
    SAY_SLAY                        = 3,
    SAY_BANISH                      = 4,
    SAY_PHASE3                      = 5,
    SAY_DEATH                       = 6,
};

enum Emotes
{
    SAY_EMOTE_BEGIN                 = 7,
    SAY_EMOTE_NEARLY                = 8,
    SAY_EMOTE_FREE                  = 9,
    SAY_EMOTE_NOVA                  = 10
};

enum Spells
{
    SPELL_SHADOW_CAGE           = 30205,
    SPELL_BLAST_NOVA            = 30616,
    SPELL_CLEAVE                = 30619,
    SPELL_BLAZE                 = 30541,
    SPELL_BLAZE_SUMMON          = 30542,
    SPELL_BERSERK               = 27680,
    SPELL_SHADOW_GRASP_VISUAL   = 30166,
    SPELL_MIND_EXHAUSTION       = 44032,
    SPELL_QUAKE                 = 30657,
    SPELL_COLLAPSE_DAMAGE       = 36449,
    SPELL_CAMERA_SHAKE          = 36455,
    SPELL_DEBRIS_VISUAL         = 30632,
    SPELL_DEBRIS_DAMAGE         = 30631
};

enum Events
{
    EVENT_EMOTE1                = 1,
    EVENT_EMOTE2                = 2,
    EVENT_EMOTE3                = 3,
    EVENT_ENTER_COMBAT          = 4,
    EVENT_RECENTLY_SPOKEN       = 5,

    EVENT_CLEAVE                = 10,
    EVENT_BLAST_NOVA            = 11,
    EVENT_BLAZE                 = 12,
    EVENT_ENRAGE                = 13,
    EVENT_QUAKE                 = 14,
    EVENT_CHECK_HEALTH          = 15,
    EVENT_COLLAPSE_CEIL         = 16,
    EVENT_COLLAPSE_DAMAGE       = 17,
    EVENT_DEBRIS                = 18,

    EVENT_RANDOM_TAUNT          = 30,
    EVENT_CHECK_GRASP           = 31,
    EVENT_CANCEL_GRASP_CHECK    = 32
};

class DealDebrisDamage : public BasicEvent
{
public:
    DealDebrisDamage(Creature& creature, uint64 targetGUID) : _owner(creature), _targetGUID(targetGUID) { }

    bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/)
    {
        if (Unit* target = ObjectAccessor::GetUnit(_owner, _targetGUID))
            target->CastSpell(target, SPELL_DEBRIS_DAMAGE, true, NULL, NULL, _owner.GetGUID());
        return true;
    }

private:
    uint64 _targetGUID;
    Creature& _owner;
};

class boss_magtheridon : public CreatureScript
{
    public:

        boss_magtheridon() : CreatureScript("boss_magtheridon") { }

        struct boss_magtheridonAI : public BossAI
        {
            boss_magtheridonAI(Creature* creature) : BossAI(creature, TYPE_MAGTHERIDON) { }

            EventMap events2;
 

            void Reset()
            {
                events2.Reset();
                events2.ScheduleEvent(EVENT_RANDOM_TAUNT, 90000);
                _Reset();
                me->CastSpell(me, SPELL_SHADOW_CAGE, true);
                me->SetReactState(REACT_PASSIVE);
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_IMMUNE_TO_PC);
            }

            void KilledUnit(Unit* victim)
            {
                if (events.GetNextEventTime(EVENT_RECENTLY_SPOKEN) == 0)
                {
                    events.ScheduleEvent(EVENT_RECENTLY_SPOKEN, 5000);
                    Talk(SAY_SLAY);
                }
            }

            void JustDied(Unit* /*killer*/)
            {
                _JustDied();
                Talk(SAY_DEATH);
            }

            void MoveInLineOfSight(Unit* /*who*/) { }

            void EnterCombat(Unit* /*who*/)
            {
                events2.Reset();
                _EnterCombat();
                events.ScheduleEvent(EVENT_EMOTE1, 0);
                events.ScheduleEvent(EVENT_EMOTE2, 60000);
                events.ScheduleEvent(EVENT_EMOTE3, 120000);
                events.ScheduleEvent(EVENT_ENTER_COMBAT, 123000);

           }

            void UpdateAI(uint32 diff)
            {
                events2.Update(diff);
                switch (events2.ExecuteEvent())
                {
                    case EVENT_RANDOM_TAUNT:
                        Talk(SAY_TAUNT);
                        events2.ScheduleEvent(EVENT_RANDOM_TAUNT, 90000);
                        break;
                    case EVENT_CHECK_GRASP:
                        if (me->GetAuraCount(SPELL_SHADOW_GRASP_VISUAL) == 5)
                        {
                            Talk(SAY_BANISH);
                            me->InterruptNonMeleeSpells(true);
                            break;
                        }
                        events2.ScheduleEvent(EVENT_CHECK_GRASP, 0);
                        break;
                }


                if (!UpdateVictim() || !CheckInRoom())
                    return;

                events.Update(diff);
                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;

                switch (events.ExecuteEvent())
                {
                    case EVENT_EMOTE1:
                        Talk(SAY_EMOTE_BEGIN);
                        break;
                    case EVENT_EMOTE2:
                        Talk(SAY_EMOTE_NEARLY);
                        break;
                    case EVENT_EMOTE3:
                        Talk(SAY_EMOTE_FREE);
                        Talk(SAY_FREE);
                        break;
                    case EVENT_ENTER_COMBAT:
                        me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_IMMUNE_TO_PC);
                        me->SetReactState(REACT_AGGRESSIVE);
                        events.ScheduleEvent(EVENT_CLEAVE, 9000);
                        events.ScheduleEvent(EVENT_BLAST_NOVA, 60000);
                        events.ScheduleEvent(EVENT_BLAZE, 10000);
                        events.ScheduleEvent(EVENT_QUAKE, 40000);
                        events.ScheduleEvent(EVENT_CHECK_HEALTH, 500);
                        events.ScheduleEvent(EVENT_ENRAGE, 22*MINUTE*IN_MILLISECONDS);

                        instance->SetData(DATA_ACTIVATE_CUBES, 1);
                        me->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE);
                        break;
                    case EVENT_CLEAVE:
                        me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
                        events.ScheduleEvent(EVENT_CLEAVE, 10000);
                        break;
                    case EVENT_BLAST_NOVA:
                        me->CastSpell(me, SPELL_BLAST_NOVA, false);
                        events.ScheduleEvent(EVENT_BLAST_NOVA, 60000);
                        events.ScheduleEvent(EVENT_CANCEL_GRASP_CHECK, 12000);
                        events2.ScheduleEvent(EVENT_CHECK_GRASP, 0);
                        break;
                    case EVENT_BLAZE:
                        me->CastCustomSpell(SPELL_BLAZE, SPELLVALUE_MAX_TARGETS, 1);
                        events.ScheduleEvent(EVENT_BLAZE, 30000);
                        break;
                    case EVENT_ENRAGE:
                        me->CastSpell(me, SPELL_BERSERK, true);
                        break;
                    case EVENT_CANCEL_GRASP_CHECK:
                        events2.Reset();
                        break;
                    case EVENT_QUAKE:
                        me->CastSpell(me, SPELL_QUAKE, false);
                        events.ScheduleEvent(EVENT_QUAKE, 50000);
                        break;
                    case EVENT_CHECK_HEALTH:
                        if (me->HealthBelowPct(30))
                        {
                            Talk(SAY_PHASE3);
                            events.SetPhase(1);
                            events.DelayEvents(18000);
                            events.ScheduleEvent(EVENT_COLLAPSE_CEIL, 8000);
                            events.ScheduleEvent(EVENT_COLLAPSE_DAMAGE, 15000);
                            break;
                        }
                        events.ScheduleEvent(EVENT_CHECK_HEALTH, 500);
                        break;
                    case EVENT_COLLAPSE_CEIL:
                        me->CastSpell(me, SPELL_CAMERA_SHAKE, true);
                        instance->SetData(DATA_COLLAPSE, GO_STATE_ACTIVE);
                        break;
                    case EVENT_COLLAPSE_DAMAGE:
                        me->CastSpell(me, SPELL_COLLAPSE_DAMAGE, true);
                        me->resetAttackTimer();
                        events.SetPhase(0);
                        events.ScheduleEvent(EVENT_DEBRIS, 20000);
                        break;
                    case EVENT_DEBRIS:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
                        {
                            target->CastSpell(target, SPELL_DEBRIS_VISUAL, true, NULL, NULL, me->GetGUID());
                            me->m_Events.AddEvent(new DealDebrisDamage(*me, target->GetGUID()), me->m_Events.CalculateTime(5000));
                        }
                        events.ScheduleEvent(EVENT_DEBRIS, 20000);
                        break;
                }

                if (!events.IsInPhase(1))
                    DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return GetInstanceAI<boss_magtheridonAI>(creature);
        }
};

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

        class spell_magtheridon_blaze_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_magtheridon_blaze_SpellScript);

            void HandleScriptEffect(SpellEffIndex /*effIndex*/)
            {
                if (Unit* target = GetHitUnit())
                    target->CastSpell(target, SPELL_BLAZE_SUMMON, true);
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_magtheridon_blaze_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

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

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

    class spell_magtheridon_shadow_grasp_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_magtheridon_shadow_grasp_AuraScript)
        
        void HandleDummyApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            GetUnitOwner()->CastSpell((Unit*)NULL, SPELL_SHADOW_GRASP_VISUAL, false);
        }
        
        void HandleDummyRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            GetUnitOwner()->InterruptNonMeleeSpells(true);
        }

        void HandlePeriodicRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_MIND_EXHAUSTION, true);
        }

        void Register()
        {
            OnEffectApply += AuraEffectApplyFn(spell_magtheridon_shadow_grasp_AuraScript::HandleDummyApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
            OnEffectRemove += AuraEffectRemoveFn(spell_magtheridon_shadow_grasp_AuraScript::HandleDummyRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
            OnEffectRemove += AuraEffectRemoveFn(spell_magtheridon_shadow_grasp_AuraScript::HandlePeriodicRemove, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
        }
    };

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

void AddSC_boss_magtheridon()
{
    new boss_magtheridon();
    new spell_magtheridon_blaze();
    new spell_magtheridon_shadow_grasp();
}

