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

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "trial_of_the_champion.h"
#include "SpellInfo.h"

enum Spells
{
    SPELL_RAISE_DEAD_JAEREN         = 67715,
    SPELL_RAISE_DEAD_ARELAS         = 67705,
    SPELL_BK_FEIGN_DEATH            = 67691,
    SPELL_BLACK_KNIGHT_RES          = 67693,
    SPELL_BK_KILL_CREDIT            = 68663,

    // phase 1
    SPELL_PLAGUE_STRIKE_N           = 67724,
    SPELL_PLAGUE_STRIKE_H           = 67884,
    SPELL_ICY_TOUCH_N               = 67718,
    SPELL_ICY_TOUCH_H               = 67881,
    SPELL_DEATH_RESPITE_N           = 67745,
    SPELL_DEATH_RESPITE_H           = 68306,
    SPELL_DEATH_RESPITE_DUMMY       = 66798,
    SPELL_OBLITERATE_N              = 67725,
    SPELL_OBLITERATE_H              = 67883,

    // phase 2 (+ abilities from phase 1 without death respite)
    SPELL_ARMY_DEAD_N               = 67761,
    SPELL_ARMY_DEAD_H               = 67874,
    SPELL_DESECRATION_N             = 67778,
    SPELL_DESECRATION_H             = 67877,
    SPELL_DESECRATION_SUMMON        = 67779,
    SPELL_BK_GHOUL_EXPLODE          = 67751,

    // phase 3
    SPELL_DEATH_BITE_N              = 67808,
    SPELL_DEATH_BITE_H              = 67875,
    SPELL_MARKED_DEATH_N            = 67823,
    SPELL_MARKED_DEATH_H            = 67882,

    // ghouls
    SPELL_CLAW_N                    = 67774,
    SPELL_CLAW_H                    = 67879,
    SPELL_EXPLODE_N                 = 67729,
    SPELL_EXPLODE_H                 = 67886,
    SPELL_LEAP_N                    = 67749,
    SPELL_LEAP_H                    = 67880,
};
#define SPELL_LEAP                  DUNGEON_MODE(SPELL_LEAP_N, SPELL_LEAP_H)
#define SPELL_EXPLODE               DUNGEON_MODE(SPELL_EXPLODE_N, SPELL_EXPLODE_H)

#define SPELL_PLAGUE_STRIKE         DUNGEON_MODE(SPELL_PLAGUE_STRIKE_N, SPELL_PLAGUE_STRIKE_H)
#define SPELL_ICY_TOUCH             DUNGEON_MODE(SPELL_ICY_TOUCH_N, SPELL_ICY_TOUCH_H)
#define SPELL_DEATH_RESPITE         DUNGEON_MODE(SPELL_DEATH_RESPITE_N, SPELL_DEATH_RESPITE_H)
#define SPELL_OBLITERATE            DUNGEON_MODE(SPELL_OBLITERATE_N, SPELL_OBLITERATE_H)
#define SPELL_ARMY_DEAD             DUNGEON_MODE(SPELL_ARMY_DEAD_N, SPELL_ARMY_DEAD_H)
#define SPELL_DESECRATION           DUNGEON_MODE(SPELL_DESECRATION_N, SPELL_DESECRATION_H)
#define SPELL_DEATH_BITE            DUNGEON_MODE(SPELL_DEATH_BITE_N, SPELL_DEATH_BITE_H)
#define SPELL_MARKED_DEATH          DUNGEON_MODE(SPELL_MARKED_DEATH_N, SPELL_MARKED_DEATH_H)

enum Events
{
    EVENT_ANNOUNCER_SAY_ZOMBIE = 1,
    EVENT_SPELL_PLAGUE_STRIKE,
    EVENT_SPELL_ICY_TOUCH,
    EVENT_SPELL_DEATH_RESPITE,
    EVENT_SPELL_OBLITERATE,
    EVENT_SPELL_DESECRATION,
    EVENT_SPELL_DEATH_BITE,
    EVENT_SPELL_MARKED_DEATH,
};

enum NPCs
{
    NPC_RISEN_CHAMPION              = 35590,
};

enum Models
{
    MODEL_SKELETON                  = 29846,
    MODEL_GHOST                     = 21300
};


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

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

        InstanceScript* pInstance;
        EventMap events;
        SummonList summons;
        uint8 Phase;

        void Reset()
        {
            events.Reset();
            summons.DespawnAll();
            Phase = 1;
            me->SetDisplayId(me->GetNativeDisplayId());
            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
            me->SetReactState(REACT_PASSIVE);
            if( pInstance )
                pInstance->SetData(BOSS_BLACK_KNIGHT, NOT_STARTED);

            //me->SetLootMode(0); // [LOOT]
        }

        void EnterEvadeMode()
        {
            me->DespawnOrUnsummon(1);
            ScriptedAI::EnterEvadeMode();
        }

        void DamageTaken(Unit*, uint32 &damage, DamageEffectType, SpellSchoolMask)
        {
            if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
            {
                damage = 0;
                return;
            }

            if( Phase < 3 && damage >= me->GetHealth() )
            {
                damage = 0;
                me->SetHealth(me->GetMaxHealth());
                events.Reset();
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
                me->RemoveAllAuras();
                me->SetControlled(true, UNIT_STATE_STUNNED);
                me->CastSpell(me, SPELL_BK_GHOUL_EXPLODE, true);
                summons.clear();

                me->CastSpell(me, SPELL_BK_FEIGN_DEATH, true);
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29);
                me->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
                me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
                me->AddUnitState(UNIT_STATE_DIED);

            }
        }

        void DoAction(int32 param)
        {
            if( param == -1 )
            {
                summons.DespawnAll();
            }
            else if( param == 1 )
            {
                if( !pInstance )
                    return;

                pInstance->SetData(BOSS_BLACK_KNIGHT, IN_PROGRESS);
                Talk(TEXT_BK_AGGRO);
                me->CastSpell((Unit*)NULL, (pInstance->GetData(DATA_TEAMID_IN_INSTANCE)==TEAM_HORDE ? SPELL_RAISE_DEAD_JAEREN : SPELL_RAISE_DEAD_ARELAS), false);
                if( Creature* announcer = pInstance->instance->GetCreature(pInstance->GetData64(DATA_ANNOUNCER)) )
                    announcer->DespawnOrUnsummon();

                events.Reset();
                events.ScheduleEvent(EVENT_ANNOUNCER_SAY_ZOMBIE, 2500);
                events.ScheduleEvent(EVENT_SPELL_PLAGUE_STRIKE, urand(7000,9000));
                events.ScheduleEvent(EVENT_SPELL_ICY_TOUCH, urand(3500,7000));
                events.ScheduleEvent(EVENT_SPELL_DEATH_RESPITE, urand(13000,15000));
                events.ScheduleEvent(EVENT_SPELL_OBLITERATE, urand(11000,19000));
            }
        }

        void SpellHitTarget(Unit* target, const SpellInfo* spell)
        {
            switch( spell->Id )
            {
                case SPELL_BLACK_KNIGHT_RES:
                    me->SetHealth(me->GetMaxHealth());
                    me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
                    me->SetControlled(false, UNIT_STATE_STUNNED);
                    
                    me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29);
                    me->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
                    me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
                    me->ClearUnitState(UNIT_STATE_DIED);

                    ++Phase;

                    switch( Phase )
                    {
                        case 2:
                            me->SetDisplayId(MODEL_SKELETON);
                            Talk(TEXT_BK_SKELETON_RES);
                            me->CastSpell(me, SPELL_ARMY_DEAD, false);

                            events.Reset();
                            events.ScheduleEvent(EVENT_SPELL_PLAGUE_STRIKE, urand(7000,9000));
                            events.ScheduleEvent(EVENT_SPELL_ICY_TOUCH, urand(3500,7000));
                            events.ScheduleEvent(EVENT_SPELL_OBLITERATE, urand(11000,19000));
                            events.ScheduleEvent(EVENT_SPELL_DESECRATION, urand(2000,3000));
                            break;
                        case 3:
                            me->SetDisplayId(MODEL_GHOST);
                            Talk(TEXT_BK_GHOST_RES);

                            events.Reset();
                            events.ScheduleEvent(EVENT_SPELL_DEATH_BITE, 2000);
                            events.ScheduleEvent(EVENT_SPELL_MARKED_DEATH, 1000);
                            break;
                        default:
                            EnterEvadeMode();
                            break;
                    }
                    break;
            }
        }

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

            events.Update(diff);

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

            switch( events.GetEvent() )
            {
                case 0:
                    break;
                case EVENT_ANNOUNCER_SAY_ZOMBIE:
                    if( pInstance && !summons.empty() )
                        if( Creature* ghoul = pInstance->instance->GetCreature(*summons.begin()) )
                            ghoul->MonsterYell("[Zombie] .... . Brains ....", LANG_UNIVERSAL, 0);
                    events.PopEvent();
                    break;
                case EVENT_SPELL_PLAGUE_STRIKE:
                    if( me->GetVictim() )
                        me->CastSpell(me->GetVictim(), SPELL_PLAGUE_STRIKE, false);
                    events.RepeatEvent(urand(10000,12000));
                    break;
                case EVENT_SPELL_ICY_TOUCH:
                    if( me->GetVictim() )
                        me->CastSpell(me->GetVictim(), SPELL_ICY_TOUCH, false);
                    events.RepeatEvent(urand(5000,6000));
                    break;
                case EVENT_SPELL_DEATH_RESPITE:
                    if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true) )
                        me->CastSpell(target, SPELL_DEATH_RESPITE, false);
                    events.RepeatEvent(urand(13000,15000));
                    break;
                case EVENT_SPELL_OBLITERATE:
                    if( me->GetVictim() )
                        me->CastSpell(me->GetVictim(), SPELL_OBLITERATE, false);
                    events.RepeatEvent(urand(15000,17000));
                    break;
                case EVENT_SPELL_DESECRATION:
                    if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true) )
                        me->CastSpell(target, SPELL_DESECRATION, false);
                    events.RepeatEvent(urand(14000,17000));
                    break;
                case EVENT_SPELL_DEATH_BITE:
                    me->CastSpell((Unit*)NULL, SPELL_DEATH_BITE, false);
                    events.RepeatEvent(urand(2000,4000));
                    break;
                case EVENT_SPELL_MARKED_DEATH:
                    if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.000000f, true) )
                        me->CastSpell(target, SPELL_MARKED_DEATH, false);
                    events.RepeatEvent(9000);
                    break;
            }

            DoMeleeAttackIfReady();
        }

        void JustSummoned(Creature* summon)
        {
            summons.Summon(summon);
            if( Unit* target = summon->SelectNearestTarget(200.0f) )
            {
                summon->AI()->AttackStart(target);
                DoZoneInCombat(summon);
            }
        }

        void KilledUnit(Unit* victim)
        {
            if( victim->GetTypeId() == TYPEID_PLAYER )
            {
                if( urand(0,1) )
                    Talk(TEXT_BK_SLAIN_1);
                else
                    Talk(TEXT_BK_SLAIN_2);
            }
        }

        void JustDied(Unit* /*killer*/)
        {
            me->CastSpell((Unit*)NULL, SPELL_BK_KILL_CREDIT, true);
            Talk(TEXT_BK_DEATH);
            if( pInstance )
                pInstance->SetData(BOSS_BLACK_KNIGHT, DONE);
            if( me->ToTempSummon() )
                me->ToTempSummon()->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN);
        }
    };

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

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

    struct npc_black_knight_skeletal_gryphonAI : public npc_escortAI
    {
        npc_black_knight_skeletal_gryphonAI(Creature* pCreature) : npc_escortAI(pCreature) {}

        void Reset()
        {
            Start(false,true,0,NULL);
            SetDespawnAtEnd(true);
        }

        void DoAction(int32 param)
        {
            if( param == 1 )
            {
                me->SetControlled(false, UNIT_STATE_ROOT);
                me->DisableRotate(false);
                me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
                SetEscortPaused(false);
            }
        }

        void WaypointReached(uint32 i)
        {
            if( i == 12 )
            {
                SetEscortPaused(true);
                me->SetOrientation(3.62f);
                me->SetControlled(true, UNIT_STATE_ROOT);
                me->DisableRotate(true);
                me->SetFacingTo(3.62f);
                me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_MOUNT_SPECIAL);
                if( InstanceScript* pInstance = me->GetInstanceScript() )
                    pInstance->SetData(DATA_SKELETAL_GRYPHON_LANDED, 0);
            }
        }

        void UpdateAI(uint32 uiDiff)
        {
            npc_escortAI::UpdateAI(uiDiff);
        }

    };

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

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

    struct npc_black_knight_ghoulAI : public ScriptedAI
    {
        npc_black_knight_ghoulAI(Creature* pCreature) : ScriptedAI(pCreature)
        {
            pInstance = pCreature->GetInstanceScript();
        }

        InstanceScript* pInstance;
        EventMap events;

        void Reset()
        {
            events.Reset();
        }

        void EnterCombat(Unit* who)
        {
            events.Reset();
            if (me->GetEntry() == NPC_RISEN_JAEREN || me->GetEntry() == NPC_RISEN_ARELAS)
                events.RescheduleEvent(1, 1000); // leap
            events.RescheduleEvent(2, urand(3000,4000)); // claw
        }

        void SpellHit(Unit* caster, const SpellInfo* spell)
        {
            if (spell->Id == SPELL_BK_GHOUL_EXPLODE)
            {
                me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
                me->CastSpell(me, SPELL_EXPLODE, false);
            }
        }

        void SpellHitTarget(Unit* target, const SpellInfo* spell)
        {
            switch(spell->Id)
            {
                case SPELL_CLAW_N:
                case SPELL_CLAW_H:
                    DoResetThreat();
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
                    {
                        me->AddThreat(target, 100.0f);
                        AttackStart(target);
                    }
                    break;
                case SPELL_EXPLODE_H:
                    if (target && target->GetTypeId() == TYPEID_PLAYER)
                        if (pInstance)
                            pInstance->SetData(DATA_ACHIEV_IVE_HAD_WORSE, 0);
                    break;
            }
        }

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

            events.Update(diff);

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

            switch( events.GetEvent() )
            {
                case 0:
                    break;
                case 1: // leap
                    if (Unit* target = me->GetVictim())
                        if (me->GetDistance(target) > 5.0f && me->GetDistance(target) < 30.0f)
                        {
                            me->CastSpell(target, SPELL_LEAP, false);
                            events.PopEvent();
                            break;
                        }
                    events.RepeatEvent(1000);
                    break;
                case 2: // claw
                    if (Unit* target = me->GetVictim())
                        me->CastSpell(target, SPELL_CLAW_N, false);
                    events.RepeatEvent(urand(6000,8000));
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };

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

void AddSC_boss_black_knight()
{
    new boss_black_knight();
    new npc_black_knight_skeletal_gryphon();
    new npc_black_knight_ghoul();
}
