/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "serpent_shrine.h"
#include "Spell.h"
#include "Player.h"
#include "WorldSession.h"

enum Says
{
    SAY_INTRO                       = 0,
    SAY_AGGRO                       = 1,
    SAY_PHASE1                      = 2,
    SAY_PHASE2                      = 3,
    SAY_PHASE3                      = 4,
    SAY_BOWSHOT                     = 5,
    SAY_SLAY                        = 6,
    SAY_DEATH                       = 7
};

enum Spells
{
    SPELL_SHOOT                     = 37770,
    SPELL_MULTI_SHOT                = 38310,
    SPELL_SHOCK_BLAST               = 38509,
    SPELL_STATIC_CHARGE             = 38280,
    SPELL_ENTANGLE                  = 38316,
    SPELL_MAGIC_BARRIER             = 38112,
    SPELL_FORKED_LIGHTNING          = 38145,

    SPELL_SUMMON_ENCHANTED_ELEMENTAL= 38017,
    SPELL_SUMMON_COILFANG_ELITE     = 38248,
    SPELL_SUMMON_COILFANG_STRIDER   = 38241,
    SPELL_SUMMON_TAINTED_ELEMENTAL  = 38140,
    SPELL_SURGE                     = 38044,

    SPELL_REMOVE_TAINTED_CORES      = 39495,
    SPELL_SUMMON_TOXIC_SPOREBAT     = 38494,
    SPELL_SUMMON_SPOREBAT1          = 38489,
    SPELL_SUMMON_SPOREBAT2          = 38490,
    SPELL_SUMMON_SPOREBAT3          = 38492,
    SPELL_SUMMON_SPOREBAT4          = 38493,
    SPELL_TOXIC_SPORES              = 38574
};

enum Misc
{
    ITEM_TAINTED_CORE               = 31088,

    POINT_HOME                      = 1,

    EVENT_SPELL_SHOCK_BLAST         = 1,
    EVENT_SPELL_STATIC_CHARGE       = 2,
    EVENT_SPELL_ENTANGLE            = 3,
    EVENT_CHECK_HEALTH              = 4,
    EVENT_SPELL_FORKED_LIGHTNING    = 5,
    EVENT_SUMMON_A                  = 6,
    EVENT_SUMMON_B                  = 7,
    EVENT_SUMMON_C                  = 8,
    EVENT_SUMMON_D                  = 9,
    EVENT_CHECK_HEALTH2             = 10,
    EVENT_SUMMON_SPOREBAT           = 11,

    EVENT_KILL_TALK                 = 20
};

class startFollow : public BasicEvent
{
    public:
        startFollow(Unit* owner) : _owner(owner)  { }

        bool Execute(uint64 /*execTime*/, uint32 /*diff*/)
        {
            if (InstanceScript* instance = _owner->GetInstanceScript())
                if (Creature* vashj = ObjectAccessor::GetCreature(*_owner, instance->GetData64(NPC_LADY_VASHJ)))
                    _owner->GetMotionMaster()->MoveFollow(vashj, 3.0f, vashj->GetAngle(_owner), MOTION_SLOT_CONTROLLED);
            return true;
        }

    private:
        Unit* _owner;
};

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

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

        struct boss_lady_vashjAI : public BossAI
        {
            boss_lady_vashjAI(Creature* creature) : BossAI(creature, DATA_LADY_VASHJ)
            {
                intro = false;
            }

            bool intro;
            int32 count;

            void Reset()
            {
                count = 0;
                BossAI::Reset();
            }

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

            void JustDied(Unit* killer)
            {
                Talk(SAY_DEATH);
                BossAI::JustDied(killer);
            }

            void EnterCombat(Unit* who)
            {
                BossAI::EnterCombat(who);
                Talk(SAY_AGGRO);

                me->CastSpell(me, SPELL_REMOVE_TAINTED_CORES, true);
                events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 10000);
                events.ScheduleEvent(EVENT_SPELL_STATIC_CHARGE, 15000);
                events.ScheduleEvent(EVENT_SPELL_ENTANGLE, 20000);
                events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
            }

            void JustSummoned(Creature* summon)
            {
                summons.Summon(summon);
                if (summon->GetEntry() == WORLD_TRIGGER)
                    summon->CastSpell(summon, SPELL_MAGIC_BARRIER, false);
                else if (summon->GetEntry() == NPC_ENCHANTED_ELEMENTAL)
                {
                    summon->SetWalk(true);
                    summon->m_Events.AddEvent(new startFollow(summon), summon->m_Events.CalculateTime(0));
                }
                else if (summon->GetEntry() == NPC_TOXIC_SPOREBAT)
                    summon->GetMotionMaster()->MoveRandom(30.0f);
                else if (summon->GetEntry() != NPC_TAINTED_ELEMENTAL)
                    summon->GetMotionMaster()->MovePoint(POINT_HOME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), true, true);
            }

            void MoveInLineOfSight(Unit* who)
            {
                if (!intro && who->GetTypeId() == TYPEID_PLAYER)
                {
                    intro = true;
                    Talk(SAY_INTRO);
                }

                BossAI::MoveInLineOfSight(who);
            }

            void MovementInform(uint32 type, uint32 id)
            {
                if (type != POINT_MOTION_TYPE || id != POINT_HOME)
                    return;

                me->SetFacingTo(me->GetHomePosition().GetOrientation());
                instance->SetData(DATA_ACTIVATE_SHIELD, 0);
                events.Reset();
                events.ScheduleEvent(EVENT_SPELL_FORKED_LIGHTNING, 3000);
                events.ScheduleEvent(EVENT_SUMMON_A, 0);
                events.ScheduleEvent(EVENT_SUMMON_B, 45000);
                events.ScheduleEvent(EVENT_SUMMON_C, 60000);
                events.ScheduleEvent(EVENT_SUMMON_D, 50000);
                events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
            }

            void UpdateAI(uint32 diff)
            {
                EnterEvadeIfOutOfCombatArea();
                if (!UpdateVictim())
                    return;

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

                switch (events.ExecuteEvent())
                {
                    case EVENT_SPELL_SHOCK_BLAST:
                        me->CastSpell(me->GetVictim(), SPELL_SHOCK_BLAST, false);
                        events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, urand(10000, 20000));
                        break;
                    case EVENT_SPELL_STATIC_CHARGE:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f))
                            me->CastSpell(target, SPELL_STATIC_CHARGE, false);
                        events.ScheduleEvent(EVENT_SPELL_STATIC_CHARGE, 20000);
                        break;
                    case EVENT_SPELL_ENTANGLE:
                        me->CastSpell(me, SPELL_ENTANGLE, false);
                        events.ScheduleEvent(EVENT_SPELL_ENTANGLE, 30000);
                        break;
                    case EVENT_CHECK_HEALTH:
                        if (me->HealthBelowPct(71))
                        {
                            Talk(SAY_PHASE2);
                            me->SetReactState(REACT_PASSIVE);
                            me->GetMotionMaster()->MovePoint(POINT_HOME, me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY(), me->GetHomePosition().GetPositionZ(), true, true);
                            break;
                        }
                        events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
                        break;
                    case EVENT_SPELL_FORKED_LIGHTNING:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f))
                            me->CastSpell(target, SPELL_FORKED_LIGHTNING, false);
                        events.ScheduleEvent(EVENT_SPELL_FORKED_LIGHTNING, urand(2500, 5000));
                        break;
                    case EVENT_SUMMON_A:
                        me->CastSpell(me, SPELL_SUMMON_ENCHANTED_ELEMENTAL, true);
                        events.ScheduleEvent(EVENT_SUMMON_A, 2500);
                        break;
                    case EVENT_SUMMON_B:
                        me->CastSpell(me, SPELL_SUMMON_COILFANG_ELITE, true);
                        events.ScheduleEvent(EVENT_SUMMON_B, 45000);
                        break;
                    case EVENT_SUMMON_C:
                        me->CastSpell(me, SPELL_SUMMON_COILFANG_STRIDER, true);
                        events.ScheduleEvent(EVENT_SUMMON_C, 60000);
                        break;
                    case EVENT_SUMMON_D:
                        me->CastSpell(me, SPELL_SUMMON_TAINTED_ELEMENTAL, true);
                        events.ScheduleEvent(EVENT_SUMMON_D, 50000);
                        break;
                    case EVENT_CHECK_HEALTH2:
                        if (!me->HasAura(SPELL_MAGIC_BARRIER))
                        {
                            Talk(SAY_PHASE3);
                            me->SetReactState(REACT_AGGRESSIVE);
                            me->GetMotionMaster()->MoveChase(me->GetVictim());
                            events.Reset();
                            events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 10000);
                            events.ScheduleEvent(EVENT_SPELL_STATIC_CHARGE, 15000);
                            events.ScheduleEvent(EVENT_SPELL_ENTANGLE, 20000);
                            events.ScheduleEvent(EVENT_SUMMON_SPOREBAT, 5000);
                            break;
                        }
                        events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
                        break;
                    case EVENT_SUMMON_SPOREBAT:
                        me->CastSpell(me, SPELL_SUMMON_TOXIC_SPOREBAT, true);
                        events.ScheduleEvent(EVENT_SUMMON_SPOREBAT, 20000 - 1000*std::min(count++, 16));
                        break;
                }

                if (me->GetReactState() != REACT_AGGRESSIVE || !me->isAttackReady())
                    return;

                if (!me->IsWithinMeleeRange(me->GetVictim()))
                {
                    me->resetAttackTimer();
                    me->SetSheath(SHEATH_STATE_RANGED);
                    me->CastSpell(me->GetVictim(), roll_chance_i(33) ? SPELL_MULTI_SHOT : SPELL_SHOOT, false);
                    if (roll_chance_i(15))
                        Talk(SAY_BOWSHOT);
                }
                else
                {
                    me->SetSheath(SHEATH_STATE_MELEE);
                    DoMeleeAttackIfReady();
                }
            }
                    
            bool CheckEvadeIfOutOfCombatArea() const
            {
                return me->GetHomePosition().GetExactDist2d(me) > 80.0f || !SelectTargetFromPlayerList(100.0f);
            }
        };
};
/*

//Toxic Sporebat
//Toxic Spores: Used in Phase 3 by the Spore Bats, it creates a contaminated green patch of ground, dealing about 2775-3225 nature damage every second to anyone who stands in it.
class npc_toxic_sporebat : public CreatureScript
{
public:
    npc_toxic_sporebat() : CreatureScript("npc_toxic_sporebat") { }

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

    struct npc_toxic_sporebatAI : public ScriptedAI
    {
        npc_toxic_sporebatAI(Creature* creature) : ScriptedAI(creature)
        {
            instance = creature->GetInstanceScript();
            EnterEvadeMode();
        }

        InstanceScript* instance;

        uint32 MovementTimer;
        uint32 ToxicSporeTimer;
        uint32 BoltTimer;
        uint32 CheckTimer;

        void Reset()
        {
            me->SetDisableGravity(true);
            me->setFaction(14);
            MovementTimer = 0;
            ToxicSporeTimer = 5000;
            BoltTimer = 5500;
            CheckTimer = 1000;
        }

        void MoveInLineOfSight(Unit* who)

        {
        }

        void MovementInform(uint32 type, uint32 id)
        {
            if (type != POINT_MOTION_TYPE)
                return;

            if (id == 1)
                MovementTimer = 0;
        }

        void UpdateAI(uint32 diff)
        {
            // Random movement
            if (MovementTimer <= diff)
            {
                uint32 rndpos = rand()%8;
                me->GetMotionMaster()->MovePoint(1, SporebatWPPos[rndpos][0], SporebatWPPos[rndpos][1], SporebatWPPos[rndpos][2]);
                MovementTimer = 6000;
            } else MovementTimer -= diff;

            // toxic spores
            if (BoltTimer <= diff)
            {
                if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                {
                    if (Creature* trig = me->SummonCreature(TOXIC_SPORES_TRIGGER, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 30000))
                    {
                        trig->setFaction(14);
                        trig->CastSpell(trig, SPELL_TOXIC_SPORES, true);
                    }
                }
                BoltTimer = 10000+rand()%5000;
            }
            else BoltTimer -= diff;

            // CheckTimer
            if (CheckTimer <= diff)
            {
                // check if vashj is death
                Unit* Vashj = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_LADYVASHJ));
                if (!Vashj || !Vashj->IsAlive() || CAST_AI(boss_lady_vashj::boss_lady_vashjAI, Vashj->ToCreature()->AI())->Phase != 3)
                {
                    // remove
                    me->setDeathState(DEAD);
                    me->RemoveCorpse();
                    me->setFaction(35);
                }

                CheckTimer = 1000;
            }
            else
                CheckTimer -= diff;
        }
    };

};
*/

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

        class spell_lady_vashj_magic_barrier_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_lady_vashj_magic_barrier_AuraScript)

            void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
            {
                Unit::DealDamage(GetTarget(), GetTarget(), GetTarget()->CountPctFromMaxHealth(5));
            }

            void Register()
            {
                AfterEffectRemove += AuraEffectRemoveFn(spell_lady_vashj_magic_barrier_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_SCHOOL_IMMUNITY, AURA_EFFECT_HANDLE_REAL);
            }
        };

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

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

        class spell_lady_vashj_remove_tainted_cores_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_lady_vashj_remove_tainted_cores_SpellScript);

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
                PreventHitDefaultEffect(effIndex);
                if (Player* target = GetHitPlayer())
                    target->DestroyItemCount(ITEM_TAINTED_CORE, -1, true);
            }

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

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

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

        class spell_lady_vashj_summon_sporebat_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_lady_vashj_summon_sporebat_SpellScript);

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
                PreventHitDefaultEffect(effIndex);
                GetCaster()->CastSpell(GetCaster(), RAND(SPELL_SUMMON_SPOREBAT1, SPELL_SUMMON_SPOREBAT2, SPELL_SUMMON_SPOREBAT3, SPELL_SUMMON_SPOREBAT4), true);
            }

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

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

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

        class spell_lady_vashj_spore_drop_effect_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_lady_vashj_spore_drop_effect_SpellScript);

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
                PreventHitDefaultEffect(effIndex);
                if (Unit* target = GetHitUnit())
                    target->CastSpell(target, SPELL_TOXIC_SPORES, true, NULL, NULL, GetCaster()->GetGUID());
            }

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

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

void AddSC_boss_lady_vashj()
{
    new boss_lady_vashj();
    new spell_lady_vashj_magic_barrier();
    new spell_lady_vashj_remove_tainted_cores();
    new spell_lady_vashj_summon_sporebat();
    new spell_lady_vashj_spore_drop_effect();
}
