/*
 * Copyright (C) 2016+     AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 */

/* ScriptData
SDName: boss_Akilzon
SD%Complete: 75%
SDComment: Missing timer for Call Lightning and Sound ID's
SQLUpdate:
#Temporary fix for Soaring Eagles

EndScriptData */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Cell.h"
#include "CellImpl.h"
#include "zulaman.h"
#include "Weather.h"

enum Spells
{
    SPELL_STATIC_DISRUPTION     = 43622,
    SPELL_STATIC_VISUAL         = 45265,
    SPELL_CALL_LIGHTNING        = 43661, // Missing timer
    SPELL_GUST_OF_WIND          = 43621,
    SPELL_ELECTRICAL_STORM      = 43648,
    SPELL_BERSERK               = 45078,
    SPELL_ELECTRICAL_OVERLOAD   = 43658,
    SPELL_EAGLE_SWOOP           = 44732,
    SPELL_ZAP                   = 43137,
    SPELL_SAND_STORM            = 25160
};

enum Says
{
    SAY_AGGRO                   = 0,
    SAY_SUMMON                  = 1,
    SAY_INTRO                   = 2, // Not used in script
    SAY_ENRAGE                  = 3,
    SAY_KILL                    = 4,
    SAY_DEATH                   = 5
};

enum Misc
{
    NPC_SOARING_EAGLE           = 24858,
    SE_LOC_X_MAX                = 400,
    SE_LOC_X_MIN                = 335,
    SE_LOC_Y_MAX                = 1435,
    SE_LOC_Y_MIN                = 1370
};

enum Events
{
    EVENT_STATIC_DISRUPTION     = 1,
    EVENT_GUST_OF_WIND          = 2,
    EVENT_CALL_LIGHTNING        = 3,
    EVENT_ELECTRICAL_STORM      = 4,
    EVENT_RAIN                  = 5,
    EVENT_SUMMON_EAGLES         = 6,
    EVENT_STORM_SEQUENCE        = 7,
    EVENT_ENRAGE                = 8
};

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

        struct boss_akilzonAI : public BossAI
        {
            boss_akilzonAI(Creature* creature) : BossAI(creature, DATA_AKILZONEVENT)
            {
                memset(BirdGUIDs, 0, sizeof(BirdGUIDs));
            }

            void Reset()
            {
                _Reset();

                TargetGUID = 0;
                CloudGUID = 0;
                CycloneGUID = 0;
                memset(BirdGUIDs, 0, sizeof(BirdGUIDs));
                StormCount = 0;
                isRaining = false;

                SetWeather(WEATHER_STATE_FINE, 0.0f);
            }

            void EnterCombat(Unit* /*who*/)
            {
                events.ScheduleEvent(EVENT_STATIC_DISRUPTION, urand(10000, 20000)); // 10 to 20 seconds (bosskillers)
                events.ScheduleEvent(EVENT_GUST_OF_WIND, urand(20000, 30000));      // 20 to 30 seconds(bosskillers)
                events.ScheduleEvent(EVENT_CALL_LIGHTNING, urand(10000, 20000));    // totaly random timer. can't find any info on this
                events.ScheduleEvent(EVENT_ELECTRICAL_STORM, 60000);                // 60 seconds(bosskillers)
                events.ScheduleEvent(EVENT_RAIN, urand(47000, 52000));
                events.ScheduleEvent(EVENT_ENRAGE, 10*MINUTE*IN_MILLISECONDS);      // 10 minutes till enrage(bosskillers)

                Talk(SAY_AGGRO);
                //DoZoneInCombat();
                instance->SetData(DATA_AKILZONEVENT, IN_PROGRESS);
            }

            void JustDied(Unit* /*killer*/)
            {
                Talk(SAY_DEATH);
                _JustDied();
            }

            void KilledUnit(Unit* who)
            {
                if (who->GetTypeId() == TYPEID_PLAYER)
                    Talk(SAY_KILL);
            }

            void SetWeather(uint32 weather, float grade)
            {
                Map* map = me->GetMap();
                if (!map->IsDungeon())
                    return;

                WorldPacket data(SMSG_WEATHER, (4+4+4));
                data << uint32(weather) << float(grade) << uint8(0);

                map->SendToPlayers(&data);
            }

            void HandleStormSequence(Unit* Cloud) // 1: begin, 2-9: tick, 10: end
            {
                if (StormCount < 10 && StormCount > 1)
                {
                    // deal damage
                    int32 bp0 = 800;
                    for (uint8 i = 2; i < StormCount; ++i)
                        bp0 *= 2;

                    CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
                    Cell cell(p);
                    cell.SetNoCreate();

                    std::list<Unit*> tempUnitMap;

                    {
                        Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(me, me, SIZE_OF_GRIDS);
                        Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(me, tempUnitMap, u_check);

                        TypeContainerVisitor<Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
                        TypeContainerVisitor<Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck>, GridTypeMapContainer >  grid_unit_searcher(searcher);

                        cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, SIZE_OF_GRIDS);
                        cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, SIZE_OF_GRIDS);
                    }

                    // deal damage
                    for (std::list<Unit*>::const_iterator i = tempUnitMap.begin(); i != tempUnitMap.end(); ++i)
                    {
                        if (Unit* target = (*i))
                        {
                            if (Cloud && !Cloud->IsWithinDist(target, 6, false))
                                Cloud->CastCustomSpell(target, SPELL_ZAP, &bp0, NULL, NULL, true, 0, 0, me->GetGUID());
                        }
                    }

                    // visual
                    float x, y, z;
                    z = me->GetPositionZ();
                    for (uint8 i = 0; i < 5+rand()%5; ++i)
                    {
                        x = 343.0f+rand()%60;
                        y = 1380.0f+rand()%60;
                        if (Unit* trigger = me->SummonTrigger(x, y, z, 0, 2000))
                        {
                            trigger->setFaction(35);
                            trigger->SetMaxHealth(100000);
                            trigger->SetHealth(100000);
                            trigger->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                            if (Cloud)
                                Cloud->CastCustomSpell(trigger, /*43661*/SPELL_ZAP, &bp0, NULL, NULL, true, 0, 0, Cloud->GetGUID());
                        }
                    }
                }

                ++StormCount;

                if (StormCount > 10)
                {
                    StormCount = 0; // finish
                    events.ScheduleEvent(EVENT_SUMMON_EAGLES, 5000);
                    me->InterruptNonMeleeSpells(false);
                    CloudGUID = 0;
                    if (Cloud)
                        Unit::DealDamage(Cloud, Cloud, Cloud->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
                    SetWeather(WEATHER_STATE_FINE, 0.0f);
                    isRaining = false;
                }
                events.ScheduleEvent(EVENT_STORM_SEQUENCE, 1000);
            }

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

                events.Update(diff);

                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_STATIC_DISRUPTION:
                            {
                            Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1);
                            if (!target)
                                target = me->GetVictim();
                            if (target)
                            {
                                TargetGUID = target->GetGUID();
                                DoCast(target, SPELL_STATIC_DISRUPTION, false);
                                me->SetInFront(me->GetVictim());
                            }
                            /*if (float dist = me->IsWithinDist3d(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 5.0f) dist = 5.0f;
                            SDisruptAOEVisual_Timer = 1000 + floor(dist / 30 * 1000.0f);*/
                            events.ScheduleEvent(EVENT_STATIC_DISRUPTION, urand(10000, 18000));
                            break;
                            }
                        case EVENT_GUST_OF_WIND:
                            {
                                Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1);
                                if (!target)
                                    target = me->GetVictim();
                                if (target)
                                    DoCast(target, SPELL_GUST_OF_WIND);
                                events.ScheduleEvent(EVENT_GUST_OF_WIND, urand(20000, 30000));
                                break;
                            }
                        case EVENT_CALL_LIGHTNING:
                            DoCastVictim(SPELL_CALL_LIGHTNING);
                            events.ScheduleEvent(EVENT_CALL_LIGHTNING, urand(12000, 17000)); // totaly random timer. can't find any info on this
                            break;
                        case EVENT_ELECTRICAL_STORM:
                            {
                                Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50, true);
                                if (!target)
                                {
                                    EnterEvadeMode();
                                    return;
                                }
                                target->CastSpell(target, 44007, true); // cloud visual
                                DoCast(target, SPELL_ELECTRICAL_STORM, false); // storm cyclon + visual
                                float x, y, z;
                                target->GetPosition(x, y, z);
                                /// @todo: fix it in correct way, that causes player to can fly until logout
                                /*
                                if (target)
                                {
                                    target->SetDisableGravity(true);
                                    target->MonsterMoveWithSpeed(x, y, me->GetPositionZ()+15, 0);
                                }
                                */

                                Unit* Cloud = me->SummonTrigger(x, y, me->GetPositionZ()+16, 0, 15000);
                                if (Cloud)
                                    {
                                        CloudGUID = Cloud->GetGUID();
                                        Cloud->SetDisableGravity(true);
                                        Cloud->StopMoving();
                                        Cloud->SetObjectScale(1.0f);
                                        Cloud->setFaction(35);
                                        Cloud->SetMaxHealth(9999999);
                                        Cloud->SetHealth(9999999);
                                        Cloud->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                                    }
                                StormCount = 1;
                                events.ScheduleEvent(EVENT_ELECTRICAL_STORM, 60000); // 60 seconds(bosskillers)
                                events.ScheduleEvent(EVENT_RAIN, urand(47000, 52000));
                                break;
                            }
                        case EVENT_RAIN:
                            if (!isRaining)
                            {
                                SetWeather(WEATHER_STATE_HEAVY_RAIN, 0.9999f);
                                isRaining = true;
                            }
                            else
                                events.ScheduleEvent(EVENT_RAIN, 1000);
                            break;
                        case EVENT_STORM_SEQUENCE:
                            {
                                Unit* target = ObjectAccessor::GetUnit(*me, CloudGUID);
                                if (!target || !target->IsAlive())
                                {
                                    EnterEvadeMode();
                                    return;
                                }
                                else if (Unit* Cyclone = ObjectAccessor::GetUnit(*me, CycloneGUID))
                                    Cyclone->CastSpell(target, SPELL_SAND_STORM, true); // keep casting or...
                                HandleStormSequence(target);
                                break;
                            }
                        case EVENT_SUMMON_EAGLES:
                            Talk(SAY_SUMMON);

                            float x, y, z;
                            me->GetPosition(x, y, z);

                            for (uint8 i = 0; i < 8; ++i)
                            {
                                Unit* bird = ObjectAccessor::GetUnit(*me, BirdGUIDs[i]);
                                if (!bird) //they despawned on die
                                {
                                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                                    {
                                        x = target->GetPositionX() + irand(-10, 10);
                                        y = target->GetPositionY() + irand(-10, 10);
                                        z = target->GetPositionZ() + urand(16, 20);
                                        if (z > 95)
                                            z = 95.0f - urand(0, 5);
                                    }
                                    Creature* creature = me->SummonCreature(NPC_SOARING_EAGLE, x, y, z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
                                    if (creature)
                                    {
                                        creature->AddThreat(me->GetVictim(), 1.0f);
                                        creature->AI()->AttackStart(me->GetVictim());
                                        BirdGUIDs[i] = creature->GetGUID();
                                    }
                                }
                            }
                            break;
                        case EVENT_ENRAGE:
                             Talk(SAY_ENRAGE);
                             DoCast(me, SPELL_BERSERK, true);
                            events.ScheduleEvent(EVENT_ENRAGE, 600000);
                            break;
                        default:
                            break;
                    }
                }

                DoMeleeAttackIfReady();
            }

            private:
                uint64 BirdGUIDs[8];
                uint64 TargetGUID;
                uint64 CycloneGUID;
                uint64 CloudGUID;
                uint8  StormCount;
                bool   isRaining;
        };

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

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

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

            uint32 EagleSwoop_Timer;
            bool arrived;
            uint64 TargetGUID;

            void Reset()
            {
                EagleSwoop_Timer = urand(5000, 10000);
                arrived = true;
                TargetGUID = 0;
                me->SetDisableGravity(true);
            }

            void EnterCombat(Unit* /*who*/)
            {
                DoZoneInCombat();
            }

            void MoveInLineOfSight(Unit* /*who*/) { }


            void MovementInform(uint32, uint32)
            {
                arrived = true;
                if (TargetGUID)
                {
                    if (Unit* target = ObjectAccessor::GetUnit(*me, TargetGUID))
                        DoCast(target, SPELL_EAGLE_SWOOP, true);
                    TargetGUID = 0;
                    me->SetSpeed(MOVE_RUN, 1.2f);
                    EagleSwoop_Timer = urand(5000, 10000);
                }
            }

            void UpdateAI(uint32 diff)
            {
                if (EagleSwoop_Timer <= diff)
                    EagleSwoop_Timer = 0;
                else
                    EagleSwoop_Timer -= diff;

                if (arrived)
                {
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                    {
                        float x, y, z;
                        if (EagleSwoop_Timer)
                        {
                            x = target->GetPositionX() + irand(-10, 10);
                            y = target->GetPositionY() + irand(-10, 10);
                            z = target->GetPositionZ() + urand(10, 15);
                            if (z > 95)
                                z = 95.0f - urand(0, 5);
                        }
                        else
                        {
                            target->GetContactPoint(me, x, y, z);
                            z += 2;
                            me->SetSpeed(MOVE_RUN, 5.0f);
                            TargetGUID = target->GetGUID();
                        }
                        me->GetMotionMaster()->MovePoint(0, x, y, z);
                        arrived = false;
                    }
                }
            }
        };

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

void AddSC_boss_akilzon()
{
    new boss_akilzon();
    new npc_akilzon_eagle();
}

