/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "gundrak.h"

enum Spells
{
    SPELL_START_VISUAL                  = 54988,
    SPELL_ENRAGE                        = 55285,
    SPELL_IMPALING_CHARGE               = 54956,
    SPELL_IMPALING_CHARGE_VEHICLE       = 54958,
    SPELL_STOMP                         = 55292,
    SPELL_PUNCTURE                      = 55276,
    SPELL_STAMPEDE                      = 55218,
    SPELL_STAMPEDE_DMG                  = 55220,
    SPELL_WHIRLING_SLASH                = 55250,
    SPELL_TRANSFORM_TO_RHINO            = 55297,
    SPELL_TRANSFORM_TO_TROLL            = 55299
};

enum Yells
{
    SAY_AGGRO                           = 0,
    SAY_SLAY                            = 1,
    SAY_DEATH                           = 2,
    SAY_SUMMON_RHINO                    = 3,
    SAY_TRANSFORM_1                     = 4,
    SAY_TRANSFORM_2                     = 5
};

enum Events
{
    EVENT_STAMPEDE                      = 1,
    EVENT_WHIRLING_SLASH                = 2,
    EVENT_PUNCTURE                      = 3,
    EVENT_ENRAGE                        = 4,
    EVENT_IMPALING_CHARGE               = 5,
    EVENT_UNSUMMON_RHINO                = 6,
    EVENT_STOMP                         = 7,
    EVENT_KILL_TALK                     = 8
};

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

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

        struct boss_gal_darahAI : public BossAI
        {
            boss_gal_darahAI(Creature* creature) : BossAI(creature, DATA_GAL_DARAH)
            {
            }

            uint8 phaseCounter;
            std::set<uint64> impaledList;

            void Reset()
            {
                BossAI::Reset();
                impaledList.clear();
                phaseCounter = 0;
            }

            void InitializeAI()
            {
                BossAI::InitializeAI();
                me->CastSpell(me, SPELL_START_VISUAL, false);
            }

            void JustReachedHome()
            {
                BossAI::JustReachedHome();
                me->CastSpell(me, SPELL_START_VISUAL, false);
            }

            void ScheduleEvents(bool troll)
            {
                events.Reset();
                if (troll)
                {
                    events.RescheduleEvent(EVENT_STAMPEDE, 10000);
                    events.RescheduleEvent(EVENT_WHIRLING_SLASH, 21000);
                }
                else
                {
                    events.RescheduleEvent(EVENT_PUNCTURE, 10000);
                    events.RescheduleEvent(EVENT_ENRAGE, 15000);
                    events.RescheduleEvent(EVENT_IMPALING_CHARGE, 21000);
                    events.RescheduleEvent(EVENT_STOMP, 5000);
                }
            }

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

                ScheduleEvents(true);
                me->RemoveAurasDueToSpell(SPELL_START_VISUAL);
                me->InterruptNonMeleeSpells(true);
            }

            void JustSummoned(Creature* summon)
            {
                uint32 despawnTime = 0;
                if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true))
                {
                    summon->CastSpell(target, SPELL_STAMPEDE_DMG, true);
                    despawnTime = (summon->GetDistance(target)/40.0f*1000)+500;
                }

                summon->DespawnOrUnsummon(despawnTime);
            }

            uint32 GetData(uint32 type) const
            {
                return impaledList.size();
            }

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

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

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

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

                switch (events.ExecuteEvent())
                {
                    case EVENT_STAMPEDE:
                        Talk(SAY_SUMMON_RHINO);
                        me->CastSpell(me->GetVictim(), SPELL_STAMPEDE, false);
                        events.ScheduleEvent(EVENT_STAMPEDE, 15000);
                        break;
                    case EVENT_WHIRLING_SLASH:
                        if (++phaseCounter >= 3)
                        {
                            ScheduleEvents(false);
                            me->CastSpell(me, SPELL_TRANSFORM_TO_RHINO, false);
                            Talk(SAY_TRANSFORM_1);
                            phaseCounter = 0;
                            return;
                        }
                        events.ScheduleEvent(EVENT_WHIRLING_SLASH, 21000);
                        me->CastSpell(me, SPELL_WHIRLING_SLASH, false);
                        break;
                    case EVENT_PUNCTURE:
                        me->CastSpell(me->GetVictim(), SPELL_PUNCTURE, false);
                        events.ScheduleEvent(EVENT_PUNCTURE, 8000);
                        break;
                    case EVENT_ENRAGE:
                        me->CastSpell(me, SPELL_ENRAGE, false);
                        events.ScheduleEvent(EVENT_ENRAGE, 20000);
                        break;
                    case EVENT_STOMP:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true))
                            me->CastSpell(target, SPELL_STOMP, false);
                        events.ScheduleEvent(EVENT_STOMP, 20000);
                        break;
                    case EVENT_IMPALING_CHARGE:
                        if (++phaseCounter >= 3)
                        {
                            ScheduleEvents(true);
                            me->CastSpell(me, SPELL_TRANSFORM_TO_TROLL, false);
                            Talk(SAY_TRANSFORM_2);
                            phaseCounter = 0;
                            return;
                        }
                        events.ScheduleEvent(EVENT_IMPALING_CHARGE, 21000);
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true))
                        {
                            me->CastSpell(target, SPELL_IMPALING_CHARGE, false);
                            impaledList.insert(target->GetGUID());
                        }
                        break;
                }

                DoMeleeAttackIfReady();
            }
        };
};

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

        class spell_galdarah_impaling_charge_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_galdarah_impaling_charge_SpellScript);

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

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_galdarah_impaling_charge_SpellScript::HandleApplyAura, EFFECT_1, SPELL_EFFECT_APPLY_AURA);
            }
        };

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

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

        class spell_galdarah_transform_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_galdarah_transform_SpellScript);

            void HandleScriptEffect(SpellEffIndex /*effIndex*/)
            {
                if (Unit* target = GetHitUnit())
                    target->RemoveAurasDueToSpell(SPELL_TRANSFORM_TO_RHINO);
            }

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

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

class achievement_share_the_love : public AchievementCriteriaScript
{
    public:
        achievement_share_the_love() : AchievementCriteriaScript("achievement_share_the_love")
        {
        }

        bool OnCheck(Player* /*player*/, Unit* target)
        {
            if (!target)
                return false;

            return target->GetAI()->GetData(target->GetEntry()) >= 5;
        }
};

void AddSC_boss_gal_darah()
{
    new boss_gal_darah();
    new spell_galdarah_impaling_charge();
    new spell_galdarah_transform();
    new achievement_share_the_love();
}
