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

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "halls_of_lightning.h"
#include "SpellInfo.h"
#include "Player.h"

enum IonarSpells
{
    SPELL_BALL_LIGHTNING_N          = 52780,
    SPELL_BALL_LIGHTNING_H          = 59800,
    SPELL_STATIC_OVERLOAD_N         = 52658,
    SPELL_STATIC_OVERLOAD_H         = 59795,

    SPELL_DISPERSE                  = 52770,
    SPELL_SUMMON_SPARK              = 52746,
    SPELL_SPARK_DESPAWN             = 52776,

    //Spark of Ionar
    SPELL_SPARK_VISUAL_TRIGGER_N    = 52667,
    SPELL_SPARK_VISUAL_TRIGGER_H    = 59833,
    SPELL_RANDOM_LIGHTNING          = 52663,
};

enum IonarOther
{
    // NPCs
    NPC_SPARK_OF_IONAR              = 28926,

    // Actions
    ACTION_CALLBACK                 = 1,
    ACTION_SPARK_DESPAWN            = 2,
};

enum Yells
{
    SAY_AGGRO                       = 0,
    SAY_SPLIT                       = 1,
    SAY_SLAY                        = 2,
    SAY_DEATH                       = 3
};

enum IonarEvents
{
    EVENT_BALL_LIGHTNING            = 1,
    EVENT_STATIC_OVERLOAD           = 2,
    EVENT_CHECK_HEALTH              = 3,
    EVENT_CALL_SPARKS               = 4,
    EVENT_RESTORE                   = 5,
};

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

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

    struct boss_ionarAI : public ScriptedAI
    {
        boss_ionarAI(Creature* creature) : ScriptedAI(creature), summons(creature)
        {
            m_pInstance = creature->GetInstanceScript();
        }

        InstanceScript* m_pInstance;
        EventMap events;
        SummonList summons;
        uint8 HealthCheck;

        void Reset()
        {
            HealthCheck = 50;
            events.Reset();
            summons.DespawnAll();

            me->SetVisible(true);

            if (m_pInstance)
                m_pInstance->SetData(TYPE_IONAR, NOT_STARTED);

            // Ionar is immune to nature damage
            me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NATURE, true);
        }

        void ScheduleEvents(bool spark)
        {
            events.SetPhase(1);
            if (!spark)
                events.RescheduleEvent(EVENT_CHECK_HEALTH, 1000, 0, 1);

            events.RescheduleEvent(EVENT_BALL_LIGHTNING, 10000, 0, 1);
            events.RescheduleEvent(EVENT_STATIC_OVERLOAD, 5000, 0, 1);
        }

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

            if (m_pInstance)
                m_pInstance->SetData(TYPE_IONAR, IN_PROGRESS);

            ScheduleEvents(false);
        }

        void JustDied(Unit*)
        {
            Talk(SAY_DEATH);

            summons.DespawnAll();

            if (m_pInstance)
                m_pInstance->SetData(TYPE_IONAR, DONE);
        }

        void KilledUnit(Unit* victim)
        {
            if (victim->GetTypeId() != TYPEID_PLAYER)
                return;

            Talk(SAY_SLAY);
        }

        void SpellHit(Unit* /*caster*/, const SpellInfo* spell)
        {
            if (spell->Id == SPELL_DISPERSE)
                Split();
        }

        void Split()
        {
            Talk(SAY_SPLIT);

            Creature* spark;
            for (uint8 i = 0; i < 5; ++i)
            {
                if (spark = me->SummonCreature(NPC_SPARK_OF_IONAR, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 20000))
                {
                    summons.Summon(spark);
                    spark->CastSpell(spark, me->GetMap()->IsHeroic() ? SPELL_SPARK_VISUAL_TRIGGER_H : SPELL_SPARK_VISUAL_TRIGGER_N, true);
                    spark->CastSpell(spark, SPELL_RANDOM_LIGHTNING, true);
                    spark->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE);
                    spark->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0);

                    if (Player* tgt = SelectTargetFromPlayerList(100))
                        spark->GetMotionMaster()->MoveFollow(tgt, 0.0f, 0.0f, MOTION_SLOT_CONTROLLED);
                }
            }
            
            me->SetVisible(false);
            me->SetControlled(true, UNIT_STATE_STUNNED);

            events.SetPhase(2);
            events.ScheduleEvent(EVENT_CALL_SPARKS, 15000, 0, 2);
        }

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

            events.Update(diff);

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

            switch (events.GetEvent())
            {
                case EVENT_BALL_LIGHTNING:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
                        me->CastSpell(target, me->GetMap()->IsHeroic() ? SPELL_BALL_LIGHTNING_H : SPELL_BALL_LIGHTNING_N, false);
                    
                    events.RepeatEvent(10000 + rand()%1000);
                    break;
                case EVENT_STATIC_OVERLOAD:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
                        me->CastSpell(target, me->GetMap()->IsHeroic() ? SPELL_STATIC_OVERLOAD_H : SPELL_STATIC_OVERLOAD_N, false);

                    events.RepeatEvent(5000 + rand()%1000);
                    break;
                case EVENT_CHECK_HEALTH:
                    if (HealthBelowPct(HealthCheck))
                        me->CastSpell(me, SPELL_DISPERSE, false);

                    events.RepeatEvent(1000);
                    return;
                case EVENT_CALL_SPARKS:
                {
                    EntryCheckPredicate pred(NPC_SPARK_OF_IONAR);
                    summons.DoAction(ACTION_CALLBACK, pred);
                    events.PopEvent();
                    events.ScheduleEvent(EVENT_RESTORE, 2000, 0, 2);
                    return;
                }
                case EVENT_RESTORE:
                    EntryCheckPredicate pred(NPC_SPARK_OF_IONAR);
                    summons.DoAction(ACTION_SPARK_DESPAWN, pred);
                    events.PopEvent();

                    me->SetVisible(true);
                    me->SetControlled(false, UNIT_STATE_STUNNED);
                    ScheduleEvents(true);
                    return;
            }

            DoMeleeAttackIfReady();
        }
    };
};

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

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

    struct npc_spark_of_ionarAI : public ScriptedAI
    {
        npc_spark_of_ionarAI(Creature* creature) : ScriptedAI(creature) { }

        bool returning;

        void MoveInLineOfSight(Unit*) { }
        void UpdateAI(uint32) { }
        void AttackStart(Unit* who) { }

        void Reset() { returning = false; }

        void DamageTaken(Unit*, uint32 &damage, DamageEffectType, SpellSchoolMask)
        {
            damage = 0;
        }

        void DoAction(int32 param)
        {
            if (param == ACTION_CALLBACK)
            {
                me->SetSpeed(MOVE_RUN, 2.5f);
                me->DeleteThreatList();
                me->CombatStop(true);
                me->GetMotionMaster()->MoveTargetedHome();
                returning = true;
            }
            else if (param == ACTION_SPARK_DESPAWN)
            {
                me->GetMotionMaster()->MoveIdle();

                me->RemoveAllAuras();
                me->CastSpell(me, SPELL_SPARK_DESPAWN, true);
                me->DespawnOrUnsummon(1000);
            }
        }
    };
};

void AddSC_boss_ionar()
{
    new boss_ionar();
    new npc_spark_of_ionar();
}
