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

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
#include "SpellScript.h"
#include "Player.h"

enum Spells
{
    SPELL_MORTAL_WOUND                  = 25646,
    SPELL_ENRAGE_10                     = 28371,
    SPELL_ENRAGE_25                     = 54427,
    SPELL_DECIMATE_10                   = 28374,
    SPELL_DECIMATE_25                   = 54426,
    SPELL_BERSERK                       = 26662,
    SPELL_INFECTED_WOUND                = 29306,
    SPELL_CHOW_SEARCHER                 = 28404,
};

enum Events
{
    EVENT_SPELL_MORTAL_WOUND            = 1,
    EVENT_SPELL_ENRAGE                  = 2,
    EVENT_SPELL_DECIMATE                = 3,
    EVENT_SPELL_BERSERK                 = 4,
    EVENT_SUMMON_ZOMBIE                 = 5,
    EVENT_CAN_EAT_ZOMBIE                = 6,
};

enum Misc
{
    NPC_ZOMBIE_CHOW                     = 16360,
};

const Position zombiePos[3] =
{
    {3267.9f, -3172.1f, 297.42f, 0.94f},
    {3253.2f, -3132.3f, 297.42f, 0},
    {3308.3f, -3185.8f, 297.42f, 1.58f},
};

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

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

    struct boss_gluthAI : public ScriptedAI
    {
        boss_gluthAI(Creature *c) : ScriptedAI(c), summons(me)
        {
            pInstance = me->GetInstanceScript();
        }

        EventMap events;
        SummonList summons;
        InstanceScript* pInstance;
        uint64 gazeTarget;
        
        void Reset()
        {
            me->ApplySpellImmune(29306, IMMUNITY_ID, 29306, true);
            events.Reset();
            summons.DespawnAll();
            gazeTarget = 0;
            me->SetReactState(REACT_AGGRESSIVE);

            if (pInstance)
                pInstance->SetData(EVENT_GLUTH, NOT_STARTED);
        }

        void MoveInLineOfSight(Unit *who)
        {
            if ((!me->GetVictim() || me->GetVictim()->GetEntry() != NPC_ZOMBIE_CHOW) && who->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinDistInMap(who, 15))
            {
                SetGazeOn(who);
                me->MonsterTextEmote("%s spots a nearby zombie to devour!", 0, false);
            }
            else
                ScriptedAI::MoveInLineOfSight(who);
        }

        void EnterCombat(Unit *who)
        {
            me->SetInCombatWithZone();
            events.ScheduleEvent(EVENT_SPELL_MORTAL_WOUND, 10000);
            events.ScheduleEvent(EVENT_SPELL_ENRAGE, 30000);
            events.ScheduleEvent(EVENT_SPELL_DECIMATE, 105000);
            events.ScheduleEvent(EVENT_SPELL_BERSERK, 8*60000);
            events.ScheduleEvent(EVENT_SUMMON_ZOMBIE, 10000);
            events.ScheduleEvent(EVENT_CAN_EAT_ZOMBIE, 1000);

            if (pInstance)
                pInstance->SetData(EVENT_GLUTH, IN_PROGRESS);
        }

        void JustSummoned(Creature *summon)
        {
            if (summon->GetEntry() == NPC_ZOMBIE_CHOW)
                summon->AI()->AttackStart(me);

            summons.Summon(summon);
        }

        void SummonedCreatureDies(Creature* cr, Unit*) { summons.Despawn(cr); }

        void KilledUnit(Unit* who)
        {
            if (me->IsAlive() && who->GetEntry() == NPC_ZOMBIE_CHOW)
                me->ModifyHealth(int32(me->GetMaxHealth()*0.05f));

            if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
                pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
        }

        void JustDied(Unit*)
        {
            summons.DespawnAll();
            if (pInstance)
                pInstance->SetData(EVENT_GLUTH, DONE);
        }

        bool SelectPlayerInRoom()
        {
            if (me->IsInCombat())
                return false;

            Map::PlayerList const& pList = me->GetMap()->GetPlayers();
            for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
            {
                Player* player = itr->GetSource();
                if (!player || !player->IsAlive())
                    continue;
                if (player->GetPositionZ() > 300.0f || me->GetExactDist(player) > 50.0f)
                    continue;

                AttackStart(player);
                return true;
            }

            return false;
        }

        void UpdateAI(uint32 diff)
        {
            if (!UpdateVictimWithGaze() && !SelectPlayerInRoom())
                return;

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

            switch (events.GetEvent())
            {
                case EVENT_SPELL_BERSERK:
                    me->CastSpell(me, SPELL_BERSERK, true);
                    events.PopEvent();
                    break;
                case EVENT_SPELL_ENRAGE:
                    me->MonsterTextEmote("Gluth becomes enraged!", 0, true);
                    me->CastSpell(me, RAID_MODE(SPELL_ENRAGE_10, SPELL_ENRAGE_25), true);
                    events.RepeatEvent(30000);
                    break;
                case EVENT_SPELL_MORTAL_WOUND:
                    me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false);
                    events.RepeatEvent(10000);
                    break;
                case EVENT_SPELL_DECIMATE:
                    me->MonsterTextEmote("Gluth decimates all nearby flesh!", 0, true);
                    me->CastSpell(me, RAID_MODE(SPELL_DECIMATE_10, SPELL_DECIMATE_25), false);
                    events.RepeatEvent(105000);
                    break;
                case EVENT_SUMMON_ZOMBIE:
                    {
                    uint8 rand = urand(0,2);
                    for (int32 i = 0; i < RAID_MODE(1,2); ++i)
                    {
                        me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[urand(0,2)]);
                        (rand == 2 ? rand = 0 : rand++);
                    }

                    events.RepeatEvent(10000);
                    break;
                    }
                case EVENT_CAN_EAT_ZOMBIE:
                    events.RepeatEvent(1000);
                    if (me->GetVictim()->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinMeleeRange(me->GetVictim()))
                    {
                        me->CastCustomSpell(SPELL_CHOW_SEARCHER, SPELLVALUE_RADIUS_MOD, 20000, me, true);
                        me->MonsterTextEmote("%s devour all nearby zombies!", 0, false);
                        return; // leave it to skip DoMeleeAttackIfReady
                    }
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

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

        class spell_gluth_decimate_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_gluth_decimate_SpellScript);

            void HandleScriptEffect(SpellEffIndex /*effIndex*/)
            {
                if (Unit* unitTarget = GetHitUnit())
                {
                    int32 damage = int32(unitTarget->GetHealth()) - int32(unitTarget->CountPctFromMaxHealth(5));
                    if (damage <= 0)
                        return;

                    if (Creature* cTarget = unitTarget->ToCreature())
                    {
                        cTarget->SetWalk(true);
                        cTarget->GetMotionMaster()->MoveFollow(GetCaster(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED);
                        cTarget->SetReactState(REACT_PASSIVE);
                        Unit::DealDamage(GetCaster(), cTarget, damage);
                        return;
                    }

                    GetCaster()->CastCustomSpell(28375, SPELLVALUE_BASE_POINT0, damage, unitTarget);
                }
            }

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

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

void AddSC_boss_gluth()
{
    new boss_gluth();
    new spell_gluth_decimate();
}

