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

/* ScriptData
TCName: Boss_Arlokk
TC%Complete: 95
TCComment: Wrong cleave and red aura is missing not yet added.
TCComment: Prowlers moving through wall hopefully mmaps will fix.
TCComment: Can't test LOS until mmaps.
TCCategory: Zul'Gurub
EndScriptData */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "zulgurub.h"

enum Says
{
    SAY_AGGRO                   = 0,
    SAY_FEAST_PROWLER           = 1,
    SAY_DEATH                   = 2
};

enum Spells
{
    SPELL_SHADOW_WORD_PAIN      = 24212, // Corrected
    SPELL_GOUGE                 = 12540, // Corrected
    SPELL_MARK_OF_ARLOKK        = 24210, // triggered spell 24211 Added to spell_dbc
    SPELL_RAVAGE                = 24213, // Corrected
    SPELL_CLEAVE                = 25174, // Searching for right spell
    SPELL_PANTHER_TRANSFORM     = 24190, // Transform to panther now used
    SPELL_SUMMON_PROWLER        = 24246, // Added to Spell_dbc
    SPELL_VANISH_VISUAL         = 24222, // Added
    SPELL_VANISH                = 24223, // Added
    SPELL_SUPER_INVIS           = 24235  // Added to Spell_dbc
};

enum Events
{
    EVENT_SHADOW_WORD_PAIN      = 1,
    EVENT_GOUGE                 = 2,
    EVENT_MARK_OF_ARLOKK        = 3,
    EVENT_RAVAGE                = 4,
    EVENT_TRANSFORM             = 5,
    EVENT_VANISH                = 6,
    EVENT_VANISH_2              = 7,
    EVENT_TRANSFORM_BACK        = 8,
    EVENT_VISIBLE               = 9,
    EVENT_SUMMON_PROWLERS       = 10
};

enum Phases
{
    PHASE_ALL                   = 0,
    PHASE_ONE                   = 1,
    PHASE_TWO                   = 2
};

enum Weapon
{
    WEAPON_DAGGER               = 10616
};

enum Misc
{
    MAX_PROWLERS_PER_SIDE       = 15
};

Position const PosMoveOnSpawn[1] =
{
    { -11561.9f, -1627.868f, 41.29941f, 0.0f }
};

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

        struct boss_arlokkAI : public BossAI
        {
            boss_arlokkAI(Creature* creature) : BossAI(creature, DATA_ARLOKK) { }

            void Reset()
            {
                if (events.IsInPhase(PHASE_TWO))
                    me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
                _Reset();
                _summonCountA = 0;
                _summonCountB = 0;
                me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER));
                me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER));
                me->SetWalk(false);
                me->GetMotionMaster()->MovePoint(0, PosMoveOnSpawn[0]);
            }

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

            void EnterCombat(Unit* /*who*/)
            {
                _EnterCombat();
                events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(7000, 9000), 0, PHASE_ONE);
                events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE);
                events.ScheduleEvent(EVENT_SUMMON_PROWLERS, 6000, 0, PHASE_ALL);
                events.ScheduleEvent(EVENT_MARK_OF_ARLOKK, urand(9000, 11000), 0, PHASE_ALL);
                events.ScheduleEvent(EVENT_TRANSFORM, urand(15000, 20000), 0, PHASE_ONE);
                Talk(SAY_AGGRO);

                // Sets up list of Panther spawners to cast on
                std::list<Creature*> triggerList;
                GetCreatureListWithEntryInGrid(triggerList, me, NPC_PANTHER_TRIGGER, 100.0f);
                if (!triggerList.empty())
                {
                    uint8 sideA = 0;
                    uint8 sideB = 0;
                    for (std::list<Creature*>::const_iterator itr = triggerList.begin(); itr != triggerList.end(); ++itr)
                    {
                        if (Creature* trigger = *itr)
                        {
                            if (trigger->GetPositionY() < -1625.0f)
                            {
                                _triggersSideAGUID[sideA] = trigger->GetGUID();
                                ++sideA;
                            }
                             else
                            {
                                _triggersSideBGUID[sideB] = trigger->GetGUID();
                                ++sideB;
                            }
                        }
                    }
                }
            }

            void EnterEvadeMode()
            {
                BossAI::EnterEvadeMode();
                if (GameObject* object = ObjectAccessor::GetGameObject(*me, instance->GetData64(GO_GONG_OF_BETHEKK)))
                    object->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
                me->DespawnOrUnsummon(4000);
            }

            void SetData(uint32 id, uint32 /*value*/)
            {
                if (id == 1)
                    --_summonCountA;
                else if (id == 2)
                    --_summonCountB;
            }

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

                events.Update(diff);

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

                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_SHADOW_WORD_PAIN:
                            DoCastVictim(SPELL_SHADOW_WORD_PAIN, true);
                            events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(5000, 7000), 0, PHASE_ONE);
                            break;
                        case EVENT_GOUGE:
                            DoCastVictim(SPELL_GOUGE, true);
                            break;
                        case EVENT_SUMMON_PROWLERS:
                            if (_summonCountA < MAX_PROWLERS_PER_SIDE)
                            {
                                if (Unit* trigger = ObjectAccessor::GetUnit(*me, _triggersSideAGUID[urand(0, 4)]))
                                {
                                    trigger->CastSpell(trigger, SPELL_SUMMON_PROWLER);
                                    ++_summonCountA;
                                }
                            }
                            if (_summonCountB < MAX_PROWLERS_PER_SIDE)
                            {
                                if (Unit* trigger = ObjectAccessor::GetUnit(*me, _triggersSideBGUID[urand(0, 4)]))
                                {
                                    trigger->CastSpell(trigger, SPELL_SUMMON_PROWLER);
                                    ++_summonCountB;
                                }
                            }
                            events.ScheduleEvent(EVENT_SUMMON_PROWLERS, 6000, 0, PHASE_ALL);
                            break;
                        case EVENT_MARK_OF_ARLOKK:
                        {
                            Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, urand(1, 3), 0.0f, false, -SPELL_MARK_OF_ARLOKK);
                            if (!target)
                                target = me->GetVictim();
                            if (target)
                            {
                                DoCast(target, SPELL_MARK_OF_ARLOKK, true);
                                Talk(SAY_FEAST_PROWLER, target);
                            }
                            events.ScheduleEvent(EVENT_MARK_OF_ARLOKK, urand(120000, 130000));
                            break;
                        }
                        case EVENT_TRANSFORM:
                        {
                            DoCast(me, SPELL_PANTHER_TRANSFORM); // SPELL_AURA_TRANSFORM
                            me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(EQUIP_UNEQUIP));
                            me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(EQUIP_UNEQUIP));
                            /*
                            const CreatureTemplate* cinfo = me->GetCreatureTemplate();
                            me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35)));
                            me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35)));
                            me->UpdateDamagePhysical(BASE_ATTACK);
                            */
                            me->AttackStop();
                            DoResetThreat();
                            me->SetReactState(REACT_PASSIVE);
                            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
                            DoCast(me, SPELL_VANISH_VISUAL);
                            DoCast(me, SPELL_VANISH);
                            events.ScheduleEvent(EVENT_VANISH, 1000, 0, PHASE_ONE);
                            break;
                        }
                        case EVENT_VANISH:
                            DoCast(me, SPELL_SUPER_INVIS);
                            me->SetWalk(false);
                            me->GetMotionMaster()->MovePoint(0, frand(-11551.0f, -11508.0f), frand(-1638.0f, -1617.0f), me->GetPositionZ());
                            events.ScheduleEvent(EVENT_VANISH_2, 9000, 0, PHASE_ONE);
                            break;
                        case EVENT_VANISH_2:
                            DoCast(me, SPELL_VANISH);
                            DoCast(me, SPELL_SUPER_INVIS);
                            events.ScheduleEvent(EVENT_VISIBLE, urand(7000, 10000), 0, PHASE_ONE);
                            break;
                        case EVENT_VISIBLE:
                            me->SetReactState(REACT_AGGRESSIVE);
                            me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
                            if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                                AttackStart(target);
                            me->RemoveAura(SPELL_SUPER_INVIS);
                            me->RemoveAura(SPELL_VANISH);
                            events.ScheduleEvent(EVENT_RAVAGE, urand(10000, 14000), 0, PHASE_TWO);
                            events.ScheduleEvent(EVENT_TRANSFORM_BACK, urand(15000, 18000), 0, PHASE_TWO);
                            events.SetPhase(PHASE_TWO);
                            me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); // hack
                            break;
                        case EVENT_RAVAGE:
                            DoCastVictim(SPELL_RAVAGE, true);
                            events.ScheduleEvent(EVENT_RAVAGE, urand(10000, 14000), 0, PHASE_TWO);
                            break;
                        case EVENT_TRANSFORM_BACK:
                        {
                            me->RemoveAura(SPELL_PANTHER_TRANSFORM); // SPELL_AURA_TRANSFORM
                            DoCast(me, SPELL_VANISH_VISUAL);
                            me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER));
                            me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER));
                            /*
                            const CreatureTemplate* cinfo = me->GetCreatureTemplate();
                            me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg));
                            me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg));
                            me->UpdateDamagePhysical(BASE_ATTACK);
                            */
                            me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
                            events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(4000, 7000), 0, PHASE_ONE);
                            events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE);
                            events.ScheduleEvent(EVENT_TRANSFORM, urand(16000, 20000), 0, PHASE_ONE);
                            events.SetPhase(PHASE_ONE);
                            break;
                        }
                        default:
                            break;
                    }
                }

                DoMeleeAttackIfReady();
            }

        private:
            uint8 _summonCountA;
            uint8 _summonCountB;
            uint64 _triggersSideAGUID[5];
            uint64 _triggersSideBGUID[5];
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return GetZulGurubAI<boss_arlokkAI>(creature);
        }
};

/*######
## npc_zulian_prowler
######*/

enum ZulianProwlerSpells
{
    SPELL_SNEAK_RANK_1_1         = 22766,
    SPELL_SNEAK_RANK_1_2         = 7939,  // Added to Spell_dbc
    SPELL_MARK_OF_ARLOKK_TRIGGER = 24211  // Added to Spell_dbc
};

enum ZulianProwlerEvents
{
    EVENT_ATTACK                 = 1
};

/*Position const PosProwlerCenter[1] =
{
    { -11556.7f, -1631.344f, 41.2994f, 0.0f }
};*/

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

        struct npc_zulian_prowlerAI : public ScriptedAI
        {
            npc_zulian_prowlerAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }

            void Reset()
            {
                if (me->GetPositionY() < -1625.0f)
                    _sideData = 1;
                else
                    _sideData = 2;

                DoCast(me, SPELL_SNEAK_RANK_1_1);
                DoCast(me, SPELL_SNEAK_RANK_1_2);

                if (Unit* arlokk = ObjectAccessor::GetUnit(*me, _instance->GetData64(NPC_ARLOKK)))
                    me->GetMotionMaster()->MovePoint(0, arlokk->GetPositionX(), arlokk->GetPositionY(), arlokk->GetPositionZ());
                _events.ScheduleEvent(EVENT_ATTACK, 6000);
            }

            void EnterCombat(Unit* /*who*/)
            {
                me->GetMotionMaster()->Clear(false);
                me->RemoveAura(SPELL_SNEAK_RANK_1_1);
                me->RemoveAura(SPELL_SNEAK_RANK_1_2);
            }

            void SpellHit(Unit* caster, SpellInfo const* spell)
            {
                if (spell->Id == SPELL_MARK_OF_ARLOKK_TRIGGER) // Should only hit if line of sight
                    me->Attack(caster, true);
            }

            void JustDied(Unit* /*killer*/)
            {
                if (Unit* arlokk = ObjectAccessor::GetUnit(*me, _instance->GetData64(NPC_ARLOKK)))
                {
                    if (arlokk->IsAlive())
                        arlokk->GetAI()->SetData(_sideData, 0);
                }
                me->DespawnOrUnsummon(4000);
            }

            void UpdateAI(uint32 diff)
            {
                if (UpdateVictim())
                {
                    DoMeleeAttackIfReady();
                    return;
                }

                _events.Update(diff);

                while (uint32 eventId = _events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_ATTACK:
                            if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0.0f, 100, false))
                                me->Attack(target, true);
                            break;
                        default:
                            break;
                    }
                }
            }

        private:
            int32 _sideData;
            EventMap _events;
            InstanceScript* _instance;
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return GetZulGurubAI<npc_zulian_prowlerAI>(creature);
        }
};

/*######
## go_gong_of_bethekk
######*/

Position const PosSummonArlokk[1] =
{
    { -11507.22f, -1628.062f, 41.38264f, 3.159046f }
};

class go_gong_of_bethekk : public GameObjectScript
{
    public: go_gong_of_bethekk() : GameObjectScript("go_gong_of_bethekk") { }

        bool OnGossipHello(Player* /*player*/, GameObject* go) override
        {
            if (go->GetInstanceScript())
            {
                go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
                go->SendCustomAnim(0);
                go->SummonCreature(NPC_ARLOKK, PosSummonArlokk[0], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 600000);
            }
            
            return true;
        }
};

void AddSC_boss_arlokk()
{
    new boss_arlokk();
    new npc_zulian_prowler();
    new go_gong_of_bethekk();
}
