/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "shattered_halls.h"
#include "CreatureTextMgr.h"

class instance_shattered_halls : public InstanceMapScript
{
    public:
        instance_shattered_halls() : InstanceMapScript("instance_shattered_halls", 540) { }

        InstanceScript* GetInstanceScript(InstanceMap* map) const
        {
            return new instance_shattered_halls_InstanceMapScript(map);
        }

        struct instance_shattered_halls_InstanceMapScript : public InstanceScript
        {
            instance_shattered_halls_InstanceMapScript(Map* map) : InstanceScript(map) { }

            void Initialize()
            {
                SetBossNumber(ENCOUNTER_COUNT);
                nethekurseDoor1GUID = 0;
                nethekurseDoor2GUID = 0;
                warchiefKargathGUID = 0;

                executionerGUID = 0;
                memset(&prisonerGUID, 0, sizeof(prisonerGUID));
                TeamIdInInstance = TEAM_NEUTRAL;
                RescueTimer = 100*MINUTE*IN_MILLISECONDS;
            }

            void OnPlayerEnter(Player* player)
            {
                if (TeamIdInInstance == TEAM_NEUTRAL)
                    TeamIdInInstance = player->GetTeamId();
            }

            void OnGameObjectCreate(GameObject* go)
            {
                switch (go->GetEntry())
                {
                    case GO_GRAND_WARLOCK_CHAMBER_DOOR_1:
                        nethekurseDoor1GUID = go->GetGUID();
                        if (GetBossState(DATA_NETHEKURSE) == DONE)
                            HandleGameObject(0, true, go);
                        break;
                    case GO_GRAND_WARLOCK_CHAMBER_DOOR_2:
                        nethekurseDoor2GUID = go->GetGUID();
                        if (GetBossState(DATA_NETHEKURSE) == DONE)
                            HandleGameObject(0, true, go);
                        break;
                }
            }

            void OnCreatureCreate(Creature* creature)
            {
                if (TeamIdInInstance == TEAM_NEUTRAL)
                {
                    Map::PlayerList const &players = instance->GetPlayers();
                    if (!players.isEmpty())
                        if (Player* player = players.begin()->GetSource())
                            TeamIdInInstance = player->GetTeamId();
                }

                switch (creature->GetEntry())
                {
                    case NPC_WARCHIEF_KARGATH:
                        warchiefKargathGUID = creature->GetGUID();
                        break;
                    case NPC_SHATTERED_EXECUTIONER:
                        if (RescueTimer > 25*MINUTE*IN_MILLISECONDS)
                            creature->AddLootMode(2);
                        executionerGUID = creature->GetGUID();
                        break;
                    case NPC_RIFLEMAN_BROWNBEARD:
                        if (TeamIdInInstance == TEAM_HORDE)
                            creature->UpdateEntry(NPC_KORAG_PROUDMANE);
                        prisonerGUID[0] = creature->GetGUID();
                        break;
                    case NPC_CAPTAIN_ALINA:
                        if (TeamIdInInstance == TEAM_HORDE)
                            creature->UpdateEntry(NPC_CAPTAIN_BONESHATTER);
                        prisonerGUID[1] = creature->GetGUID();
                        break;
                    case NPC_PRIVATE_JACINT:
                        if (TeamIdInInstance == TEAM_HORDE)
                            creature->UpdateEntry(NPC_SCOUT_ORGARR);
                        prisonerGUID[2] = creature->GetGUID();
                        break;
                }
            }

            bool SetBossState(uint32 type, EncounterState state)
            {
                if (!InstanceScript::SetBossState(type, state))
                    return false;

                switch (type)
                {
                    case DATA_NETHEKURSE:
                        if (state == IN_PROGRESS)
                        {
                            HandleGameObject(nethekurseDoor1GUID, false);
                            HandleGameObject(nethekurseDoor2GUID, false);
                        }
                        else
                        {
                            HandleGameObject(nethekurseDoor1GUID, true);
                            HandleGameObject(nethekurseDoor2GUID, true);
                        }
                        break;
                }
                return true;
            }

            void SetData(uint32 type, uint32 data)
            {
                if (type == DATA_ENTERED_ROOM && data == DATA_ENTERED_ROOM && RescueTimer == 100*MINUTE*IN_MILLISECONDS)
                {
                    DoCastSpellOnPlayers(SPELL_KARGATHS_EXECUTIONER_1);
                    instance->LoadGrid(230, -80);

                    if (Creature* kargath = instance->GetCreature(warchiefKargathGUID))
                        sCreatureTextMgr->SendChat(kargath, TeamIdInInstance == TEAM_ALLIANCE ? 3 : 4, NULL, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP);

                    RescueTimer = 80*MINUTE*IN_MILLISECONDS;
                }
            }

            uint64 GetData64(uint32 data) const
            {
                switch (data)
                {
                    case DATA_PRISONER_1:
                    case DATA_PRISONER_2:
                    case DATA_PRISONER_3:
                        return prisonerGUID[data-DATA_PRISONER_1];
                    case DATA_EXECUTIONER:
                        return executionerGUID;
                }
                return 0;
            }

            void Update(uint32 diff)
            {
                if (RescueTimer && RescueTimer < 100*MINUTE*IN_MILLISECONDS)
                {
                    RescueTimer -= std::min(RescueTimer, diff);

                    if ((RescueTimer / IN_MILLISECONDS) == 25*MINUTE)
                    {
                        DoRemoveAurasDueToSpellOnPlayers(SPELL_KARGATHS_EXECUTIONER_1);
                        DoCastSpellOnPlayers(SPELL_KARGATHS_EXECUTIONER_2);
                        if (Creature* prisoner = instance->GetCreature(prisonerGUID[0]))
                            Unit::Kill(prisoner, prisoner);
                        if (Creature* executioner = instance->GetCreature(executionerGUID))
                            executioner->RemoveLootMode(2);
                    }
                    else if ((RescueTimer / IN_MILLISECONDS) == 15*MINUTE)
                    {
                        DoRemoveAurasDueToSpellOnPlayers(SPELL_KARGATHS_EXECUTIONER_2);
                        DoCastSpellOnPlayers(SPELL_KARGATHS_EXECUTIONER_3);
                        if (Creature* prisoner = instance->GetCreature(prisonerGUID[1]))
                            Unit::Kill(prisoner, prisoner);
                    }
                    else if ((RescueTimer / IN_MILLISECONDS) == 0)
                    {
                        DoRemoveAurasDueToSpellOnPlayers(SPELL_KARGATHS_EXECUTIONER_3);
                        if (Creature* prisoner = instance->GetCreature(prisonerGUID[2]))
                            Unit::Kill(prisoner, prisoner);
                    }
                }
            }

            std::string GetSaveData()
            {
                OUT_SAVE_INST_DATA;

                std::ostringstream saveStream;
                saveStream << "S H " << GetBossSaveData() << ' ' << RescueTimer;

                OUT_SAVE_INST_DATA_COMPLETE;
                return saveStream.str();
            }

            void Load(const char* strIn)
            {
                if (!strIn)
                {
                    OUT_LOAD_INST_DATA_FAIL;
                    return;
                }

                OUT_LOAD_INST_DATA(strIn);

                char dataHead1, dataHead2;

                std::istringstream loadStream(strIn);
                loadStream >> dataHead1 >> dataHead2;

                if (dataHead1 == 'S' && dataHead2 == 'H')
                {
                    for (uint8 i = 0; i < ENCOUNTER_COUNT; ++i)
                    {
                        uint32 tmpState;
                        loadStream >> tmpState;
                        if (tmpState == IN_PROGRESS || tmpState > SPECIAL)
                            tmpState = NOT_STARTED;
                        SetBossState(i, EncounterState(tmpState));
                    }

                    loadStream >> RescueTimer;
                }
                else
                    OUT_LOAD_INST_DATA_FAIL;

                OUT_LOAD_INST_DATA_COMPLETE;
            }

            protected:
                uint64 warchiefKargathGUID;
                uint64 nethekurseDoor1GUID;
                uint64 nethekurseDoor2GUID;

                uint64 executionerGUID;
                uint64 prisonerGUID[3];
                uint32 RescueTimer;
                TeamId TeamIdInInstance;
        };
};

class spell_tsh_shoot_flame_arrow : public SpellScriptLoader
{
    public:
        spell_tsh_shoot_flame_arrow() : SpellScriptLoader("spell_tsh_shoot_flame_arrow") { }

        class spell_tsh_shoot_flame_arrow_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_tsh_shoot_flame_arrow_SpellScript);

            void FilterTargets(std::list<WorldObject*>& unitList)
            {
                Trinity::Containers::RandomResizeList(unitList, 1);
            }

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
                PreventHitDefaultEffect(effIndex);
                if (Unit* target = GetHitUnit())
                    target->CastSpell(target, 30953, true);
            }

            void Register()
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_tsh_shoot_flame_arrow_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
                OnEffectHitTarget += SpellEffectFn(spell_tsh_shoot_flame_arrow_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_tsh_shoot_flame_arrow_SpellScript();
        }
};

class at_shattered_halls_execution : public AreaTriggerScript
{
    public:
        at_shattered_halls_execution() : AreaTriggerScript("at_shattered_halls_execution") { }

        bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/)
        {
            if (InstanceScript* instanceScript = player->GetInstanceScript())
                instanceScript->SetData(DATA_ENTERED_ROOM, DATA_ENTERED_ROOM);

            return true;
        }
};

void AddSC_instance_shattered_halls()
{
    new instance_shattered_halls();
    new spell_tsh_shoot_flame_arrow();
    new at_shattered_halls_execution();
}
