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

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

enum Spells
{
    // SJONNIR
    SPELL_FRENZY                            = 28747, //at 20% hp
    SPELL_CHAIN_LIGHTNING                   = 50830,
    SPELL_CHAIN_LIGHTNING_H                 = 59844,
    SPELL_LIGHTNING_SHIELD                  = 50831,
    SPELL_LIGHTNING_SHIELD_H                = 59845,
    SPELL_STATIC_CHARGE                     = 50834,
    SPELL_STATIC_CHARGE_H                   = 59846,
    SPELL_LIGHTNING_RING                    = 50840, 
    SPELL_LIGHTNING_RING_H                  = 59848,

    // IRON SLUDGE
    SPELL_TOXIC_VOLLEY                      = 50838,
    SPELL_TOXIC_VOLLEY_H                    = 59853,

    // FORGED IRON DWARF
    SPELL_LIGHTNING_TETHER                  = 50895,
    SPELL_LIGHTNING_TETHER_H                = 59851,

    // FORGED IRON TROGG
    SPELL_LIGHTNING_SHOCK                   = 50900,
    SPELL_LIGHTNING_SHOCK_H                 = 59852,
};

enum Npc
{
    NPC_IRON_SLUDGE                         = 28165, // if 2 ooze then spawn 1 iron_sludge
    NPC_DWARFES_FRIENDLY                    = 27980, //after fix the machine by Brann
    NPC_OOZE                                = 27981, //spawn after killing dwarf
    NPC_FORGED_IRON_DWARF                   = 27982,
    NPC_FORGED_IRON_TROGG                   = 27979,
};

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

enum Events
{
    // SJONNIR
    EVENT_SHIELD                            = 1,
    EVENT_CHAIN_LIGHTNING                   = 2,
    EVENT_STATIC_CHARGE                     = 3,
    EVENT_LIGHTNING_RING                    = 4,
    EVENT_CHECK_HEALTH                      = 5,
    EVENT_SUMMON                            = 6,
    EVENT_SUMMON_SPEACH                     = 7,

    // TRASH
    EVENT_MALFORMED_OOZE_CHECK              = 10,
    EVENT_TOXIC_VOLLEY                      = 11,
    EVENT_FORGED_LIGHTNING_SHOCK            = 12,
    EVENT_FORGED_LIGHTNING_TETHER           = 13,
};

enum Misc
{
    POS_GEN_RIGHT                           = 0,
    POS_GEN_LEFT                            = 1,
    POS_ROOM_CENTER                         = 2,

    // ACTIONS
    ACTION_SLUG_KILLED                      = 1,
};

enum SummonPhases
{
    PHASE_SUMMON_UNFRIENDLY_DWARFES         = 0,
    PHASE_SUMMON_OOZE                       = 1,
    PHASE_SUMMON_FRIENDLY_DWARFES           = 2,
};

static Position RoomPosition[] =
{
    {1293.0f, 610.0f, 199.3f},
    {1294.2f, 724.3f, 199.3f},
    {1295.2f, 667.1f, 189.7f},
};

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

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

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

        InstanceScript* pInstance;
        EventMap events;
        SummonList summons;

        uint8 SummonPhase;
        uint8 SlugeCount;

        void Reset() 
        {
            events.Reset();
            summons.DespawnAll();

            SlugeCount = 0;
            SummonPhase = PHASE_SUMMON_UNFRIENDLY_DWARFES;

            if (pInstance) 
            {
                pInstance->SetData(BOSS_SJONNIR, NOT_STARTED);
                pInstance->SetData(DATA_SJONNIR_ACHIEVEMENT, false);
                
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                if (pInstance->GetData(BOSS_TRIBUNAL_OF_AGES) == DONE)
                {
                    me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    if (GameObject *doors = me->GetMap()->GetGameObject(pInstance->GetData64(GO_SJONNIR_DOOR)))
                        doors->SetGoState(GO_STATE_ACTIVE);

                    if (GameObject *console = me->GetMap()->GetGameObject( pInstance->GetData64(GO_SJONNIR_CONSOLE)))
                        console->SetGoState(GO_STATE_READY);

                    if (Creature *brann = ObjectAccessor::GetCreature(*me, pInstance->GetData64(NPC_BRANN)))
                    {
                        brann->setDeathState(JUST_DIED);
                        brann->Respawn();
                        brann->AI()->DoAction(5);
                    }
                }
            }
        }

        void EnterCombat(Unit* who)
        {
            Talk(SAY_AGGRO);
            
            events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
            events.ScheduleEvent(EVENT_SHIELD, 14000 + rand()%5000);
            events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 6000 + rand()%6000);
            events.ScheduleEvent(EVENT_STATIC_CHARGE, 24000);
            events.ScheduleEvent(EVENT_LIGHTNING_RING, 25000 + rand()%6000);
            events.ScheduleEvent(EVENT_SUMMON, 20000);
            events.ScheduleEvent(EVENT_SUMMON, 21500);
            events.ScheduleEvent(EVENT_SUMMON_SPEACH, 20000);

            if (pInstance)
            {
                pInstance->SetData(BOSS_SJONNIR, IN_PROGRESS);

                if (GameObject *doors = me->GetMap()->GetGameObject(pInstance->GetData64(GO_SJONNIR_DOOR)))
                    doors->SetGoState(GO_STATE_READY);
                    
                if (pInstance->GetData(BOSS_TRIBUNAL_OF_AGES) == DONE)
                    if (Creature *brann = ObjectAccessor::GetCreature(*me, pInstance->GetData64(NPC_BRANN)))
                        brann->AI()->DoAction(3);
            }
        }

        void DoAction(int32 param)
        {
            if (param == ACTION_SLUG_KILLED)
            {
                SlugeCount++;
                if (SlugeCount >= 5 && pInstance)
                    pInstance->SetData(DATA_SJONNIR_ACHIEVEMENT, true);
            }
        }

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

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

            switch (events.GetEvent())
            {
                case EVENT_CHECK_HEALTH:
                {
                    if (SummonPhase == PHASE_SUMMON_UNFRIENDLY_DWARFES && HealthBelowPct(50)) 
                    {
                        SummonPhase = PHASE_SUMMON_OOZE;
                        events.CancelEvent(EVENT_SUMMON);
                        events.ScheduleEvent(EVENT_SUMMON, 0);
                        events.ScheduleEvent(EVENT_SUMMON, 1500);

                        if (pInstance)
                            if (Creature *brann = ObjectAccessor::GetCreature(*me, pInstance->GetData64(NPC_BRANN)))
                            {
                                brann->MonsterYell("What in the name o' Madoran did THAT do? Oh! Wait: I just about got it...", LANG_UNIVERSAL, 0);
                                brann->PlayDirectSound(14276);
                            }
                    }

                    if (HealthBelowPct(20))
                    {
                        if (Creature *brann = ObjectAccessor::GetCreature(*me, pInstance->GetData64(NPC_BRANN)))
                        {
                            brann->MonsterYell("Ha, that did it! Help's a-comin'! Take this, ya glowin' iron brute!", LANG_UNIVERSAL, 0);
                            brann->PlayDirectSound(14277);
                        }
                        SummonPhase = PHASE_SUMMON_FRIENDLY_DWARFES;
                        me->CastSpell(me, SPELL_FRENZY, false);

                        events.CancelEvent(EVENT_SUMMON);
                        events.ScheduleEvent(EVENT_SUMMON, 0);
                        events.PopEvent();
                        break;
                    }

                    events.RepeatEvent(1000);
                    break;
                }
                case EVENT_SHIELD:
                {
                    me->CastSpell(me, DUNGEON_MODE(SPELL_LIGHTNING_SHIELD, SPELL_LIGHTNING_SHIELD_H), false);
                    events.RepeatEvent(14000 + rand()%5000);
                    break;
                }
                case EVENT_CHAIN_LIGHTNING:
                {
                    if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true, 0))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_CHAIN_LIGHTNING, SPELL_CHAIN_LIGHTNING_H), false);

                    events.RepeatEvent(6000 + rand()%6000);
                    break;
                }
                case EVENT_STATIC_CHARGE:
                {
                    if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true, 0))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_STATIC_CHARGE, SPELL_STATIC_CHARGE_H), false);

                    events.RepeatEvent(20000);
                    break;
                }
                case EVENT_LIGHTNING_RING:
                {
                    me->CastSpell(me, DUNGEON_MODE(SPELL_LIGHTNING_RING, SPELL_LIGHTNING_RING_H), false);
                    events.RepeatEvent(25000 + rand()%6000);
                    events.DelayEvents(10000); // Channel duration
                    break;
                }
                case EVENT_SUMMON_SPEACH:
                {
                    if (Creature *brann = ObjectAccessor::GetCreature(*me, pInstance->GetData64(NPC_BRANN)))
                    {
                        brann->MonsterYell("This is a wee bit trickier that before... Oh, bloody--incomin'!", LANG_UNIVERSAL, 0);
                        brann->PlayDirectSound(14275);
                    }

                    events.PopEvent();
                    break;
                }
                case EVENT_SUMMON:
                {
                    switch (SummonPhase)
                    {
                        case PHASE_SUMMON_UNFRIENDLY_DWARFES:
                        {
                            SummonDwarfes(false);
                            events.RepeatEvent(20000);
                            break;
                        }
                        case PHASE_SUMMON_OOZE:
                        {
                            for (uint8 i = POS_GEN_RIGHT; i <= POS_GEN_LEFT; i++)
                            {
                                if (Creature* ooze = me->SummonCreature(NPC_OOZE, RoomPosition[i].GetPositionX(), RoomPosition[i].GetPositionY(), RoomPosition[i].GetPositionZ(), 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000))
                                {
                                    ActivatePipe(i);
                                    ooze->GetMotionMaster()->MovePoint(0, RoomPosition[POS_ROOM_CENTER].GetPositionX(), RoomPosition[POS_ROOM_CENTER].GetPositionY(), RoomPosition[POS_ROOM_CENTER].GetPositionZ());
                                    summons.Summon(ooze);
                                }
                            }
                            events.RepeatEvent(10000);
                            break;
                        }
                        case PHASE_SUMMON_FRIENDLY_DWARFES:
                        {
                            SummonDwarfes(true);
                            events.PopEvent();
                            break;
                        }
                    }
                    break;
                }
            }
            
            DoMeleeAttackIfReady();
        }

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

            summons.DespawnAll();
            if (pInstance)
            {
                pInstance->SetData(BOSS_SJONNIR, DONE);
                if (GameObject *sd = me->GetMap()->GetGameObject(pInstance->GetData64(GO_SJONNIR_DOOR)))
                    sd->SetGoState(GO_STATE_ACTIVE);

                if (Creature *brann = ObjectAccessor::GetCreature(*me, pInstance->GetData64(NPC_BRANN)))
                    brann->AI()->DoAction(4);
            }
        }

        void KilledUnit(Unit *victim)
        {
            if (urand(0,1))
                return;

            Talk(SAY_SLAY);
        }

        void ActivatePipe(uint8 side)
        {
            if (pInstance)
                if (GameObject *pipe = me->GetMap()->GetGameObject(pInstance->GetData64(side == POS_GEN_RIGHT ? GO_RIGHT_PIPE : GO_LEFT_PIPE)))
                    pipe->SendCustomAnim(0);
        }

        void SummonDwarfes(bool friendly)
        {
            if (friendly)
            {
                for (int i = 0; i < 3; i++)
                {
                    uint8 Pos = urand(POS_GEN_RIGHT, POS_GEN_LEFT);
                    if (Creature* dwarf = me->SummonCreature(NPC_DWARFES_FRIENDLY, RoomPosition[Pos].GetPositionX(), RoomPosition[Pos].GetPositionY() , RoomPosition[Pos].GetPositionZ(), 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000))
                    {
                        if (Player *plr = SelectTargetFromPlayerList(100.0f))
                            dwarf->setFaction(plr->getFaction());

                        ActivatePipe(Pos);
                        dwarf->AI()->AttackStart(me);
                        summons.Summon(dwarf);
                    }
                }
            }
            else
            {
                for (int i = 0; i < 2; i++)
                {
                    uint8 Pos = urand(POS_GEN_RIGHT, POS_GEN_LEFT);
                    if (Creature* dwarf = me->SummonCreature(urand(0,1) ? NPC_FORGED_IRON_TROGG : NPC_FORGED_IRON_DWARF, RoomPosition[Pos].GetPositionX(), RoomPosition[Pos].GetPositionY() , RoomPosition[Pos].GetPositionZ(), 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000))
                    {
                        ActivatePipe(Pos);
                        dwarf->SetInCombatWithZone();
                        summons.Summon(dwarf);
                    }
                }
            }
        }
    };
};

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

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

    struct boss_sjonnir_dwarfAI : public ScriptedAI
    {
        boss_sjonnir_dwarfAI(Creature *c) : ScriptedAI(c) { }

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

            DoSpellAttackIfReady((me->GetEntry() == NPC_FORGED_IRON_DWARF) ? DUNGEON_MODE(SPELL_LIGHTNING_TETHER, SPELL_LIGHTNING_TETHER_H) : DUNGEON_MODE(SPELL_LIGHTNING_SHOCK, SPELL_LIGHTNING_SHOCK_H));
        }
    };
};

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

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

    struct boss_sjonnir_iron_sludgeAI : public ScriptedAI
    {
        boss_sjonnir_iron_sludgeAI(Creature *c) : ScriptedAI(c) { }
        
        EventMap events;
        void Reset() 
        {
            events.Reset();
        }

        void EnterCombat(Unit *)
        {
            events.ScheduleEvent(EVENT_TOXIC_VOLLEY, 5000);
        }
        void JustDied(Unit* killer)
        {
            if (InstanceScript *pInstance = me->GetInstanceScript())
                if (Creature *sjonnir = ObjectAccessor::GetCreature(*me, pInstance->GetData64(NPC_SJONNIR)))
                    sjonnir->AI()->DoAction(ACTION_SLUG_KILLED);
        }
        void UpdateAI(uint32 diff)
        {
            if (!UpdateVictim())
                return;             

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

            switch (events.GetEvent())
            {
                // Every 5 seconds
                case EVENT_TOXIC_VOLLEY:
                {
                    me->CastSpell(me, DUNGEON_MODE(SPELL_TOXIC_VOLLEY, SPELL_TOXIC_VOLLEY_H), false);
                    events.RepeatEvent(5000);
                    break;
                }
            }

            DoMeleeAttackIfReady();
        }
    };
};

//OOZE
class boss_sjonnir_malformed_ooze : public CreatureScript
{
public:
    boss_sjonnir_malformed_ooze() : CreatureScript("boss_sjonnir_malformed_ooze") { }

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

    struct boss_sjonnir_malformed_oozeAI : public ScriptedAI
    {
        boss_sjonnir_malformed_oozeAI(Creature *c) : ScriptedAI(c) {    }

        EventMap events;
        void MovementInform(uint32 type, uint32 point)
        {
            if (type == POINT_MOTION_TYPE && point == 0)
                events.RescheduleEvent(EVENT_MALFORMED_OOZE_CHECK, 1000);
        }

        void EnterCombat(Unit *) { }
        void MoveInLineOfSight(Unit *) { }

        void UpdateAI(uint32 diff)
        {
            events.Update(diff);
            switch (events.GetEvent())
            {
                case EVENT_MALFORMED_OOZE_CHECK:
                {
                    std::list<Creature*> oozeList;
                    me->GetCreaturesWithEntryInRange(oozeList, 5.0f, NPC_OOZE);
                    for (std::list<Creature*>::const_iterator itr = oozeList.begin(); itr != oozeList.end(); ++itr)
                        if ((*itr)->GetGUID() != me->GetGUID() && (*itr)->IsAlive() && me->IsAlive())
                            if (Creature* is = me->SummonCreature(NPC_IRON_SLUDGE, me->GetPositionX(), me->GetPositionY() , me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000))
                            {
                                Unit::Kill(me, me);
                                Unit::Kill(*itr, *itr);
                                is->SetInCombatWithZone();
                                break;
                            }

                    events.RepeatEvent(1000);
                    break;
                }
            }
        }
    };
};

void AddSC_boss_sjonnir()
{
    new boss_sjonnir();
    new boss_sjonnir_dwarf();
    new boss_sjonnir_malformed_ooze();
    new boss_sjonnir_iron_sludge();
}