/*
 * Copyright (C) 2010 - 2013 ProjectSkyfire <http://www.projectskyfire.org/>
 *
 * Copyright (C) 2011 - 2013 ArkCORE <http://www.arkania.net/>
 * Copyright (C) 2008 - 2013 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 *
 * 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 2 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/>.
 */

/*
 * Scripts for spells with SPELLFAMILY_PRIEST and SPELLFAMILY_GENERIC spells used by priest players.
 * Ordered alphabetically using scriptname.
 * Scriptnames of files in this file should be prefixed with "spell_pri_".
 */

#include "ScriptPCH.h"
#include "SpellAuraEffects.h"

enum PriestSpells
{
    PRIEST_SPELL_GUARDIAN_SPIRIT_HEAL           = 48153,
    PRIEST_SPELL_PENANCE_R1                     = 47540,
    PRIEST_SPELL_PENANCE_R1_DAMAGE              = 47758,
    PRIEST_SPELL_PENANCE_R1_HEAL                = 47757,
    PRIEST_SPELL_REFLECTIVE_SHIELD_TRIGGERED    = 33619,
    PRIEST_SPELL_REFLECTIVE_SHIELD_R1           = 33201,
};

// Guardian Spirit
class spell_pri_guardian_spirit: public SpellScriptLoader
{
public:
    spell_pri_guardian_spirit () : SpellScriptLoader("spell_pri_guardian_spirit") { }

    class spell_pri_guardian_spirit_AuraScript: public AuraScript
    {
        PrepareAuraScript(spell_pri_guardian_spirit_AuraScript);

        uint32 healPct;

        bool Validate (SpellEntry const * /*spellEntry*/)
        {
            return sSpellStore.LookupEntry(PRIEST_SPELL_GUARDIAN_SPIRIT_HEAL);
                    return false;
                return true;
		}

        bool Load ()
        {
            healPct = SpellMgr::CalculateSpellEffectAmount(GetSpellProto(), EFFECT_1);
            return true;
        }

        void CalculateAmount (AuraEffect const * /*aurEff*/, int32 & amount, bool & canBeRecalculated)
        {
            // Set absorbtion amount to unlimited
            amount = -1;
        }

        void Absorb (AuraEffect * aurEff, DamageInfo & dmgInfo, uint32 & absorbAmount)
        {
            Unit * target = GetTarget();
            if (dmgInfo.GetDamage() < target->GetHealth())
                return;

            int32 healAmount = int32(target->CountPctFromMaxHealth(healPct));
            // remove the aura now, we don't want 40% healing bonus
            Remove(AURA_REMOVE_BY_ENEMY_SPELL);
            target->CastCustomSpell(target, PRIEST_SPELL_GUARDIAN_SPIRIT_HEAL, &healAmount, NULL, NULL, true);
            absorbAmount = dmgInfo.GetDamage();
        }

        void Register ()
        {
            DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pri_guardian_spirit_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_SCHOOL_ABSORB);
            OnEffectAbsorb += AuraEffectAbsorbFn(spell_pri_guardian_spirit_AuraScript::Absorb, EFFECT_1);
        }
    };

    AuraScript *GetAuraScript () const
    {
        return new spell_pri_guardian_spirit_AuraScript();
    }
};

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

    class spell_pri_mana_burn_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_pri_mana_burn_SpellScript)
        bool Validate (SpellEntry const * /*spellEntry*/)
        {
            return true;
        }

        void HandleAfterHit ()
        {
            Unit * unitTarget = GetHitUnit();
            if (!unitTarget)
                return;

            unitTarget->RemoveAurasWithMechanic((1 << MECHANIC_FEAR) | (1 << MECHANIC_POLYMORPH));
        }

        void Register ()
        {
            AfterHit += SpellHitFn(spell_pri_mana_burn_SpellScript::HandleAfterHit);
        }
    };

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

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

    // 47948 Pain and Suffering (proc)
    class spell_pri_pain_and_suffering_proc_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_pri_pain_and_suffering_proc_SpellScript)
        void HandleEffectScriptEffect (SpellEffIndex /*effIndex*/)
        {
            // Refresh Shadow Word: Pain on target
            if (Unit *unitTarget = GetHitUnit())
                if (AuraEffect* aur = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_PRIEST, 0x8000, 0, 0, GetCaster()->GetGUID()))
                    aur->GetBase()->RefreshDuration();
        }

        void Register ()
        {
            OnEffect += SpellEffectFn(spell_pri_pain_and_suffering_proc_SpellScript::HandleEffectScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
        }
    };

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

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

    class spell_pri_mind_sear_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_pri_mind_sear_SpellScript)
        ;

        void FilterTargets (std::list<Unit*>& unitList)
        {
            unitList.remove_if(Trinity::ObjectGUIDCheck(GetCaster()->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT)));
        }

        void Register ()
        {
            OnUnitTargetSelect += SpellUnitTargetFn(spell_pri_mind_sear_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_AREA_ENEMY_DST);
        }
    };

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

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

    class spell_pri_penance_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_pri_penance_SpellScript)
        bool Validate (SpellEntry const * spellEntry)
        {
            if (!sSpellStore.LookupEntry(PRIEST_SPELL_PENANCE_R1))
                return false;
            // can't use other spell than this penance due to spell_ranks dependency
            if (sSpellMgr->GetFirstSpellInChain(PRIEST_SPELL_PENANCE_R1) != sSpellMgr->GetFirstSpellInChain(spellEntry->Id))
                return false;

            uint8 rank = sSpellMgr->GetSpellRank(spellEntry->Id);
            if (!sSpellMgr->GetSpellWithRank(PRIEST_SPELL_PENANCE_R1_DAMAGE, rank, true))
                return false;
            if (!sSpellMgr->GetSpellWithRank(PRIEST_SPELL_PENANCE_R1_HEAL, rank, true))
                return false;

            return true;
        }

        void HandleDummy (SpellEffIndex /*effIndex*/)
        {
            Unit *unitTarget = GetHitUnit();
            if (!unitTarget || !unitTarget->isAlive())
                return;

            Unit *caster = GetCaster();

            uint8 rank = sSpellMgr->GetSpellRank(GetSpellInfo()->Id);

            if (caster->IsFriendlyTo(unitTarget))
                caster->CastSpell(unitTarget, sSpellMgr
                ->GetSpellWithRank(PRIEST_SPELL_PENANCE_R1_HEAL, rank), false, 0);
                else
                caster->CastSpell(unitTarget, sSpellMgr->GetSpellWithRank(PRIEST_SPELL_PENANCE_R1_DAMAGE, rank), false, 0);
            }

            void Register()
            {
                // add dummy effect spell handler to Penance
                OnEffect += SpellEffectFn(spell_pri_penance_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
            }
        };

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

// Reflective Shield
class spell_pri_reflective_shield_trigger: public SpellScriptLoader
{
public:
    spell_pri_reflective_shield_trigger () : SpellScriptLoader("spell_pri_reflective_shield_trigger") { }

    class spell_pri_reflective_shield_trigger_AuraScript: public AuraScript
    {
        PrepareAuraScript(spell_pri_reflective_shield_trigger_AuraScript);

        bool Validate (SpellEntry const * /*spellEntry*/)
        {
            return sSpellStore.LookupEntry(PRIEST_SPELL_REFLECTIVE_SHIELD_TRIGGERED) && sSpellStore.LookupEntry(PRIEST_SPELL_REFLECTIVE_SHIELD_R1);
                    return false;
                return true;
		}

        void Trigger (AuraEffect * aurEff, DamageInfo & dmgInfo, uint32 & absorbAmount)
        {
            Unit * target = GetTarget();
            if (dmgInfo.GetAttacker() == target)
                return;
            Unit * caster = GetCaster();
            if (!caster)
                return;
            if (AuraEffect * talentAurEff = target->GetAuraEffectOfRankedSpell(PRIEST_SPELL_REFLECTIVE_SHIELD_R1, EFFECT_0))
            {
                int32 bp = CalculatePctN(absorbAmount, talentAurEff->GetAmount());
                target->CastCustomSpell(dmgInfo.GetAttacker(), PRIEST_SPELL_REFLECTIVE_SHIELD_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff);
            }
        }

        void Register ()
        {
            AfterEffectAbsorb += AuraEffectAbsorbFn(spell_pri_reflective_shield_trigger_AuraScript::Trigger, EFFECT_0);
        }
    };

    AuraScript *GetAuraScript () const
    {
        return new spell_pri_reflective_shield_trigger_AuraScript();
    }
};

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

    class spell_priest_flash_heal_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_priest_flash_heal_SpellScript)

        bool Validate (SpellEntry const * /*spellEntry*/)
        {
            return true;
        }

        void HandleDummy (SpellEffIndex /*effIndex*/)
        {
            if (Unit * caster = GetCaster())
            {
                if (caster->GetTypeId() != TYPEID_PLAYER)
                    return;

                caster->ToPlayer()->KilledMonsterCredit(44175, 0);
            }
        }

        void Register ()
        {
            OnEffect += SpellEffectFn(spell_priest_flash_heal_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_HEAL);
        }
    };

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

// Shadow Word: Death
// Spell Id: 32379
class spell_pri_shadow_word_death: public SpellScriptLoader
{
public:
    spell_pri_shadow_word_death () : SpellScriptLoader("spell_pri_shadow_word_death") { }

    class spell_pri_shadow_word_death_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_pri_shadow_word_death_SpellScript);

        void DamageCaster (SpellEffIndex effIndex)
        {
            if (Unit* caster = GetCaster())
            {
                int32 back_damage = caster->SpellDamageBonus(GetHitUnit(), GetSpellInfo(), effIndex, uint32(GetHitDamage()), SPELL_DIRECT_DAMAGE);
                if (AuraEffect* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 2874, 1))
                    back_damage -= back_damage * (-aurEff->GetAmount() / 100);

                if (back_damage < int32(GetHitUnit()->GetHealth()))
                {
                    caster->CastCustomSpell(caster, 32409, &back_damage, NULL, NULL, true);
                    if (GetHitUnit()->HealthBelowPct(25))
                    {
                        SetHitDamage(int32(GetHitDamage() * 3));                              // Deals 3 times more damage to targets below 25% health

                        if (caster->HasAura(55682) && !GetHitUnit()->HasAura(95652))          // Glyph of Shadow Word: Death
                        {
                            caster->AddAura(95652, GetHitUnit());                             // Glyph of Shadow Word: Death - Marker
                            if (caster->GetTypeId() == TYPEID_PLAYER)
                                caster->ToPlayer()->RemoveSpellCooldown(32379, true);         // Shadow Word: Death
                        }
                    }
                }
            }
        }

        void Register ()
        {
            OnEffectHitTarget += SpellEffectFn(spell_pri_shadow_word_death_SpellScript::DamageCaster, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
        }
    };

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

// Mind Blast
// Spell Id: 8092
class spell_pri_mind_blast: public SpellScriptLoader
{
public:
    spell_pri_mind_blast () : SpellScriptLoader("spell_pri_mind_blast") { }

    class spell_pri_mind_blast_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_pri_mind_blast_SpellScript);

        void HandleDamage (SpellEffIndex /*effIndex*/)
        {
            if (Unit* caster = GetCaster())
            {
                if (AuraEffect * aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 95, 1))          // If we have Improved Mind Blast
                    if (caster->GetShapeshiftForm() == FORM_SHADOW)
                        if (roll_chance_i(aurEff->GetAmount()))
                            caster->CastSpell(GetHitUnit(), 48301, true);                                 // Cast Mind Trauma

                // Remove Mind Melt
                caster->RemoveAurasDueToSpell(87160);
                caster->RemoveAurasDueToSpell(81292);
            }
        }

        void Register ()
        {
            OnEffectHitTarget += SpellEffectFn(spell_pri_mind_blast_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
        }
    };

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

// Power Word: Fortitude
// Spell Id: 21562
class spell_pri_power_word_fortitude: public SpellScriptLoader
{
public:
    spell_pri_power_word_fortitude () : SpellScriptLoader("spell_pri_power_word_fortitude") { }

    class spell_pri_power_word_fortitude_SpellScript: public SpellScript
    {
        PrepareSpellScript(spell_pri_power_word_fortitude_SpellScript)
        ;

        void HandleDummy (SpellEffIndex /*effIndex*/)
        {
            if (Unit* caster = GetCaster())
            {
                std::list<Unit*> PartyMembers;
                caster->GetPartyMembers(PartyMembers);
                if (PartyMembers.size() > 1)
                    caster->CastSpell(GetHitUnit(), 79105, true);          // Power Word : Fortitude (Raid)
                else
                    caster->CastSpell(GetHitUnit(), 79104, true);          // Power Word : Fortitude (Caster)
            }
        }

        void Register ()
        {
            OnEffectHitTarget += SpellEffectFn(spell_pri_power_word_fortitude_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
        }
    };

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

void AddSC_priest_spell_scripts ()
{
    new spell_pri_guardian_spirit();
    new spell_pri_mana_burn;
    new spell_pri_pain_and_suffering_proc;
    new spell_pri_penance;
    new spell_pri_reflective_shield_trigger();
    new spell_priest_flash_heal;
    new spell_pri_mind_sear();
    new spell_pri_shadow_word_death();
    new spell_pri_mind_blast();
    new spell_pri_power_word_fortitude();
}