/*
 * 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.Entities;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Game.Scripting
{
    // helper class from which SpellScript and SpellAura derive, use these classes instead
    public class BaseSpellScript
    {
        // internal use classes & functions
        // DO NOT OVERRIDE THESE IN SCRIPTS
        public BaseSpellScript()
        {
            m_currentScriptState = SpellScriptHookType.None;
        }

        public virtual bool _Validate(SpellInfo entry)
        {
            if (!Validate(entry))
            {
                Log.outError(LogFilter.Scripts, "Spell `{0}` did not pass Validate() function of script `{1}` - script will be not added to the spell", entry.Id, m_scriptName);
                return false;
            }
            return true;
        }

        public bool ValidateSpellInfo(params uint[] spellIds)
        {
            bool allValid = true;
            foreach (uint spellId in spellIds)
            {
                if (!Global.SpellMgr.HasSpellInfo(spellId))
                {
                    Log.outError(LogFilter.Scripts, "BaseSpellScript.ValidateSpellInfo: Spell {0} does not exist.", spellId);
                    allValid = false;
                }
            }

            return allValid;
        }

        public void _Register()
        {
            m_currentScriptState = SpellScriptHookType.Registration;
            Register();
            m_currentScriptState = SpellScriptHookType.None;
        }
        public void _Unload()
        {
            m_currentScriptState = SpellScriptHookType.Unloading;
            Unload();
            m_currentScriptState = SpellScriptHookType.None;
        }

        public void _Init(string scriptname, uint spellId)
        {
            m_currentScriptState = SpellScriptHookType.None;
            m_scriptName = scriptname;
            m_scriptSpellId = spellId;
        }
        public string _GetScriptName()
        {
            return m_scriptName;
        }

        public abstract class EffectHook
        {
            public EffectHook(uint effIndex)
            {
                // effect index must be in range <0;2>, allow use of special effindexes
                Contract.Assert(_effIndex == SpellConst.EffectAll || _effIndex == SpellConst.EffectFirstFound || _effIndex < SpellConst.MaxEffects);
                _effIndex = effIndex;
            }

            public uint GetAffectedEffectsMask(SpellInfo spellEntry)
            {
                uint mask = 0;
                if ((_effIndex == SpellConst.EffectAll) || (_effIndex == SpellConst.EffectFirstFound))
                {
                    for (byte i = 0; i < SpellConst.MaxEffects; ++i)
                    {
                        if ((_effIndex == SpellConst.EffectFirstFound) && mask != 0)
                            return mask;
                        if (CheckEffect(spellEntry, i))
                            mask |= (1u << i);
                    }
                }
                else
                {
                    if (CheckEffect(spellEntry, _effIndex))
                        mask |= (1u << (int)_effIndex);
                }
                return mask;
            }

            public bool IsEffectAffected(SpellInfo spellEntry, uint effIndex)
            {
                return Convert.ToBoolean(GetAffectedEffectsMask(spellEntry) & (1 << (int)effIndex));
            }

            public abstract bool CheckEffect(SpellInfo spellEntry, uint effIndex);

            uint _effIndex;
        }

        public SpellScriptHookType m_currentScriptState { get; set; }
        public AuraScriptHookType m_currentAuraScriptState { get; set; }
        public string m_scriptName { get; set; }
        public uint m_scriptSpellId { get; set; }

        //
        // SpellScript/AuraScript interface base
        // these functions are safe to override, see notes below for usage instructions
        //
        // Function in which handler functions are registered, must be implemented in script
        public virtual void Register() { }
        // Function called on server startup, if returns false script won't be used in core
        // use for: dbc/template data presence/correctness checks
        public virtual bool Validate(SpellInfo spellEntry) { return true; }
        // Function called when script is created, if returns false script will be unloaded afterwards
        // use for: initializing local script variables (DO NOT USE CONSTRUCTOR FOR THIS PURPOSE!)
        public virtual bool Load() { return true; }
        // Function called when script is destroyed
        // use for: deallocating memory allocated by script
        public virtual void Unload() { }
    }

    public class SpellScript : BaseSpellScript
    {
        // internal use classes & functions
        // DO NOT OVERRIDE THESE IN SCRIPTS
        public delegate SpellCastResult SpellCheckCastFnType();
        public delegate void SpellEffectFnType(uint index);
        public delegate void SpellBeforeHitFnType(SpellMissInfo missInfo);
        public delegate void SpellHitFnType();
        public delegate void SpellCastFnType();
        public delegate void SpellObjectAreaTargetSelectFnType(List<WorldObject> targets);
        public delegate void SpellObjectTargetSelectFnType(ref WorldObject targets);
        public delegate void SpellDestinationTargetSelectFnType(ref SpellDestination dest);

        public class CastHandler
        {
            public CastHandler(SpellCastFnType _pCastHandlerScript) { pCastHandlerScript = _pCastHandlerScript; }

            public void Call()
            {
                pCastHandlerScript();
            }

            SpellCastFnType pCastHandlerScript;
        }

        public class CheckCastHandler
        {
            public CheckCastHandler(SpellCheckCastFnType checkCastHandlerScript)
            {
                _checkCastHandlerScript = checkCastHandlerScript;
            }

            public SpellCastResult Call()
            {
                return _checkCastHandlerScript();
            }

            SpellCheckCastFnType _checkCastHandlerScript;
        }

        public class EffectHandler : EffectHook
        {
            public EffectHandler(SpellEffectFnType pEffectHandlerScript, uint effIndex, SpellEffectName effName) : base(effIndex)
            {
                _pEffectHandlerScript = pEffectHandlerScript;
                _effName = effName;
            }

            public override bool CheckEffect(SpellInfo spellEntry, uint effIndex)
            {
                SpellEffectInfo effect = spellEntry.GetEffect(effIndex);
                if (effect == null)
                    return false;

                if (effect.Effect == 0 && _effName == 0)
                    return true;
                if (effect.Effect == 0)
                    return false;
                return (_effName == SpellEffectName.Any) || (effect.Effect == _effName);
            }

            public void Call(uint effIndex)
            {
                _pEffectHandlerScript(effIndex);
            }

            SpellEffectName _effName;
            SpellEffectFnType _pEffectHandlerScript;
        }

        public class BeforeHitHandler
        {
            public BeforeHitHandler(SpellBeforeHitFnType pBeforeHitHandlerScript)
            {
                _pBeforeHitHandlerScript = pBeforeHitHandlerScript;
            }

            public void Call(SpellMissInfo missInfo)
            {
                _pBeforeHitHandlerScript(missInfo);
            }

            SpellBeforeHitFnType _pBeforeHitHandlerScript;
        }

        public class HitHandler
        {
            public HitHandler(SpellHitFnType pHitHandlerScript)
            {
                _pHitHandlerScript = pHitHandlerScript;
            }

            public void Call()
            {
                _pHitHandlerScript();
            }

            SpellHitFnType _pHitHandlerScript;
        }

        public class TargetHook : EffectHook
        {
            public TargetHook(uint effectIndex, Targets targetType, bool area, bool dest = false) : base(effectIndex)
            {
                _targetType = targetType;
                _area = area;
                _dest = dest;
            }

            public override bool CheckEffect(SpellInfo spellEntry, uint effIndex)
            {
                if (_targetType == 0)
                    return false;

                SpellEffectInfo effect = spellEntry.GetEffect(effIndex);
                if (effect == null)
                    return false;

                if (effect.TargetA.GetTarget() != _targetType && effect.TargetB.GetTarget() != _targetType)
                    return false;

                SpellImplicitTargetInfo targetInfo = new SpellImplicitTargetInfo(_targetType);
                switch (targetInfo.GetSelectionCategory())
                {
                    case SpellTargetSelectionCategories.Channel: // SINGLE
                        return !_area;
                    case SpellTargetSelectionCategories.Nearby: // BOTH
                        return true;
                    case SpellTargetSelectionCategories.Cone: // AREA
                    case SpellTargetSelectionCategories.Area: // AREA
                        return _area;
                    case SpellTargetSelectionCategories.Default:
                        switch (targetInfo.GetObjectType())
                        {
                            case SpellTargetObjectTypes.Src: // EMPTY
                                return false;
                            case SpellTargetObjectTypes.Dest: // Dest
                                return _dest;
                            default:
                                switch (targetInfo.GetReferenceType())
                                {
                                    case SpellTargetReferenceTypes.Caster: // SINGLE
                                        return !_area;
                                    case SpellTargetReferenceTypes.Target: // BOTH
                                        return true;
                                    default:
                                        break;
                                }
                                break;
                        }
                        break;
                    default:
                        break;
                }

                return false;
            }

            public Targets GetTarget() { return _targetType; }

            Targets _targetType;
            bool _area;
            bool _dest;
        }

        public class ObjectAreaTargetSelectHandler : TargetHook
        {
            public ObjectAreaTargetSelectHandler(SpellObjectAreaTargetSelectFnType pObjectAreaTargetSelectHandlerScript, uint effIndex, Targets targetType)
                : base(effIndex, targetType, true)
            {
                _pObjectAreaTargetSelectHandlerScript = pObjectAreaTargetSelectHandlerScript;
            }

            public void Call(List<WorldObject> targets)
            {
                _pObjectAreaTargetSelectHandlerScript(targets);
            }

            SpellObjectAreaTargetSelectFnType _pObjectAreaTargetSelectHandlerScript;
        }

        public class ObjectTargetSelectHandler : TargetHook
        {
            public ObjectTargetSelectHandler(SpellObjectTargetSelectFnType _pObjectTargetSelectHandlerScript, uint _effIndex, Targets _targetType)
                : base(_effIndex, _targetType, false)
            {
                pObjectTargetSelectHandlerScript = _pObjectTargetSelectHandlerScript;
            }

            public void Call(ref WorldObject target)
            {
                pObjectTargetSelectHandlerScript(ref target);
            }

            SpellObjectTargetSelectFnType pObjectTargetSelectHandlerScript;
        }

        public class DestinationTargetSelectHandler : TargetHook
        {
            public DestinationTargetSelectHandler(SpellDestinationTargetSelectFnType _DestinationTargetSelectHandlerScript, uint _effIndex, Targets _targetType)
                : base(_effIndex, _targetType, false, true)
            {
                DestinationTargetSelectHandlerScript = _DestinationTargetSelectHandlerScript;
            }

            public void Call(ref SpellDestination target)
            {
                DestinationTargetSelectHandlerScript(ref target);
            }

            SpellDestinationTargetSelectFnType DestinationTargetSelectHandlerScript;
        }

        public override bool _Validate(SpellInfo entry)
        {
            foreach (var eff in OnEffectLaunch)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectLaunch` of SpellScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectLaunchTarget)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectLaunchTarget` of SpellScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectHit)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectHit` of SpellScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectHitTarget)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectHitTarget` of SpellScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectSuccessfulDispel)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectSuccessfulDispel` of SpellScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnObjectAreaTargetSelect)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnObjectAreaTargetSelect` of SpellScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnObjectTargetSelect)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnObjectTargetSelect` of SpellScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            return base._Validate(entry);
        }
        public bool _Load(Spell spell)
        {
            m_spell = spell;
            _PrepareScriptCall(SpellScriptHookType.Loading);
            bool load = Load();
            _FinishScriptCall();
            return load;
        }
        public void _InitHit()
        {
            m_hitPreventEffectMask = 0;
            m_hitPreventDefaultEffectMask = 0;
        }
        public bool _IsEffectPrevented(uint effIndex) { return Convert.ToBoolean(m_hitPreventEffectMask & (1 << (int)effIndex)); }
        public bool _IsDefaultEffectPrevented(uint effIndex) { return Convert.ToBoolean(m_hitPreventDefaultEffectMask & (1 << (int)effIndex)); }
        public void _PrepareScriptCall(SpellScriptHookType hookType)
        {
            m_currentScriptState = hookType;
        }
        public void _FinishScriptCall()
        {
            m_currentScriptState = SpellScriptHookType.None;
        }
        public bool IsInCheckCastHook()
        {
            return m_currentScriptState == SpellScriptHookType.CheckCast;
        }
        public bool IsInTargetHook()
        {
            switch (m_currentScriptState)
            {
                case SpellScriptHookType.LaunchTarget:
                case SpellScriptHookType.EffectHitTarget:
                case SpellScriptHookType.EffectSuccessfulDispel:
                case SpellScriptHookType.BeforeHit:
                case SpellScriptHookType.OnHit:
                case SpellScriptHookType.AfterHit:
                    return true;
            }
            return false;
        }
        public bool IsInHitPhase()
        {
            return (m_currentScriptState >= SpellScriptHookType.EffectHit && m_currentScriptState < SpellScriptHookType.AfterHit + 1);
        }
        public bool IsInEffectHook()
        {
            return (m_currentScriptState >= SpellScriptHookType.Launch && m_currentScriptState <= SpellScriptHookType.EffectHitTarget)
                || m_currentScriptState == SpellScriptHookType.EffectSuccessfulDispel;
        }

        Spell m_spell;
        uint m_hitPreventEffectMask;
        uint m_hitPreventDefaultEffectMask;

        // SpellScript interface
        // hooks to which you can attach your functions
        public List<CastHandler> BeforeCast = new List<CastHandler>();
        public List<CastHandler> OnCast = new List<CastHandler>();
        public List<CastHandler> AfterCast = new List<CastHandler>();

        // where function is SpellCastResult function()
        public List<CheckCastHandler> OnCheckCast = new List<CheckCastHandler>();

        // where function is void function(uint effIndex)
        public List<EffectHandler> OnEffectLaunch = new List<EffectHandler>();
        public List<EffectHandler> OnEffectLaunchTarget = new List<EffectHandler>();
        public List<EffectHandler> OnEffectHit = new List<EffectHandler>();
        public List<EffectHandler> OnEffectHitTarget = new List<EffectHandler>();
        public List<EffectHandler> OnEffectSuccessfulDispel = new List<EffectHandler>();

        public List<BeforeHitHandler> BeforeHit = new List<BeforeHitHandler>();
        public List<HitHandler> OnHit = new List<HitHandler>();
        public List<HitHandler> AfterHit = new List<HitHandler>();

        // where function is void function(List<WorldObject> targets)
        public List<ObjectAreaTargetSelectHandler> OnObjectAreaTargetSelect = new List<ObjectAreaTargetSelectHandler>();

        // where function is void function(ref WorldObject target)
        public List<ObjectTargetSelectHandler> OnObjectTargetSelect = new List<ObjectTargetSelectHandler>();

        // where function is void function(SpellDestination target)
        public List<DestinationTargetSelectHandler> OnDestinationTargetSelect = new List<DestinationTargetSelectHandler>();

        // hooks are executed in following order, at specified event of spell:
        // 1. BeforeCast - executed when spell preparation is finished (when cast bar becomes full) before cast is handled
        // 2. OnCheckCast - allows to override result of CheckCast function
        // 3a. OnObjectAreaTargetSelect - executed just before adding selected targets to final target list (for area targets)
        // 3b. OnObjectTargetSelect - executed just before adding selected target to final target list (for single unit targets)
        // 4. OnCast - executed just before spell is launched (creates missile) or executed
        // 5. AfterCast - executed after spell missile is launched and immediate spell actions are done
        // 6. OnEffectLaunch - executed just before specified effect handler call - when spell missile is launched
        // 7. OnEffectLaunchTarget - executed just before specified effect handler call - when spell missile is launched - called for each target from spell target map
        // 8. OnEffectHit - executed just before specified effect handler call - when spell missile hits dest
        // 9. BeforeHit - executed just before spell hits a target - called for each target from spell target map
        // 10. OnEffectHitTarget - executed just before specified effect handler call - called for each target from spell target map
        // 11. OnHit - executed just before spell deals damage and procs auras - when spell hits target - called for each target from spell target map
        // 12. AfterHit - executed just after spell finishes all it's jobs for target - called for each target from spell target map

        //
        // methods allowing interaction with Spell object
        //
        // methods useable during all spell handling phases
        public Unit GetCaster() { return m_spell.GetCaster(); }
        public Unit GetOriginalCaster() { return m_spell.GetOriginalCaster(); }
        public SpellInfo GetSpellInfo() { return m_spell.GetSpellInfo(); }
        SpellValue GetSpellValue() { return m_spell.m_spellValue; }

        public SpellEffectInfo GetEffectInfo(uint effIndex)
        {
            return m_spell.GetEffect(effIndex);
        }

        // methods useable after spell is prepared
        // accessors to the explicit targets of the spell
        // explicit target - target selected by caster (player, game client, or script - DoCast(explicitTarget, ...), required for spell to be cast
        // examples:
        // -shadowstep - explicit target is the unit you want to go behind of
        // -chain heal - explicit target is the unit to be healed first
        // -holy nova/arcane explosion - explicit target = null because target you are selecting doesn't affect how spell targets are selected
        // you can determine if spell requires explicit targets by dbc columns:
        // - Targets - mask of explicit target types
        // - ImplicitTargetXX set to TARGET_XXX_TARGET_YYY, _TARGET_ here means that explicit target is used by the effect, so spell needs one too

        // returns: WorldLocation which was selected as a spell destination or null
        public WorldLocation GetExplTargetDest()
        {
            if (m_spell.m_targets.HasDst())
                return m_spell.m_targets.GetDstPos();
            return null;
        }

        public void SetExplTargetDest(WorldLocation loc)
        {
            m_spell.m_targets.SetDst(loc);
        }

        // returns: WorldObject which was selected as an explicit spell target or null if there's no target
        public WorldObject GetExplTargetWorldObject() { return m_spell.m_targets.GetObjectTarget(); }

        // returns: Unit which was selected as an explicit spell target or null if there's no target
        public Unit GetExplTargetUnit() { return m_spell.m_targets.GetUnitTarget(); }

        // returns: GameObject which was selected as an explicit spell target or null if there's no target
        GameObject GetExplTargetGObj() { return m_spell.m_targets.GetGOTarget(); }

        // returns: Item which was selected as an explicit spell target or null if there's no target
        Item GetExplTargetItem() { return m_spell.m_targets.GetItemTarget(); }

        // methods useable only during spell hit on target, or during spell launch on target:
        // returns: target of current effect if it was Unit otherwise null
        public Unit GetHitUnit()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitUnit was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return null;
            }
            return m_spell.unitTarget;
        }
        // returns: target of current effect if it was Creature otherwise null
        public Creature GetHitCreature()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitCreature was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return null;
            }
            if (m_spell.unitTarget != null)
                return m_spell.unitTarget.ToCreature();
            else
                return null;
        }
        // returns: target of current effect if it was Player otherwise null
        public Player GetHitPlayer()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitPlayer was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return null;
            }
            if (m_spell.unitTarget != null)
                return m_spell.unitTarget.ToPlayer();
            else
                return null;
        }
        // returns: target of current effect if it was Item otherwise null
        Item GetHitItem()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitItem was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return null;
            }
            return m_spell.itemTarget;
        }
        // returns: target of current effect if it was GameObject otherwise null
        public GameObject GetHitGObj()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitGObj was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return null;
            }
            return m_spell.gameObjTarget;
        }
        // returns: destination of current effect
        public WorldLocation GetHitDest()
        {
            if (!IsInEffectHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitDest was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return null;
            }
            return m_spell.destTarget;
        }
        // setter/getter for for damage done by spell to target of spell hit
        // returns damage calculated before hit, and real dmg done after hit
        public int GetHitDamage()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitDamage was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return 0;
            }
            return m_spell.m_damage;
        }
        public void SetHitDamage(int damage)
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.SetHitDamage was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return;
            }
            m_spell.m_damage = damage;
        }
        void PreventHitDamage() { SetHitDamage(0); }
        // setter/getter for for heal done by spell to target of spell hit
        // returns healing calculated before hit, and real dmg done after hit
        public int GetHitHeal()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitHeal was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return 0;
            }
            return m_spell.m_healing;
        }
        public void SetHitHeal(int heal)
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.SetHitHeal was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return;
            }
            m_spell.m_healing = heal;
        }
        void PreventHitHeal() { SetHitHeal(0); }
        public Spell GetSpell() { return m_spell; }
        // returns current spell hit target aura
        public Aura GetHitAura()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.GetHitAura was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return null;
            }
            if (m_spell.m_spellAura == null)
                return null;
            if (m_spell.m_spellAura.IsRemoved())
                return null;
            return m_spell.m_spellAura;
        }
        // prevents applying aura on current spell hit target
        void PreventHitAura()
        {
            if (!IsInTargetHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.PreventHitAura was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return;
            }
            if (m_spell.m_spellAura != null)
                m_spell.m_spellAura.Remove();
        }

        // prevents effect execution on current spell hit target
        // including other effect/hit scripts
        // will not work on aura/damage/heal
        // will not work if effects were already handled
        public void PreventHitEffect(uint effIndex)
        {
            if (!IsInHitPhase() && !IsInEffectHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.PreventHitEffect was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return;
            }
            m_hitPreventEffectMask |= (1u << (int)effIndex);
            PreventHitDefaultEffect(effIndex);
        }

        // prevents default effect execution on current spell hit target
        // will not work on aura/damage/heal effects
        // will not work if effects were already handled
        public void PreventHitDefaultEffect(uint effIndex)
        {
            if (!IsInHitPhase() && !IsInEffectHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.PreventHitDefaultEffect was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return;
            }
            m_hitPreventDefaultEffectMask |= (1u << (int)effIndex);
        }

        public SpellEffectInfo GetEffectInfo()
        {
            Contract.Assert(IsInEffectHook(), string.Format("Script: `{0}` Spell: `{1}`: function SpellScript::GetEffectInfo was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId));

            return m_spell.effectInfo;
        }

        // method avalible only in EffectHandler method
        public int GetEffectValue()
        {
            if (!IsInEffectHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.PreventHitDefaultEffect was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return 0;
            }
            return m_spell.damage;
        }

        public void SetEffectValue(int value)
        {
            if (!IsInEffectHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.SetEffectValue was called, but function has no effect in current hook!", m_scriptName, m_scriptSpellId);
                return;
            }

            m_spell.damage = value;
        }

        // returns: cast item if present.
        public Item GetCastItem() { return m_spell.m_CastItem; }

        // Creates item. Calls Spell.DoCreateItem method.
        public void CreateItem(uint effIndex, uint itemId) { m_spell.DoCreateItem(effIndex, itemId); }

        // Returns SpellInfo from the spell that triggered the current one
        public SpellInfo GetTriggeringSpell() { return m_spell.m_triggeredByAuraSpell; }

        // finishes spellcast prematurely with selected error message
        public void FinishCast(SpellCastResult result)
        {
            m_spell.SendCastResult(result);
            m_spell.finish(result == SpellCastResult.SpellCastOk);
        }

        public void SetCustomCastResultMessage(SpellCustomErrors result)
        {
            if (!IsInCheckCastHook())
            {
                Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}`: function SpellScript.SetCustomCastResultMessage was called while spell not in check cast phase!", m_scriptName, m_scriptSpellId);
                return;
            }

            m_spell.m_customError = result;
        }
    }

    public class AuraScript : BaseSpellScript
    {
        // internal use classes & functions
        // DO NOT OVERRIDE THESE IN SCRIPTS
        public delegate bool AuraCheckAreaTargetDelegate(Unit target);
        public delegate void AuraDispelDelegate(DispelInfo dispelInfo);
        public delegate void AuraEffectApplicationModeDelegate(AuraEffect aura, AuraEffectHandleModes auraMode);
        public delegate void AuraEffectPeriodicDelegate(AuraEffect aura);
        public delegate void AuraEffectUpdatePeriodicDelegate(AuraEffect aura);
        public delegate void AuraEffectCalcAmountDelegate(AuraEffect aura, ref int amount, ref bool canBeRecalculated);
        public delegate void AuraEffectCalcPeriodicDelegate(AuraEffect aura, bool isPeriodic, int amplitude);
        public delegate void AuraEffectCalcSpellModDelegate(AuraEffect aura, ref SpellModifier spellMod);
        public delegate void AuraEffectAbsorbDelegate(AuraEffect aura, DamageInfo damageInfo, ref uint absorbAmount);
        public delegate void AuraEffectSplitDelegate(AuraEffect aura, DamageInfo damageInfo, uint splitAmount);
        public delegate bool AuraCheckProcDelegate(ProcEventInfo info);
        public delegate void AuraProcDelegate(ProcEventInfo info);
        public delegate void AuraEffectProcDelegate(AuraEffect aura, ProcEventInfo info);

        public class CheckAreaTargetHandler
        {
            public CheckAreaTargetHandler(AuraCheckAreaTargetDelegate _pHandlerScript) { pHandlerScript = _pHandlerScript; }
            public bool Call(Unit target)
            {
                return pHandlerScript(target);
            }

            AuraCheckAreaTargetDelegate pHandlerScript;
        }
        public class AuraDispelHandler
        {
            public AuraDispelHandler(AuraDispelDelegate _pHandlerScript) { pHandlerScript = _pHandlerScript; }
            public void Call(DispelInfo dispelInfo)
            {
                pHandlerScript(dispelInfo);
            }

            AuraDispelDelegate pHandlerScript;
        }
        public class EffectBase : EffectHook
        {
            public EffectBase(uint _effIndex, AuraType _effName)
                : base(_effIndex)
            {
                effAurName = _effName;
            }

            public override bool CheckEffect(SpellInfo spellEntry, uint effIndex)
            {
                SpellEffectInfo effect = spellEntry.GetEffect(effIndex);
                if (effect == null)
                    return false;

                if (effect.ApplyAuraName == 0 && effAurName == 0)
                    return true;
                if (effect.ApplyAuraName == 0)
                    return false;
                return (effAurName == AuraType.Any) || (effect.ApplyAuraName == effAurName);
            }

            AuraType effAurName;
        }

        public class EffectPeriodicHandler : EffectBase
        {
            public EffectPeriodicHandler(AuraEffectPeriodicDelegate _pEffectHandlerScript, byte _effIndex, AuraType _effName)
                : base(_effIndex, _effName)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }
            public void Call(AuraEffect _aurEff)
            {
                pEffectHandlerScript(_aurEff);
            }
            AuraEffectPeriodicDelegate pEffectHandlerScript;
        }
        public class EffectUpdatePeriodicHandler : EffectBase
        {
            public EffectUpdatePeriodicHandler(AuraEffectUpdatePeriodicDelegate _pEffectHandlerScript, byte _effIndex, AuraType _effName)
                : base(_effIndex, _effName)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }
            public void Call(AuraEffect aurEff) { pEffectHandlerScript(aurEff); }

            AuraEffectUpdatePeriodicDelegate pEffectHandlerScript;
        }
        public class EffectCalcAmountHandler : EffectBase
        {
            public EffectCalcAmountHandler(AuraEffectCalcAmountDelegate _pEffectHandlerScript, uint _effIndex, AuraType _effName)
                : base(_effIndex, _effName)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }
            public void Call(AuraEffect aurEff, ref int amount, ref bool canBeRecalculated)
            {
                pEffectHandlerScript(aurEff, ref amount, ref canBeRecalculated);
            }

            public AuraEffectCalcAmountDelegate pEffectHandlerScript;
        }
        public class EffectCalcPeriodicHandler : EffectBase
        {
            public EffectCalcPeriodicHandler(AuraEffectCalcPeriodicDelegate _pEffectHandlerScript, byte _effIndex, AuraType _effName)
                : base(_effIndex, _effName)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }
            public void Call(AuraEffect aurEff, ref bool isPeriodic, ref int periodicTimer)
            {
                pEffectHandlerScript(aurEff, isPeriodic, periodicTimer);
            }

            AuraEffectCalcPeriodicDelegate pEffectHandlerScript;
        }
        public class EffectCalcSpellModHandler : EffectBase
        {
            public EffectCalcSpellModHandler(AuraEffectCalcSpellModDelegate _pEffectHandlerScript, byte _effIndex, AuraType _effName)
                : base(_effIndex, _effName)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }
            public void Call(AuraEffect aurEff, ref SpellModifier spellMod)
            {
                pEffectHandlerScript(aurEff, ref spellMod);
            }

            AuraEffectCalcSpellModDelegate pEffectHandlerScript;
        }
        public class EffectApplyHandler : EffectBase
        {
            public EffectApplyHandler(AuraEffectApplicationModeDelegate _pEffectHandlerScript, byte _effIndex, AuraType _effName, AuraEffectHandleModes _mode)
                : base(_effIndex, _effName)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
                mode = _mode;
            }
            public void Call(AuraEffect _aurEff, AuraEffectHandleModes _mode)
            {
                if (Convert.ToBoolean(_mode & mode))
                    pEffectHandlerScript(_aurEff, _mode);
            }

            AuraEffectApplicationModeDelegate pEffectHandlerScript;
            AuraEffectHandleModes mode;
        }
        public class EffectAbsorbHandler : EffectBase
        {
            public EffectAbsorbHandler(AuraEffectAbsorbDelegate _pEffectHandlerScript, byte _effIndex)
                : base(_effIndex, AuraType.SchoolAbsorb)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }

            public void Call(AuraEffect aurEff, DamageInfo dmgInfo, ref uint absorbAmount)
            {
                pEffectHandlerScript(aurEff, dmgInfo, ref absorbAmount);
            }

            AuraEffectAbsorbDelegate pEffectHandlerScript;
        }
        public class EffectManaShieldHandler : EffectBase
        {
            public EffectManaShieldHandler(AuraEffectAbsorbDelegate _pEffectHandlerScript, byte _effIndex)
                : base(_effIndex, AuraType.ManaShield)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }
            public void Call(AuraEffect aurEff, DamageInfo dmgInfo, ref uint absorbAmount)
            {
                pEffectHandlerScript(aurEff, dmgInfo, ref absorbAmount);
            }

            AuraEffectAbsorbDelegate pEffectHandlerScript;
        }
        public class EffectSplitHandler : EffectBase
        {
            public EffectSplitHandler(AuraEffectSplitDelegate _pEffectHandlerScript, byte _effIndex)
                : base(_effIndex, AuraType.SplitDamagePct)
            {
                pEffectHandlerScript = _pEffectHandlerScript;
            }
            public void Call(AuraEffect aurEff, DamageInfo dmgInfo, uint splitAmount)
            {
                pEffectHandlerScript(aurEff, dmgInfo, splitAmount);
            }

            AuraEffectSplitDelegate pEffectHandlerScript;
        }
        public class CheckProcHandler
        {
            public CheckProcHandler(AuraCheckProcDelegate handlerScript)
            {
                _HandlerScript = handlerScript;
            }
            public bool Call(ProcEventInfo eventInfo)
            {
                return _HandlerScript(eventInfo);
            }

            AuraCheckProcDelegate _HandlerScript;
        }
        public class AuraProcHandler
        {
            public AuraProcHandler(AuraProcDelegate handlerScript)
            {
                _HandlerScript = handlerScript;
            }
            public void Call(ProcEventInfo eventInfo)
            {
                _HandlerScript(eventInfo);
            }

            AuraProcDelegate _HandlerScript;
        }
        public class EffectProcHandler : EffectBase
        {
            public EffectProcHandler(AuraEffectProcDelegate effectHandlerScript, byte effIndex, AuraType effName) : base(effIndex, effName)
            {
                _EffectHandlerScript = effectHandlerScript;
            }
            public void Call(AuraEffect aurEff, ProcEventInfo eventInfo)
            {
                _EffectHandlerScript(aurEff, eventInfo);
            }

            AuraEffectProcDelegate _EffectHandlerScript;
        }

        public AuraScript()
        {
            m_aura = null;
            m_auraApplication = null;
            m_defaultActionPrevented = false;
        }

        public override bool _Validate(SpellInfo entry)
        {
            foreach (var eff in DoCheckAreaTarget)
                if (!entry.HasAreaAuraEffect() && !entry.HasEffect(SpellEffectName.PersistentAreaAura) && !entry.HasEffect(SpellEffectName.ApplyAura))
                    Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry.Id, m_scriptName);

            foreach (var eff in OnDispel)
                if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
                    Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `OnDispel` of AuraScript won't be executed", entry.Id, m_scriptName);

            foreach (var eff in AfterDispel)
                if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
                    Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `AfterDispel` of AuraScript won't be executed", entry.Id, m_scriptName);

            foreach (var eff in OnEffectApply)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectApply` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectRemove)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectRemove` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in AfterEffectApply)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `AfterEffectApply` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in AfterEffectRemove)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `AfterEffectRemove` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectPeriodic)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectPeriodic` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectUpdatePeriodic)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectUpdatePeriodic` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in DoEffectCalcAmount)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `DoEffectCalcAmount` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in DoEffectCalcPeriodic)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `DoEffectCalcPeriodic` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in DoEffectCalcSpellMod)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `DoEffectCalcSpellMod` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectAbsorb)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectAbsorb` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in AfterEffectAbsorb)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `AfterEffectAbsorb` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectManaShield)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectManaShield` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in AfterEffectManaShield)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `AfterEffectManaShield` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in OnEffectSplit)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectSplit` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in DoCheckProc)
                if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
                    Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry.Id, m_scriptName);

            foreach (var eff in DoPrepareProc)
                if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
                    Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry.Id, m_scriptName);

            foreach (var eff in OnProc)
                if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
                    Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `OnProc` of AuraScript won't be executed", entry.Id, m_scriptName);

            foreach (var eff in AfterProc)
                if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
                    Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `AfterProc` of AuraScript won't be executed", entry.Id, m_scriptName);

            foreach (var eff in OnEffectProc)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `OnEffectProc` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            foreach (var eff in AfterEffectProc)
                if (eff.GetAffectedEffectsMask(entry) == 0)
                    Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `AfterEffectProc` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);

            return base._Validate(entry);
        }
        public bool _Load(Aura aura)
        {
            m_aura = aura;
            _PrepareScriptCall(AuraScriptHookType.Loading, null);
            bool load = Load();
            _FinishScriptCall();
            return load;
        }
        public void _PrepareScriptCall(AuraScriptHookType hookType, AuraApplication aurApp = null)
        {
            m_scriptStates.Push(new ScriptStateStore((byte)m_currentScriptState, m_auraApplication, m_defaultActionPrevented));
            m_currentAuraScriptState = hookType;
            m_defaultActionPrevented = false;
            m_auraApplication = aurApp;
        }
        public void _FinishScriptCall()
        {
            ScriptStateStore stateStore = m_scriptStates.Peek();
            m_currentAuraScriptState = (AuraScriptHookType)stateStore._currentScriptState;
            m_auraApplication = stateStore._auraApplication;
            m_defaultActionPrevented = stateStore._defaultActionPrevented;
            m_scriptStates.Pop();
        }
        public bool _IsDefaultActionPrevented()
        {
            switch (m_currentAuraScriptState)
            {
                case AuraScriptHookType.EffectApply:
                case AuraScriptHookType.EffectRemove:
                case AuraScriptHookType.EffectPeriodic:
                case AuraScriptHookType.EffectAbsorb:
                case AuraScriptHookType.EffectSplit:
                case AuraScriptHookType.PrepareProc:
                case AuraScriptHookType.EffectProc:
                    return m_defaultActionPrevented;
                default:
                    Contract.Assert(false, "AuraScript._IsDefaultActionPrevented is called in a wrong place");
                    return false;
            }
        }

        Aura m_aura;
        AuraApplication m_auraApplication;
        bool m_defaultActionPrevented;

        class ScriptStateStore
        {
            public AuraApplication _auraApplication;
            public byte _currentScriptState;
            public bool _defaultActionPrevented;
            public ScriptStateStore(byte currentScriptState, AuraApplication auraApplication, bool defaultActionPrevented)
            {
                _auraApplication = auraApplication;
                _currentScriptState = currentScriptState;
                _defaultActionPrevented = defaultActionPrevented;
            }
        }
        Stack<ScriptStateStore> m_scriptStates = new Stack<ScriptStateStore>();

        // AuraScript interface
        // hooks to which you can attach your functions
        //
        // executed when area aura checks if it can be applied on target
        // example: OnEffectApply += AuraEffectApplyFn(class.function);
        // where function is: bool function (Unit target);
        public List<CheckAreaTargetHandler> DoCheckAreaTarget = new List<CheckAreaTargetHandler>();

        // executed when aura is dispelled by a unit
        // example: OnDispel += AuraDispelFn(class.function);
        // where function is: void function (DispelInfo dispelInfo);
        public List<AuraDispelHandler> OnDispel = new List<AuraDispelHandler>();

        // executed after aura is dispelled by a unit
        // example: AfterDispel += AuraDispelFn(class.function);
        // where function is: void function (DispelInfo dispelInfo);
        public List<AuraDispelHandler> AfterDispel = new List<AuraDispelHandler>();

        // executed when aura effect is applied with specified mode to target
        // should be used when when effect handler preventing/replacing is needed, do not use this hook for triggering spellcasts/removing auras etc - may be unsafe
        // example: OnEffectApply += AuraEffectApplyFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier, AuraEffectHandleModes);
        // where function is: void function (AuraEffect aurEff, AuraEffectHandleModes mode);
        public List<EffectApplyHandler> OnEffectApply = new List<EffectApplyHandler>();

        // executed after aura effect is applied with specified mode to target
        // example: AfterEffectApply += AuraEffectApplyFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier, AuraEffectHandleModes);
        // where function is: void function (AuraEffect aurEff, AuraEffectHandleModes mode);
        public List<EffectApplyHandler> AfterEffectApply = new List<EffectApplyHandler>();

        // executed after aura effect is removed with specified mode from target
        // should be used when when effect handler preventing/replacing is needed, do not use this hook for triggering spellcasts/removing auras etc - may be unsafe
        // example: OnEffectRemove += AuraEffectRemoveFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier, AuraEffectHandleModes);
        // where function is: void function (AuraEffect aurEff, AuraEffectHandleModes mode);
        public List<EffectApplyHandler> OnEffectRemove = new List<EffectApplyHandler>();

        // executed when aura effect is removed with specified mode from target
        // example: AfterEffectRemove += AuraEffectRemoveFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier, AuraEffectHandleModes);
        // where function is: void function (AuraEffect aurEff, AuraEffectHandleModes mode);
        public List<EffectApplyHandler> AfterEffectRemove = new List<EffectApplyHandler>();

        // executed when periodic aura effect ticks on target
        // example: OnEffectPeriodic += AuraEffectPeriodicFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier);
        // where function is: void function (AuraEffect aurEff);
        public List<EffectPeriodicHandler> OnEffectPeriodic = new List<EffectPeriodicHandler>();

        // executed when periodic aura effect is updated
        // example: OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier);
        // where function is: void function (AuraEffect aurEff);
        public List<EffectUpdatePeriodicHandler> OnEffectUpdatePeriodic = new List<EffectUpdatePeriodicHandler>();

        // executed when aura effect calculates amount
        // example: DoEffectCalcAmount += AuraEffectCalcAmounFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier);
        // where function is: void function (AuraEffect aurEff, int& amount, bool& canBeRecalculated);
        public List<EffectCalcAmountHandler> DoEffectCalcAmount = new List<EffectCalcAmountHandler>();

        // executed when aura effect calculates periodic data
        // example: DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier);
        // where function is: void function (AuraEffect aurEff, bool& isPeriodic, int& amplitude);
        public List<EffectCalcPeriodicHandler> DoEffectCalcPeriodic = new List<EffectCalcPeriodicHandler>();

        // executed when aura effect calculates spellmod
        // example: DoEffectCalcSpellMod += AuraEffectCalcSpellModFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier);
        // where function is: void function (AuraEffect aurEff, SpellModifier& spellMod);
        public List<EffectCalcSpellModHandler> DoEffectCalcSpellMod = new List<EffectCalcSpellModHandler>();

        // executed when absorb aura effect is going to reduce damage
        // example: OnEffectAbsorb += AuraEffectAbsorbFn(class.function, EffectIndexSpecifier);
        // where function is: void function (AuraEffect aurEff, DamageInfo& dmgInfo, uint& absorbAmount);
        public List<EffectAbsorbHandler> OnEffectAbsorb = new List<EffectAbsorbHandler>();

        // executed after absorb aura effect reduced damage to target - absorbAmount is real amount absorbed by aura
        // example: AfterEffectAbsorb += AuraEffectAbsorbFn(class.function, EffectIndexSpecifier);
        // where function is: void function (AuraEffect aurEff, DamageInfo& dmgInfo, uint& absorbAmount);
        public List<EffectAbsorbHandler> AfterEffectAbsorb = new List<EffectAbsorbHandler>();

        // executed when mana shield aura effect is going to reduce damage
        // example: OnEffectManaShield += AuraEffectAbsorbFn(class.function, EffectIndexSpecifier);
        // where function is: void function (AuraEffect aurEff, DamageInfo& dmgInfo, uint& absorbAmount);
        public List<EffectManaShieldHandler> OnEffectManaShield = new List<EffectManaShieldHandler>();

        // executed after mana shield aura effect reduced damage to target - absorbAmount is real amount absorbed by aura
        // example: AfterEffectManaShield += AuraEffectAbsorbFn(class.function, EffectIndexSpecifier);
        // where function is: void function (AuraEffect aurEff, DamageInfo& dmgInfo, uint& absorbAmount);
        public List<EffectManaShieldHandler> AfterEffectManaShield = new List<EffectManaShieldHandler>();

        // executed when the caster of some spell with split dmg aura gets damaged through it
        // example: OnEffectSplit += AuraEffectSplitFn(class.function, EffectIndexSpecifier);
        // where function is: void function (AuraEffect aurEff, DamageInfo& dmgInfo, uint& splitAmount);
        public List<EffectSplitHandler> OnEffectSplit = new List<EffectSplitHandler>();

        // executed when aura checks if it can proc
        // example: DoCheckProc += AuraCheckProcFn(class.function);
        // where function is: bool function (ProcEventInfo& eventInfo);
        public List<CheckProcHandler> DoCheckProc = new List<CheckProcHandler>();

        // executed before aura procs (possibility to prevent charge drop/cooldown)
        // example: DoPrepareProc += AuraProcFn(class.function);
        // where function is: void function (ProcEventInfo& eventInfo);
        public List<AuraProcHandler> DoPrepareProc = new List<AuraProcHandler>();

        // executed when aura procs
        // example: OnProc += AuraProcFn(class.function);
        // where function is: void function (ProcEventInfo& eventInfo);
        public List<AuraProcHandler> OnProc = new List<AuraProcHandler>();

        // executed after aura proced
        // example: AfterProc += AuraProcFn(class.function);
        // where function is: void function (ProcEventInfo& eventInfo);
        public List<AuraProcHandler> AfterProc = new List<AuraProcHandler>();

        // executed when aura effect procs
        // example: OnEffectProc += AuraEffectProcFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier);
        // where function is: void function (AuraEffect aurEff, ProcEventInfo& procInfo);
        public List<EffectProcHandler> OnEffectProc = new List<EffectProcHandler>();

        // executed after aura effect proced
        // example: AfterEffectProc += AuraEffectProcFn(class.function, EffectIndexSpecifier, EffectAuraNameSpecifier);
        // where function is: void function (AuraEffect aurEff, ProcEventInfo& procInfo);
        public List<EffectProcHandler> AfterEffectProc = new List<EffectProcHandler>();

        // AuraScript interface - hook/effect execution manipulators

        // prevents default action of a hook from being executed (works only while called in a hook which default action can be prevented)
        public void PreventDefaultAction()
        {
            switch (m_currentAuraScriptState)
            {
                case AuraScriptHookType.EffectApply:
                case AuraScriptHookType.EffectRemove:
                case AuraScriptHookType.EffectPeriodic:
                case AuraScriptHookType.EffectAbsorb:
                case AuraScriptHookType.EffectSplit:
                case AuraScriptHookType.PrepareProc:
                case AuraScriptHookType.EffectProc:
                    m_defaultActionPrevented = true;
                    break;
                default:
                    Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}` AuraScript.PreventDefaultAction called in a hook in which the call won't have effect!", m_scriptName, m_scriptSpellId);
                    break;
            }
        }

        // AuraScript interface - functions which are redirecting to Aura class

        // returns proto of the spell
        public SpellInfo GetSpellInfo() { return m_aura.GetSpellInfo(); }
        // returns spellid of the spell
        public uint GetId() { return m_aura.GetId(); }

        // returns guid of object which casted the aura (m_originalCaster of the Spell class)
        public ObjectGuid GetCasterGUID() { return m_aura.GetCasterGUID(); }
        // returns unit which casted the aura or null if not avalible (caster logged out for example)
        public Unit GetCaster() { return m_aura.GetCaster(); }
        // returns object on which aura was casted, target for non-area auras, area aura source for area auras
        public WorldObject GetOwner() { return m_aura.GetOwner(); }
        // returns owner if it's unit or unit derived object, null otherwise (only for persistent area auras null is returned)
        public Unit GetUnitOwner() { return m_aura.GetUnitOwner(); }
        // returns owner if it's dynobj, null otherwise
        DynamicObject GetDynobjOwner() { return m_aura.GetDynobjOwner(); }

        // removes aura with remove mode (see AuraRemoveMode enum)
        public void Remove(AuraRemoveMode removeMode = 0) { m_aura.Remove(removeMode); }
        // returns aura object of script
        public Aura GetAura() { return m_aura; }

        // returns type of the aura, may be dynobj owned aura or unit owned aura
        AuraObjectType GetAuraType() { return m_aura.GetAuraType(); }

        // aura duration manipulation - when duration goes to 0 aura is removed
        int GetDuration() { return m_aura.GetDuration(); }
        public void SetDuration(int duration, bool withMods = false) { m_aura.SetDuration(duration, withMods); }
        // sets duration to maxduration
        void RefreshDuration() { m_aura.RefreshDuration(); }
        long GetApplyTime() { return m_aura.GetApplyTime(); }
        public int GetMaxDuration() { return m_aura.GetMaxDuration(); }
        void SetMaxDuration(int duration) { m_aura.SetMaxDuration(duration); }
        int CalcMaxDuration() { return m_aura.CalcMaxDuration(); }
        // expired - duration just went to 0
        public bool IsExpired() { return m_aura.IsExpired(); }
        // permament - has infinite duration
        bool IsPermanent() { return m_aura.IsPermanent(); }

        // charges manipulation - 0 - not charged aura
        byte GetCharges() { return m_aura.GetCharges(); }
        void SetCharges(byte charges) { m_aura.SetCharges(charges); }
        byte CalcMaxCharges() { return m_aura.CalcMaxCharges(); }
        bool ModCharges(sbyte num, AuraRemoveMode removeMode = AuraRemoveMode.Default) { return m_aura.ModCharges(num, removeMode); }
        // returns true if last charge dropped
        bool DropCharge(AuraRemoveMode removeMode = AuraRemoveMode.Default) { return m_aura.DropCharge(removeMode); }

        // stack amount manipulation
        public byte GetStackAmount() { return m_aura.GetStackAmount(); }
        void SetStackAmount(byte num) { m_aura.SetStackAmount(num); }
        public bool ModStackAmount(int num, AuraRemoveMode removeMode = AuraRemoveMode.Default) { return m_aura.ModStackAmount(num, removeMode); }

        // passive - "working in background", not saved, not removed by immunities, not seen by player
        bool IsPassive() { return m_aura.IsPassive(); }
        // death persistent - not removed on death
        bool IsDeathPersistent() { return m_aura.IsDeathPersistent(); }

        // check if aura has effect of given effindex
        public bool HasEffect(byte effIndex) { return m_aura.HasEffect(effIndex); }
        // returns aura effect of given effect index or null
        public AuraEffect GetEffect(byte effIndex) { return m_aura.GetEffect(effIndex); }

        // check if aura has effect of given aura type
        bool HasEffectType(AuraType type)
        {
            return m_aura.HasEffectType(type);
        }

        // AuraScript interface - functions which are redirecting to AuraApplication class
        // Do not call these in hooks in which AuraApplication is not avalible, otherwise result will differ from expected (the functions will return null)

        // returns currently processed target of an aura
        // Return value does not need to be null-checked, the only situation this will (always)
        // return null is when the call happens in an unsupported hook, in other cases, it is always valid
        public Unit GetTarget()
        {
            switch (m_currentAuraScriptState)
            {
                case AuraScriptHookType.EffectApply:
                case AuraScriptHookType.EffectRemove:
                case AuraScriptHookType.EffectAfterApply:
                case AuraScriptHookType.EffectAfterRemove:
                case AuraScriptHookType.EffectPeriodic:
                case AuraScriptHookType.EffectAbsorb:
                case AuraScriptHookType.EffectAfterAbsorb:
                case AuraScriptHookType.EffectManaShield:
                case AuraScriptHookType.EffectAfterManaShield:
                case AuraScriptHookType.EffectSplit:
                case AuraScriptHookType.CheckProc:
                case AuraScriptHookType.PrepareProc:
                case AuraScriptHookType.Proc:
                case AuraScriptHookType.AfterProc:
                case AuraScriptHookType.EffectProc:
                case AuraScriptHookType.EffectAfterProc:
                    return m_auraApplication.GetTarget();
                default:
                    Log.outError(LogFilter.Scripts, "Script: `{0}` Spell: `{1}` AuraScript.GetTarget called in a hook in which the call won't have effect!", m_scriptName, m_scriptSpellId);
                    break;
            }

            return null;
        }
        // returns AuraApplication object of currently processed target
        public AuraApplication GetTargetApplication() { return m_auraApplication; }
    }
}
