/*
REWRITTEN FROM SCRATCH BY XINEF, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "utgarde_pinnacle.h"
#include "SpellScript.h"
#include "Player.h"
#include "PassiveAI.h"

enum Misc
{
    // SAY
    TALK_INTRO_A1                           = 0,
    TALK_INTRO_A2                           = 1,
    TALK_INTRO_S1                           = 0,
    TALK_INTRO_S2                           = 0,
    TALK_INTRO_S3                           = 1,
    SAY_AGGRO                               = 2,
    SAY_SLAY                                = 3,
    SAY_DEATH                               = 4,
    SAY_SACRIFICE_PLAYER                    = 5,

    // SPELLS
    // INTRO
    SPELL_ARTHAS_TRANSFORMING_SVALA         = 54142,
    SPELL_SVALA_TRANSFORMING1               = 54205,
    SPELL_SVALA_TRANSFORMING2               = 54140,

    // SORROWGRAVE
    SPELL_CALL_FLAMES                       = 48258,
    SPELL_BALL_OF_FLAME                     = 48246,
    SPELL_RITUAL_OF_THE_SWORD               = 48276,
    SPELL_RITUAL_STRIKE                     = 48331,
    SPELL_SINSTER_STRIKE_N                  = 15667,
    SPELL_SINSTER_STRIKE_H                  = 59409,
    EQUIP_SWORD                             = 40343,

    // CHANNELERS
    SPELL_PARALYZE                          = 48278,
    SPELL_SHADOWS_IN_THE_DARK               = 59407,
    SPELL_TELEPORT_VISUAL                   = 64446,

    // NPCS
    NPC_RITUAL_CHANNELER                    = 27281,
    NPC_ARTHAS                              = 29280,
    NPC_FLAME_BRAZIER                       = 27273,

    // ACTIONS
    ACTION_START_SORROWGRAVE                = 1,
};

enum Events
{
    // BASE EVENT START
    EVENT_SVALA_START                   = 1,
    EVENT_SVALA_TALK1                   = 2,
    EVENT_SVALA_TALK2                   = 3,
    EVENT_SVALA_TALK3                   = 4,
    EVENT_SVALA_TALK4                   = 5,
    EVENT_SVALA_TALK5                   = 6,
    EVENT_SVALA_TALK6                   = 7,
    EVENT_SVALA_TALK7                   = 8,
    EVENT_SVALA_TALK8                   = 9,
    EVENT_SVALA_TALK9                   = 20,

    // FIGHT
    EVENT_SORROWGRAVE_SS                = 10,
    EVENT_SORROWGRAVE_FLAMES            = 11,
    EVENT_SORROWGRAVE_FLAMES2           = 12,
    EVENT_SORROWGRAVE_RITUAL            = 13,
    EVENT_SORROWGRAVE_RITUAL_SPELLS     = 14,
    EVENT_SORROWGRAVE_FINISH_RITUAL     = 15,
};

const Position RitualChannelerLoc[3]=
{
    {296.42f, -355.01f, 90.94f, 0.0f},
    {302.36f, -352.01f, 90.54f, 0.0f},
    {291.39f, -350.89f, 90.54f, 0.0f}
};

/*######
## Boss Svala
######*/

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

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

    struct boss_svalaAI : public ScriptedAI
    {
        boss_svalaAI(Creature* creature) : ScriptedAI(creature), summons(me)
        {
            instance = creature->GetInstanceScript();
            Started = false;
            ArthasGUID = 0;
        }

        uint64 ArthasGUID;
        bool Started;
        InstanceScript* instance;
        EventMap events;
        EventMap events2;
        SummonList summons;

        void Reset()
        {
            if (instance)
            {
                instance->SetData(DATA_SVALA_SORROWGRAVE, NOT_STARTED);
                instance->SetData(DATA_SVALA_ACHIEVEMENT, false);
            }

            summons.DespawnAll();
            events.Reset();
            events2.Reset();
            if (!Started)
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
            else
            {
                me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
                me->SetHover(true);
            }
        }

        void EnterEvadeMode()
        {
            me->SetControlled(false, UNIT_STATE_ROOT);
            ScriptedAI::EnterEvadeMode();
        }

        void SetData(uint32 data, uint32 param)
        {
            if (data != 1 || param != 1 || Started || instance->GetData(DATA_SVALA_SORROWGRAVE) == DONE)
                return;

            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
            Started = true;
            me->setActive(true);
            events2.ScheduleEvent(EVENT_SVALA_START, 5000);
            if (Creature* pArthas = me->SummonCreature(NPC_ARTHAS, 295.81f, -366.16f, 92.57f, 1.58f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 59000))
                ArthasGUID = pArthas->GetGUID();

            if (instance)
            {
                instance->SetData(DATA_SVALA_SORROWGRAVE, IN_PROGRESS);
                if (GameObject* mirror = ObjectAccessor::GetGameObject(*me, instance->GetData64(GO_SVALA_MIRROR)))
                    mirror->SetGoState(GO_STATE_READY);
            }
        }

        void JustSummoned(Creature* summon)
        {
            summons.Summon(summon);
            if (summon->GetEntry() == NPC_RITUAL_CHANNELER)
                summon->CastSpell(summon, SPELL_TELEPORT_VISUAL, true);
        }

        void EnterCombat(Unit*)
        {
            me->SetInCombatWithZone();
            Talk(SAY_AGGRO);

            events.ScheduleEvent(EVENT_SORROWGRAVE_SS, 3000);
            events.ScheduleEvent(EVENT_SORROWGRAVE_FLAMES, 11000);
            events.ScheduleEvent(EVENT_SORROWGRAVE_RITUAL, 25000);

            if (instance)
                instance->SetData(DATA_SVALA_SORROWGRAVE, IN_PROGRESS);
        }

        void JustDied(Unit*)
        {
            summons.DespawnAll();
            Talk(SAY_DEATH);
            if(instance)
                instance->SetData(DATA_SVALA_SORROWGRAVE, DONE);
        }

        void KilledUnit(Unit* victim)
        {
            if (victim->GetEntry() == NPC_SCOURGE_HULK && instance)
            {
                instance->SetData(DATA_SVALA_ACHIEVEMENT, true);
                instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, 26555, 1, NULL);
            }

            if (victim->GetTypeId() == TYPEID_PLAYER)
                Talk(SAY_SLAY);
        }

        void UpdateAI(uint32 diff)
        {
            events2.Update(diff);
            switch (events2.ExecuteEvent())
            {
                case EVENT_SVALA_START:
                    Talk(TALK_INTRO_S1);
                    events2.ScheduleEvent(EVENT_SVALA_TALK1, 8000);
                    break;
                case EVENT_SVALA_TALK1:
                    if (Creature* Arthas = ObjectAccessor::GetCreature(*me, ArthasGUID))
                        Arthas->AI()->Talk(TALK_INTRO_A1);
                    events2.ScheduleEvent(EVENT_SVALA_TALK2, 9000);
                    break;
                case EVENT_SVALA_TALK2:
                    if (Creature* Arthas = ObjectAccessor::GetCreature(*me, ArthasGUID))
                        Arthas->CastSpell(me, SPELL_ARTHAS_TRANSFORMING_SVALA, false);
                    me->CastSpell(me, SPELL_SVALA_TRANSFORMING2, true);
                    events2.ScheduleEvent(EVENT_SVALA_TALK3, 3000);
                    break;
                case EVENT_SVALA_TALK3:
                    me->SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 6.0f);
                    me->SetHover(true);
                    me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD);
                    events2.ScheduleEvent(30, 1000);
                    events2.ScheduleEvent(EVENT_SVALA_TALK4, 9000);
                    break;
                case 30:
                {
                    WorldPacket data(SMSG_SPLINE_MOVE_SET_HOVER, 9);
                    data.append(me->GetPackGUID());
                    me->SendMessageToSet(&data, false);
                    break;
                }
                case EVENT_SVALA_TALK4:
                {
                    me->CastSpell(me, SPELL_SVALA_TRANSFORMING1, true);
                    me->UpdateEntry(NPC_SVALA_SORROWGRAVE);
                    me->SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 6.0f);
                    me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
                    if (Creature* Arthas = ObjectAccessor::GetCreature(*me, ArthasGUID))
                        Arthas->InterruptNonMeleeSpells(false);
                    me->RemoveAllAuras();
                    me->SetWalk(false);
                    events2.ScheduleEvent(EVENT_SVALA_TALK5, 2000);

                    std::list<Creature*> creatureList;
                    me->GetCreaturesWithEntryInRange(creatureList, 100.0f, NPC_DRAGONFLAYER_SPECTATOR);
                    for (std::list<Creature*>::const_iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr)
                        (*itr)->AI()->SetData(1, 2);

                    break;
                }
                case EVENT_SVALA_TALK5:
                    Talk(TALK_INTRO_S2);
                    events2.ScheduleEvent(EVENT_SVALA_TALK6, 12000);
                    break;
                case EVENT_SVALA_TALK6:
                    if (Creature *Arthas = ObjectAccessor::GetCreature(*me, ArthasGUID))
                        Arthas->AI()->Talk(TALK_INTRO_A2);
                    events2.ScheduleEvent(EVENT_SVALA_TALK7, 9000);
                    break;
                case EVENT_SVALA_TALK7:
                    me->SetFacingTo(M_PI/2.0f);
                    Talk(TALK_INTRO_S3);
                    if (GameObject* mirror = ObjectAccessor::GetGameObject(*me, instance->GetData64(GO_SVALA_MIRROR)))
                        mirror->SetGoState(GO_STATE_ACTIVE);
                    events2.ScheduleEvent(EVENT_SVALA_TALK8, 13000);
                    break;
                case EVENT_SVALA_TALK8:
                    me->GetMotionMaster()->MoveFall(0, true);
                    events2.ScheduleEvent(EVENT_SVALA_TALK9, 2000);
                    break;
                case EVENT_SVALA_TALK9:
                    me->SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 3.0f);
                    me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
                    me->LoadEquipment(1, true);
                    me->setActive(false);
                    if (Player* target = SelectTargetFromPlayerList(100.0f))
                        AttackStart(target);
                    return;
            }

            if (!UpdateVictim())
                return;

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

            switch (events.ExecuteEvent())
            {
                case EVENT_SORROWGRAVE_SS:
                    me->CastSpell(me->GetVictim(), IsHeroic() ? SPELL_SINSTER_STRIKE_H : SPELL_SINSTER_STRIKE_N, false);
                    events.ScheduleEvent(EVENT_SORROWGRAVE_SS, urand(3000, 5000));
                    break;
                case EVENT_SORROWGRAVE_FLAMES:
                    summons.DespawnAll();
                    me->CastSpell(me, SPELL_CALL_FLAMES, false);
                    events.ScheduleEvent(EVENT_SORROWGRAVE_FLAMES2, 500);
                    events.ScheduleEvent(EVENT_SORROWGRAVE_FLAMES2, 1000);
                    events.ScheduleEvent(EVENT_SORROWGRAVE_FLAMES, urand(8000, 12000));
                    break;
                case EVENT_SORROWGRAVE_FLAMES2:
                {
                    std::list<Creature*> braziers;
                    me->GetCreaturesWithEntryInRange(braziers, 100.0f, NPC_FLAME_BRAZIER);
                    if (!braziers.empty())
                    {
                        for (std::list<Creature*>::const_iterator itr = braziers.begin(); itr != braziers.end(); ++itr)
                            (*itr)->CastCustomSpell(SPELL_BALL_OF_FLAME, SPELLVALUE_MAX_TARGETS, 1, (*itr), true);
                    }
                    break;
                }
                case EVENT_SORROWGRAVE_RITUAL:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                    {
                        Talk(SAY_SACRIFICE_PLAYER);

                        for (uint8 i = 0; i < 3; ++i)
                            if (Creature* cr = me->SummonCreature(NPC_RITUAL_CHANNELER, RitualChannelerLoc[i], TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000))
                                cr->AI()->AttackStart(target);

                        me->GetMotionMaster()->MoveIdle();
                        DoTeleportPlayer(target, 296.632f, -346.075f, 90.63f, 4.6f);
                        me->NearTeleportTo(296.632f, -346.075f, 110.0f, 4.6f, false);
                        me->SetControlled(true, UNIT_STATE_ROOT);
                        me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
                        me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
                        me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
                    }

                    events.DelayEvents(25001); // +1 just to be sure
                    events.ScheduleEvent(EVENT_SORROWGRAVE_RITUAL_SPELLS, 0);
                    events.ScheduleEvent(EVENT_SORROWGRAVE_FINISH_RITUAL, 25000);
                    return;
                case EVENT_SORROWGRAVE_RITUAL_SPELLS:
                    //me->CastSpell(me, SPELL_RITUAL_OF_THE_SWORD, false);
                    me->CastSpell(me, SPELL_RITUAL_STRIKE, true);
                    return;
                case EVENT_SORROWGRAVE_FINISH_RITUAL:
                    me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
                    me->SetControlled(false, UNIT_STATE_ROOT);
                    AttackStart(me->GetVictim());
                    me->GetMotionMaster()->MoveFall(0, true);
                    summons.DespawnAll();
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

/*######
## Mob Ritual Channeler
######*/

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

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_ritual_channelerAI (pCreature);
    }

    struct npc_ritual_channelerAI : public NullCreatureAI
    {
        npc_ritual_channelerAI(Creature* pCreature) : NullCreatureAI(pCreature) {}

        void AttackStart(Unit* pWho)
        {
            if (me->GetMap()->GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC)
                me->AddAura(SPELL_SHADOWS_IN_THE_DARK, me);

            if (pWho)
            {
                me->AddThreat(pWho, 10000000.0f);
                me->CastSpell(pWho, SPELL_PARALYZE, false);
            }
        }
    };
};

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

        class spell_svala_ritual_strike_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_svala_ritual_strike_SpellScript);

            void HandleDummyEffect(SpellEffIndex /*effIndex*/)
            {
                if (Unit* unitTarget = GetHitUnit())
                {
                    if (unitTarget->GetTypeId() != TYPEID_UNIT)
                        return;

                    Unit::DealDamage(GetCaster(), unitTarget, 7000, NULL, DIRECT_DAMAGE);
                }
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_svala_ritual_strike_SpellScript::HandleDummyEffect, EFFECT_2, SPELL_EFFECT_DUMMY);
            }
        };

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

        class spell_svala_ritual_strike_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_svala_ritual_strike_AuraScript);

            void CalculateAmount(AuraEffect const * /*aurEff*/, int32 & amount, bool & /*canBeRecalculated*/)
            {
                // Set amount based on difficulty
                amount = (GetCaster()->GetMap()->IsHeroic() ? 2000 : 1000);
            }

            void Register()
            {
                DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_svala_ritual_strike_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE);
            }
        };

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

void AddSC_boss_svala()
{
    new boss_svala();
    new npc_ritual_channeler();
    new spell_svala_ritual_strike();
}
