/* Xinef
*/

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

enum spells
{
    GROUND_SPIKE_H              = 59750,
    BOULDER_TOSS                = 50843,
    BOULDER_TOSS_H              = 59742,
    SHATTER                     = 50810,
    SHATTER_H                   = 61546,
    STOMP                       = 50868,
    STOMP_H                     = 59744,
    GROUND_SLAM                 = 50827,
    GROUND_SLAM_STONED_EFFECT   = 50812,
    SPELL_SHATTER_EFFECT        = 50811,
};

enum events
{
    EVENT_NONE,
    EVENT_BOULDER,
    EVENT_STOMP,
    EVENT_GROUND_SLAM,
    EVENT_GROUND_SPIKE,
    EVENT_SHATTER,
    EVENT_REMOVE_STONED,
};

enum Yells
{
    SAY_AGGRO                                   = 0,
    SAY_KILL                                    = 1,
    SAY_DEATH                                   = 2,
    SAY_SHATTER                                 = 3
};

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

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

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

        EventMap events;
        InstanceScript* pInstance;

        void Reset() 
        {
            events.Reset();
            if (pInstance)
                pInstance->SetData(BOSS_KRYSTALLUS, NOT_STARTED);
        }

        void EnterCombat(Unit* who)
        {
            events.Reset();
            events.RescheduleEvent(EVENT_BOULDER, 8000);
            events.RescheduleEvent(EVENT_STOMP, 5000);
            events.RescheduleEvent(EVENT_GROUND_SLAM, 15000);
            if (me->GetMap()->IsHeroic())
                events.RescheduleEvent(EVENT_GROUND_SPIKE, 10000);

            if (pInstance)
                pInstance->SetData(BOSS_KRYSTALLUS, IN_PROGRESS);

            Talk(SAY_AGGRO);
        }

        void RemoveStonedEffect()
        {
            Map* map = me->GetMap();
            if (map->IsDungeon())
            {
                Map::PlayerList const &players = map->GetPlayers();
                for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
                    if (itr->GetSource()->IsAlive())
                        itr->GetSource()->RemoveAura(GROUND_SLAM_STONED_EFFECT);
            }
        }

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

            events.Update(diff);

            if (me->HasUnitState(UNIT_STATE_CASTING))
                return;

            switch (events.GetEvent())
            {
                case EVENT_BOULDER:
                {
                    if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true, 0)) 
                        me->CastSpell(target, DUNGEON_MODE(BOULDER_TOSS, BOULDER_TOSS_H), false);

                    events.RepeatEvent(5000 + rand()%2000);
                    break;
                }
                case EVENT_GROUND_SPIKE:
                {
                    me->CastSpell(me->GetVictim(), GROUND_SPIKE_H, false); // current enemy target
                    events.RepeatEvent(8000 + rand()%3000);
                    break;
                }
                case EVENT_STOMP:
                {
                    me->CastSpell(me, DUNGEON_MODE(STOMP, STOMP_H), false);
                    events.RepeatEvent(13000 + rand()% 5000);
                    break;
                }
                case EVENT_GROUND_SLAM:
                {
                    events.RepeatEvent(10000 + rand()%3000);
                    me->CastSpell(me->GetVictim(), GROUND_SLAM, true);
                    events.DelayEvents(10000);
                    events.RescheduleEvent(EVENT_SHATTER, 8000);
                    break;
                }
                case EVENT_SHATTER:
                {
                    me->CastSpell((Unit*)NULL, DUNGEON_MODE(SHATTER, SHATTER_H), false);
                    Talk(SAY_SHATTER);
                    events.RescheduleEvent(EVENT_REMOVE_STONED, 1500);
                    events.PopEvent();
                    break;
                }
                case EVENT_REMOVE_STONED:
                {
                    RemoveStonedEffect();
                    events.PopEvent();
                    break;
                }
            }

            DoMeleeAttackIfReady();
        }

        void JustDied(Unit* killer)
        {
            Talk(SAY_DEATH);
            if (pInstance)
                pInstance->SetData(BOSS_KRYSTALLUS, DONE);
        }

        void KilledUnit(Unit *victim)
        {
            Talk(SAY_KILL);
        }
    };
};


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

        class spell_krystallus_shatter_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_krystallus_shatter_SpellScript);

            void HandleScript(SpellEffIndex /*effIndex*/)
            {
                if (Unit* target = GetHitUnit())
                {
                    target->RemoveAurasDueToSpell(GROUND_SLAM_STONED_EFFECT);
                    target->CastSpell((Unit*)NULL, SPELL_SHATTER_EFFECT, true);
                }
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_krystallus_shatter_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

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

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

        class spell_krystallus_shatter_effect_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_krystallus_shatter_effect_SpellScript);

            void CalculateDamage()
            {
                if (!GetHitUnit())
                    return;

                float radius = GetSpellInfo()->Effects[EFFECT_0].CalcRadius(GetCaster());
                if (!radius)
                    return;

                float distance = GetCaster()->GetDistance2d(GetHitUnit());
                if (distance > 1.0f)
                    SetHitDamage(int32(GetHitDamage() * ((radius - distance) / radius)));
            }

            void Register()
            {
                OnHit += SpellHitFn(spell_krystallus_shatter_effect_SpellScript::CalculateDamage);
            }
        };

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

void AddSC_boss_krystallus()
{
    new boss_krystallus();
    new spell_krystallus_shatter();
    new spell_krystallus_shatter_effect();
}