/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using Framework.Constants;
using Game.AI;
using Game.Entities;
using Game.Maps;
using Game.Scripting;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Scripts.Northrend.AzjolNerub.AzjolNerub.KrikthirTheGatewatcher
{
    struct SpellIds
    {
        // Krik'Thir The Gatewatcher
        public const uint SubbossAggroTrigger = 52343;
        public const uint Swarm = 52440;
        public const uint MindFlay = 52586;
        public const uint CurseOfFatigue = 52592;
        public const uint Frenzy = 28747;

        // Watchers - Shared
        public const uint WebWrap = 52086;
        public const uint WebWrapWrapped = 52087;
        public const uint InfectedBite = 52469;

        // Watcher Gashra
        public const uint Enrage = 52470;
        // Watcher Narjil
        public const uint BlindingWebs = 52524;
        // Watcher Silthik
        public const uint PoisonSpray = 52493;

        // Anub'Ar Warrior
        public const uint Cleave = 49806;
        public const uint Strike = 52532;

        // Anub'Ar Skirmisher
        public const uint Charge = 52538;
        public const uint Backstab = 52540;
        public const uint FixtateTrigger = 52536;
        public const uint FixtateTriggered = 52537;

        // Anub'Ar Shadowcaster
        public const uint ShadowBolt = 52534;
        public const uint ShadowNova = 52535;

        // Skittering Infector
        public const uint AcidSplash = 52446;
    }

    struct Misc
    {
        public const uint DataPetGroup = 0;

        // Krik'thir the Gatewatcher
        public const uint EventSendGroup = 1;
        public const uint EventSwarm = 2;
        public const uint EventMindFlay = 3;
        public const uint EventFrenzy = 4;
    }

    struct ActionIds
    {
        public const int GashraDied = 0;
        public const int NarjilDied = 1;
        public const int SilthikDied = 2;
        public const int WatcherEngaged = 3;
        public const int PetEngaged = 4;
        public const int PetEvade = 5;
    }

    struct TextIds
    {
        public const uint SayAggro = 0;
        public const uint SaySlay = 1;
        public const uint SayDeath = 2;
        public const uint SaySwarm = 3;
        public const uint SayPrefight = 4;
        public const uint SaySendGroup = 5;
    }

    [Script]
    class boss_krik_thir : CreatureScript
    {
        public boss_krik_thir() : base("boss_krik_thir") { }

        class boss_krik_thirAI : BossAI
        {
            public boss_krik_thirAI(Creature creature) : base(creature, ANDataTypes.KrikthirTheGatewatcher) { }

            void SummonAdds()
            {
                if (instance.GetBossState(ANDataTypes.KrikthirTheGatewatcher) == EncounterState.Done)
                    return;

                for (byte i = 1; i <= 3; ++i)
                {
                    List<TempSummon> summons;
                    me.SummonCreatureGroup(i, out summons);

                    foreach (TempSummon summon in summons)
                        summon.GetAI().SetData(Misc.DataPetGroup, i);
                }
            }

            public override void Reset()
            {
                base.Reset();
                _hadFrenzy = false;
                _petsInCombat = false;
                _watchersActive = 0;
                me.SetReactState(ReactStates.Passive);
            }

            public override void InitializeAI()
            {
                base.InitializeAI();
                SummonAdds();
            }

            public override void JustRespawned()
            {
                base.JustRespawned();
                SummonAdds();
            }

            public override void KilledUnit(Unit victim)
            {
                if (victim.IsTypeId(TypeId.Player))
                    Talk(TextIds.SaySlay);
            }

            public override void JustDied(Unit killer)
            {
                summons.Clear();
                base.JustDied(killer);
                Talk(TextIds.SayDeath);
            }

            public override void EnterCombat(Unit who)
            {
                _petsInCombat = false;
                me.SetReactState(ReactStates.Aggressive);
                summons.DoZoneInCombat();

                _events.CancelEvent(Misc.EventSendGroup);
                _events.ScheduleEvent(Misc.EventSwarm, TimeSpan.FromSeconds(5));
                _events.ScheduleEvent(Misc.EventMindFlay, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3));

                base.EnterCombat(who);
            }

            public override void MoveInLineOfSight(Unit who)
            {
                if (!me.HasReactState(ReactStates.Passive))
                {
                    base.MoveInLineOfSight(who);
                    return;
                }

                if (me.CanStartAttack(who, false) && me.IsWithinDistInMap(who, me.GetAttackDistance(who) + me.m_CombatDistance))
                    EnterCombat(who);
            }

            public override void EnterEvadeMode(EvadeReason why)
            {
                summons.DespawnAll();
                _DespawnAtEvade();
            }

            public override void DoAction(int action)
            {
                switch (action)
                {
                    case -ANInstanceMisc.ActionGatewatcherGreet:
                        if (!_hadGreet && me.IsAlive() && !me.IsInCombat() && !_petsInCombat)
                        {
                            _hadGreet = true;
                            Talk(TextIds.SayPrefight);
                        }
                        break;
                    case ActionIds.GashraDied:
                    case ActionIds.NarjilDied:
                    case ActionIds.SilthikDied:
                        if (_watchersActive == 0) // something is wrong
                        {
                            EnterEvadeMode(EvadeReason.Other);
                            return;
                        }
                        if ((--_watchersActive) == 0) // if there are no watchers currently in combat...
                            _events.RescheduleEvent(Misc.EventSendGroup, TimeSpan.FromSeconds(5)); // ...send the next watcher after the targets sooner
                        break;
                    case ActionIds.WatcherEngaged:
                        ++_watchersActive;
                        break;
                    case ActionIds.PetEngaged:
                        if (_petsInCombat || me.IsInCombat())
                            break;
                        _petsInCombat = true;
                        Talk(TextIds.SayAggro);
                        _events.ScheduleEvent(Misc.EventSendGroup, TimeSpan.FromSeconds(70));
                        break;
                    case ActionIds.PetEvade:
                        EnterEvadeMode(EvadeReason.Other);
                        break;
                }
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim() && !_petsInCombat)
                    return;

                _events.Update(diff);

                if (me.HasUnitState(UnitState.Casting))
                    return;

                if (me.HealthBelowPct(10) && !_hadFrenzy)
                {
                    _hadFrenzy = true;
                    _events.ScheduleEvent(Misc.EventFrenzy, TimeSpan.FromSeconds(1));
                }

                _events.ExecuteEvents(eventId =>
                {
                    switch (eventId)
                    {
                        case Misc.EventSendGroup:
                            DoCastAOE(SpellIds.SubbossAggroTrigger, true);
                            _events.Repeat(TimeSpan.FromSeconds(70));
                            break;
                        case Misc.EventSwarm:
                            DoCastAOE(SpellIds.Swarm);
                            Talk(TextIds.SaySwarm);
                            break;
                        case Misc.EventMindFlay:
                            DoCastVictim(SpellIds.MindFlay);
                            _events.Repeat(TimeSpan.FromSeconds(9), TimeSpan.FromSeconds(11));
                            break;
                        case Misc.EventFrenzy:
                            DoCastSelf(SpellIds.Frenzy);
                            DoCastAOE(SpellIds.CurseOfFatigue);
                            _events.Repeat(TimeSpan.FromSeconds(15));
                            break;
                    }

                    if (me.HasUnitState(UnitState.Casting))
                        return;
                });

                DoMeleeAttackIfReady();
            }

            public override void SpellHit(Unit whose, SpellInfo spell)
            {
                if (spell.Id == SpellIds.SubbossAggroTrigger)
                    DoZoneInCombat();
            }

            public override void SpellHitTarget(Unit who, SpellInfo spell)
            {
                if (spell.Id == SpellIds.SubbossAggroTrigger)
                    Talk(TextIds.SaySendGroup);
            }

            bool _hadGreet;
            bool _hadFrenzy;
            bool _petsInCombat;
            byte _watchersActive;
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<boss_krik_thirAI>(creature);
        }
    }

    class npc_gatewatcher_petAI : ScriptedAI
    {
        public npc_gatewatcher_petAI(Creature creature, bool isWatcher) : base(creature)
        {
            _instance = creature.GetInstanceScript();
            _isWatcher = isWatcher;
        }

        public virtual void _EnterCombat() { }

        public override void EnterCombat(Unit who)
        {
            if (_isWatcher)
            {
                _isWatcher = false;

                TempSummon meSummon = me.ToTempSummon();
                if (meSummon)
                {
                    Creature summoner = meSummon.GetSummonerCreatureBase();
                    if (summoner)
                        summoner.GetAI().DoAction(ActionIds.WatcherEngaged);
                }
            }

            if (me.HasReactState(ReactStates.Passive))
            {
                List<Creature> others = new List<Creature>();
                me.GetCreatureListWithEntryInGrid(others, 0, 40.0f);
                foreach (Creature other in others)
                {
                    if (other.GetAI().GetData(Misc.DataPetGroup) == _petGroup)
                    {
                        other.SetReactState(ReactStates.Aggressive);
                        other.GetAI().AttackStart(who);
                    }
                }

                TempSummon meSummon = me.ToTempSummon();
                if (meSummon)
                {
                    Creature summoner = meSummon.GetSummonerCreatureBase();
                    if (summoner)
                        summoner.GetAI().DoAction(ActionIds.PetEngaged);
                }
            }
            _EnterCombat();
            base.EnterCombat(who);
        }

        public override void SetData(uint data, uint value)
        {
            if (data == Misc.DataPetGroup)
            {
                _petGroup = value;
                me.SetReactState(_petGroup != 0 ? ReactStates.Passive : ReactStates.Aggressive);
            }
        }

        public override uint GetData(uint data)
        {
            if (data == Misc.DataPetGroup)
                return _petGroup;
            return 0;
        }

        public override void MoveInLineOfSight(Unit who)
        {
            if (!me.HasReactState(ReactStates.Passive))
            {
                base.MoveInLineOfSight(who);
                return;
            }

            if (me.CanStartAttack(who, false) && me.IsWithinDistInMap(who, me.GetAttackDistance(who) + me.m_CombatDistance))
                EnterCombat(who);
        }

        public override void SpellHit(Unit whose, SpellInfo spell)
        {
            if (spell.Id == SpellIds.SubbossAggroTrigger)
                DoZoneInCombat();
        }

        public override void EnterEvadeMode(EvadeReason why)
        {
            TempSummon meSummon = me.ToTempSummon();
            if (meSummon)
            {
                Creature summoner = meSummon.GetSummonerCreatureBase();
                if (summoner)
                    summoner.GetAI().DoAction(ActionIds.PetEvade);
                else
                    me.DespawnOrUnsummon();
                return;
            }
            base.EnterEvadeMode(why);
        }

        protected InstanceScript _instance;
        uint _petGroup;
        bool _isWatcher;
    }

    [Script]
    class npc_watcher_gashra : CreatureScript
    {
        public npc_watcher_gashra() : base("npc_watcher_gashra") { }

        class npc_watcher_gashraAI : npc_gatewatcher_petAI
        {
            public npc_watcher_gashraAI(Creature creature) : base(creature, true)
            {
                _instance = creature.GetInstanceScript();
                me.SetReactState(ReactStates.Passive);
            }

            public override void Reset()
            {
                _scheduler.CancelAll();
            }

            public override void _EnterCombat()
            {
                _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting));

                _scheduler.Schedule(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), task =>
                {
                    DoCastSelf(SpellIds.Enrage);
                    task.Repeat(TimeSpan.FromSeconds(12), TimeSpan.FromSeconds(20));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(16), TimeSpan.FromSeconds(19), task =>
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100.0f);
                    if (target)
                        DoCast(target, SpellIds.WebWrap);
                    task.Repeat(TimeSpan.FromSeconds(13), TimeSpan.FromSeconds(19));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(11), task =>
                {
                    DoCastVictim(SpellIds.InfectedBite);
                    task.Repeat(TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(27));
                });
            }

            public override void JustDied(Unit killer)
            {
                Creature krikthir = _instance.GetCreature(ANDataTypes.KrikthirTheGatewatcher);
                if (krikthir && krikthir.IsAlive())
                    krikthir.GetAI().DoAction(ActionIds.GashraDied);
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim())
                    return;

                _scheduler.Update(diff);

                if (me.HasUnitState(UnitState.Casting))
                    return;

                DoMeleeAttackIfReady();
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_watcher_gashraAI>(creature);
        }
    }

    [Script]
    class npc_watcher_narjil : CreatureScript
    {
        public npc_watcher_narjil() : base("npc_watcher_narjil") { }

        class npc_watcher_narjilAI : npc_gatewatcher_petAI
        {
            public npc_watcher_narjilAI(Creature creature) : base(creature, true)
            {
                _instance = creature.GetInstanceScript();
            }

            public override void Reset()
            {
                _scheduler.CancelAll();
            }

            public override void _EnterCombat()
            {
                _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting));

                _scheduler.Schedule(TimeSpan.FromSeconds(13), TimeSpan.FromSeconds(18), task =>
                {
                    DoCastVictim(SpellIds.BlindingWebs);
                    task.Repeat(TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(27));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), task =>
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100, true);
                    if (target)
                        DoCast(target, SpellIds.WebWrap);
                    task.Repeat(TimeSpan.FromSeconds(13), TimeSpan.FromSeconds(19));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(11), task =>
                {
                    DoCastVictim(SpellIds.InfectedBite);
                    task.Repeat(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(25));
                });
            }

            public override void JustDied(Unit killer)
            {
                Creature krikthir = _instance.GetCreature(ANDataTypes.KrikthirTheGatewatcher);
                if (krikthir && krikthir.IsAlive())
                    krikthir.GetAI().DoAction(ActionIds.NarjilDied);
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim())
                    return;

                _scheduler.Update(diff);

                if (me.HasUnitState(UnitState.Casting))
                    return;

                DoMeleeAttackIfReady();
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_watcher_narjilAI>(creature);
        }
    }

    [Script]
    class npc_watcher_silthik : CreatureScript
    {
        public npc_watcher_silthik() : base("npc_watcher_silthik") { }

        class npc_watcher_silthikAI : npc_gatewatcher_petAI
        {
            public npc_watcher_silthikAI(Creature creature) : base(creature, true)
            {
                _instance = creature.GetInstanceScript();
            }

            public override void Reset()
            {
                _scheduler.CancelAll();
            }

            public override void _EnterCombat()
            {
                _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting));

                _scheduler.Schedule(TimeSpan.FromSeconds(16), TimeSpan.FromSeconds(19), task =>
                 {
                     DoCastVictim(SpellIds.PoisonSpray);
                     task.Repeat(TimeSpan.FromSeconds(13), TimeSpan.FromSeconds(19));
                 });

                _scheduler.Schedule(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(11), task =>
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100, true);
                    if (target)
                        DoCast(target, SpellIds.WebWrap);
                    task.Repeat(TimeSpan.FromSeconds(13), TimeSpan.FromSeconds(17));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), task =>
                {
                    DoCastVictim(SpellIds.InfectedBite);
                    task.Repeat(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(24));
                });
            }

            public override void JustDied(Unit killer)
            {
                Creature krikthir = _instance.GetCreature(ANDataTypes.KrikthirTheGatewatcher);
                if (krikthir && krikthir.IsAlive())
                    krikthir.GetAI().DoAction(ActionIds.SilthikDied);
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim())
                    return;

                _scheduler.Update(diff);

                if (me.HasUnitState(UnitState.Casting))
                    return;

                DoMeleeAttackIfReady();
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_watcher_silthikAI>(creature);
        }
    }

    [Script]
    class npc_anub_ar_warrior : CreatureScript
    {
        public npc_anub_ar_warrior() : base("npc_anub_ar_warrior") { }

        class npc_anub_ar_warriorAI : npc_gatewatcher_petAI
        {
            public npc_anub_ar_warriorAI(Creature creature) : base(creature, false) { }

            public override void Reset()
            {
                _scheduler.CancelAll();
            }

            public override void _EnterCombat()
            {
                _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting));

                _scheduler.Schedule(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(9), task =>
                {
                    DoCastVictim(SpellIds.Cleave);
                    task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(16));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), task =>
                {
                    DoCastVictim(SpellIds.Strike);
                    task.Repeat(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(19));
                });
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim())
                    return;

                _scheduler.Update(diff);

                if (me.HasUnitState(UnitState.Casting))
                    return;

                DoMeleeAttackIfReady();
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_anub_ar_warriorAI>(creature);
        }
    }

    [Script]
    class npc_anub_ar_skirmisher : CreatureScript
    {
        public npc_anub_ar_skirmisher() : base("npc_anub_ar_skirmisher") { }

        class npc_anub_ar_skirmisherAI : npc_gatewatcher_petAI
        {
            public npc_anub_ar_skirmisherAI(Creature creature) : base(creature, false) { }

            public override void Reset()
            {
                _scheduler.CancelAll();
            }

            public override void _EnterCombat()
            {
                _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting));

                _scheduler.Schedule(TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(8), task =>
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100.0f, true);
                    if (target)
                        DoCast(target, SpellIds.Charge);
                    task.Repeat(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(25));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(9), task =>
                {
                    if (me.GetVictim() && me.GetVictim().isInBack(me))
                        DoCastVictim(SpellIds.Backstab);
                    task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(13));
                });
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim())
                    return;

                _scheduler.Update(diff);

                if (me.HasUnitState(UnitState.Casting))
                    return;

                DoMeleeAttackIfReady();
            }

            public override void SpellHitTarget(Unit target, SpellInfo spell)
            {
                if (spell.Id == SpellIds.Charge && target)
                    DoCast(target, SpellIds.FixtateTrigger);
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_anub_ar_skirmisherAI>(creature);
        }
    }

    [Script]
    class npc_anub_ar_shadowcaster : CreatureScript
    {
        public npc_anub_ar_shadowcaster() : base("npc_anub_ar_shadowcaster") { }

        class npc_anub_ar_shadowcasterAI : npc_gatewatcher_petAI
        {
            public npc_anub_ar_shadowcasterAI(Creature creature) : base(creature, false) { }

            public override void Reset()
            {
                _scheduler.CancelAll();
            }

            public override void _EnterCombat()
            {
                _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting));

                _scheduler.Schedule(TimeSpan.FromSeconds(4), task =>
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100.0f, true);
                    if (target)
                        DoCast(target, SpellIds.ShadowBolt);
                    task.Repeat(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4));
                });

                _scheduler.Schedule(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(14), task =>
                {
                    DoCastVictim(SpellIds.ShadowNova);
                    task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(16));
                });
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim())
                    return;

                _scheduler.Update(diff);

                if (me.HasUnitState(UnitState.Casting))
                    return;

                DoMeleeAttackIfReady();
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_anub_ar_shadowcasterAI>(creature);
        }
    }

    [Script]
    class npc_skittering_swarmer : CreatureScript
    {
        public npc_skittering_swarmer() : base("npc_skittering_swarmer") { }

        class npc_skittering_swarmerAI : ScriptedAI
        {
            public npc_skittering_swarmerAI(Creature creature) : base(creature) { }

            public override void InitializeAI()
            {
                base.InitializeAI();
                Creature gatewatcher = me.GetInstanceScript().GetCreature(ANDataTypes.KrikthirTheGatewatcher);
                if (gatewatcher)
                {
                    Unit target = gatewatcher.getAttackerForHelper();
                    if (target)
                        AttackStart(target);
                    gatewatcher.GetAI().JustSummoned(me);
                }
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_skittering_swarmerAI>(creature);
        }
    }

    [Script]
    class npc_skittering_infector : CreatureScript
    {
        public npc_skittering_infector() : base("npc_skittering_infector") { }

        class npc_skittering_infectorAI : ScriptedAI
        {
            public npc_skittering_infectorAI(Creature creature) : base(creature) { }

            public override void InitializeAI()
            {
                base.InitializeAI();
                Creature gatewatcher = me.GetInstanceScript().GetCreature(ANDataTypes.KrikthirTheGatewatcher);
                if (gatewatcher)
                {
                    Unit target = gatewatcher.getAttackerForHelper();
                    if (target)
                        AttackStart(target);
                    gatewatcher.GetAI().JustSummoned(me);
                }
            }

            public override void JustDied(Unit killer)
            {
                DoCastAOE(SpellIds.AcidSplash);
                base.JustDied(killer);
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_skittering_infectorAI>(creature);
        }
    }

    [Script]
    class npc_gatewatcher_web_wrap : CreatureScript
    {
        public npc_gatewatcher_web_wrap() : base("npc_gatewatcher_web_wrap") { }

        class npc_gatewatcher_web_wrapAI : NullCreatureAI
        {
            public npc_gatewatcher_web_wrapAI(Creature creature) : base(creature) { }

            public override void JustDied(Unit killer)
            {
                TempSummon meSummon = me.ToTempSummon();
                if (meSummon)
                {
                    Unit summoner = meSummon.GetSummoner();
                    if (summoner)
                        summoner.RemoveAurasDueToSpell(SpellIds.WebWrapWrapped);
                }
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<npc_gatewatcher_web_wrapAI>(creature);
        }
    }

    [Script]
    class spell_gatewatcher_subboss_trigger : SpellScriptLoader
    {
        public spell_gatewatcher_subboss_trigger() : base("spell_gatewatcher_subboss_trigger") { }

        class spell_gatewatcher_subboss_trigger_SpellScript : SpellScript
        {
            void HandleTargets(List<WorldObject> targetList)
            {
                // Remove any Watchers that are already in combat
                foreach (var obj in targetList.ToList())
                {
                    Creature creature = obj.ToCreature();
                    if (creature)
                        if (creature.IsAlive() && !creature.IsInCombat())
                            continue;

                    targetList.Remove(obj);
                }

                // Default to Krik'thir himself if he isn't engaged
                WorldObject target = null;
                if (GetCaster() && !GetCaster().IsInCombat())
                    target = GetCaster();
                // Unless there are Watchers that aren't engaged yet
                if (!targetList.Empty())
                {
                    // If there are, pick one of them at random
                    target = targetList.PickRandom();
                }
                // And hit only that one
                targetList.Clear();
                if (target)
                    targetList.Add(target);
            }

            public override void Register()
            {
                OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(HandleTargets, 0, Targets.UnitSrcAreaEntry));
            }
        }

        public override SpellScript GetSpellScript()
        {
            return new spell_gatewatcher_subboss_trigger_SpellScript();
        }
    }

    [Script]
    class spell_anub_ar_skirmisher_fixtate : SpellScriptLoader
    {
        public spell_anub_ar_skirmisher_fixtate() : base("spell_anub_ar_skirmisher_fixtate") { }

        class spell_anub_ar_skirmisher_fixtate_SpellScript : SpellScript
        {
            public override bool Validate(SpellInfo spell)
            {
                return ValidateSpellInfo(SpellIds.FixtateTriggered);
            }

            void HandleScript(uint effIndex)
            {
                Unit target = GetHitUnit();
                if (target)
                    target.CastSpell(GetCaster(), SpellIds.FixtateTriggered, true);
            }

            public override void Register()
            {
                OnEffectHitTarget.Add(new EffectHandler(HandleScript, 0, SpellEffectName.ScriptEffect));
            }
        }

        public override SpellScript GetSpellScript()
        {
            return new spell_anub_ar_skirmisher_fixtate_SpellScript();
        }
    }

    [Script]
    class spell_gatewatcher_web_wrap : SpellScriptLoader
    {
        public spell_gatewatcher_web_wrap() : base("spell_gatewatcher_web_wrap") { }

        class spell_gatewatcher_web_wrap_AuraScript : AuraScript
        {
            public override bool Validate(SpellInfo spell)
            {
                return ValidateSpellInfo(SpellIds.WebWrapWrapped);
            }

            void HandleEffectRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
            {
                if (GetTargetApplication().GetRemoveMode() != AuraRemoveMode.Expire)
                    return;

                Unit target = GetTarget();
                if (target)
                    target.CastSpell(target, SpellIds.WebWrapWrapped, true);
            }

            public override void Register()
            {
                OnEffectRemove.Add(new EffectApplyHandler(HandleEffectRemove, 0, AuraType.ModRoot, AuraEffectHandleModes.Real));
            }
        }

        public override AuraScript GetAuraScript()
        {
            return new spell_gatewatcher_web_wrap_AuraScript();
        }
    }

    [Script]
    class achievement_watch_him_die : AchievementCriteriaScript
    {
        public achievement_watch_him_die() : base("achievement_watch_him_die") { }

        public override bool OnCheck(Player player, Unit target)
        {
            if (!target)
                return false;

            InstanceScript instance = target.GetInstanceScript();
            if (instance == null)
                return false;

            foreach (uint watcherData in new[] { ANDataTypes.WatcherGashra, ANDataTypes.WatcherNarjil, ANDataTypes.WatcherSilthik })
            {
                Creature watcher = instance.GetCreature(watcherData);
                if (watcher)
                    if (watcher.IsAlive())
                        continue;
                return false;
            }

            return true;
        }
    }
}
