﻿using UnityEngine;

namespace BattleRoyale.Spells
{
    [CreateAssetMenu(fileName = "Data", menuName = "BattleRoyale/Spell Stats", order = 1)]
    [System.Serializable]
    public class SpellStats : ScriptableObject
    {
        [Header("Special Behaviors")]
        #region
        [Tooltip("Does the spell become a child of the Wand that fired it upon being instantiated?")]
        public bool SpellIsChild;
        [Tooltip("Controls how the spell behaves while the wand's fire input is held.")]
        public HoldFireBehavior HoldFireBehavior;
        [Tooltip("When checked, caches all active spell instances when the player releases the fire button.")]
        public bool CacheSpellsOnRelease;
        [Tooltip("When checked, causes the wand to deactivate when the user's mana reaches zero.")]
        public bool ReleaseOnManaEmpty;
        [Tooltip("When checked, causes the wand to cost mana while the fire input is held rather than on cast.")]
        public bool DrainByHolding;
        [Tooltip("Can the player who cast the spell be affected by the spell?")]
        public bool CanHitOwner;
        [Tooltip("When checked, fires a seal containing the spell instead of the spell itself.")]
        public bool CreatesSeal;
        [Tooltip("When checked, a single spell instance can deal damage to a target repeatedly as long as they remain in contact with the spell")]
        public bool CanHitRepeatedly;
        #endregion

        [Header("Windup Delay")]
        #region
        [Tooltip("Controls the windup time from the initial fire input to the first spell being cast.")]
        public AnimationCurve Graph_WindupDelay;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_WindupDelay;
        #endregion

        [Header("Recast Delay")]
        #region
        [Tooltip("Controls the delay after firing a spell that must elapse before a new spell can be instantiated.")]
        public AnimationCurve Graph_RecastDelay;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_RecastDelay;
        #endregion

        [Header("Mana Cost")]
        #region
        [Tooltip("Controls the cost of mana for consecutive/channeled spell casts. Scales with Rarity by default. Use a flat value graph if no scaling is desired.")]
        public AnimationCurve Graph_ManaCost;
        #endregion

        [Header("Charging Behavior")]
        #region
        [Tooltip("How a spell charges while holding the left mouse button.")]
        public ChargingBehavior ChargingBehavior;
        [Tooltip("The maximum value of charge that can be accrued in a given charge. If the charging behavior does not support charging or fires at the minimum cost, this is disregarded.")]
        [Range(0, 100)] public float MaxChargeValue;
        #endregion

        [Header("Charge Rate")]
        #region
        [Tooltip("Controls the rate at which a wand will gain charge.")]
        public AnimationCurve Graph_ChargeRate;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_ChargeRate;
        #endregion

        [Header("Multicast")]
        #region
        [Tooltip("Used to determine number of projectiles, bursts, barriers, etc. that the spell will generate upon casting.")]
        [Range(1, 10)] public int SpellInstancesPerCast = 1;
        
        [Tooltip("Determines how many seconds pass between multicast instances.")]
        public AnimationCurve Graph_MultiCastDelay;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_MultiCastDelay;
        #endregion

        [Header("Spell Instance Limit")]
        #region
        [Tooltip("The number of spell prefabs that can be active from one wand at a time. 0 means no limit.")]
        [Range(1, 100)] public int SpellInstanceLimit = 1;

        [Tooltip("How a spell handles hitting the instance limit of its spells.")]
        public SpellOverLimitBehavior OverLimitBehavior;
        #endregion
        
        [Header("Size")]
        #region
        [Tooltip("The scale factor of a prefab at cast.")]
        public AnimationCurve Graph_BaseSize;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_BaseSize;
        [Tooltip("The percentage of size applied over its lifetime")]
        public AnimationCurve Graph_SizeOverTime;
        #endregion

        [Header("Speed")]
        #region
        public AnimationCurve Graph_BaseSpeed;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_BaseSpeed;
        [Tooltip("The percentage of speed applied over its lifetime")]
        public AnimationCurve Graph_SpeedOverTime;
        #endregion

        [Header("Gravity")]
        #region
        [Tooltip("The strength of gravity on the spell. 0 means no gravity.")]
        [Range(0, 10)] public float GravityFactor;
        #endregion

        [Header("Accuracy")]
        #region
        [Tooltip("The maximum variation in degrees that a spell will angle itself upon being cast")]
        public AnimationCurve Graph_Accuracy;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_Accuracy;
        #endregion

        [Header("Range")]
        #region
        [Tooltip("Is the range of the spell limited?")]
        public bool LimitRange;
        [Tooltip("Is the spell destroyed when it reaches its range? Otherwise, it will simply stop in place.")]
        public bool CacheAtRange;
        [Tooltip("The range of the spell upon being cast.")]
        public AnimationCurve Graph_Range;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_Range;
        #endregion

        [Header("Lifespan")]
        #region
        [Tooltip("Is the lifetime of the spell limited?")]
        public bool LimitLifespan;
        [Tooltip("Is the spell destroyed when it reaches its lifetime? Otherwise, it will simply stop in place.")]
        public bool CacheAtLifetime;
        [Tooltip("The lifetime of the spell upon being cast.")]
        public AnimationCurve Graph_Lifetime;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_Lifetime;
        #endregion

        [Header("Damage")]
        #region
        [Tooltip("Controls how much damage a spell applies to a player when hit by a spell.")]
        public AnimationCurve Graph_BaseDamage;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_BaseDamage;
        [Tooltip("The percentage of damage applied over its lifetime")]
        public AnimationCurve Graph_DamageOverTime;
        #endregion

        [Header("Pushing Force")]
        #region
        [Tooltip("Controls the strength of the pushing force a target receives when coming into contact with this spell.")]
        public AnimationCurve Graph_BasePushForce;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_PushForce;
        [Tooltip("The percentage of push force applied over its lifetime")]
        public AnimationCurve Graph_PushForceOverTime;
        [Tooltip("The direction that force is applied in")]
        public SpellForceDirection ForceDirection;
        #endregion
        
        [Header("Hit Behaviors")]
        #region
        [Tooltip("Is the spell destroyed by contact with terrain?")]
        public bool DestroyedByTerrain;
        [Tooltip("Is the spell able to move along the terrain? This setting disables Targetting due to navigation conflicts.")]
        public bool TravelOverTerrain;
        [Tooltip("Player Hit Behaviors")]
        public HitBehavior PlayerBehavior;
        [Tooltip("Barrier Hit Behaviors")]
        public HitBehavior BarrierBehavior;
        [Tooltip("Wall Hit Behaviors")]
        public HitBehavior WallBehavior;
        #endregion
        
        [Header("Targetting")]
        #region
        [Tooltip("Controls how targets are acquired.")]
        public SpellTargettingBehavior TargettingBehavior;
        [Tooltip("Controls the angle from the wand's direction that targets can be acquired.")]
        public AnimationCurve Graph_TargettingAngle;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_TargettingAngle;
        [Tooltip("Controls the range from the player at which targets can be acquired.")]
        public AnimationCurve Graph_TargettingRange;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_TargettingRange;
        #endregion

        [Header("Target Tracking")]
        #region
        [Tooltip("Controls how fast a spell will rotate towards the target (degrees per second)")]
        public AnimationCurve Graph_BaseTrackingRate;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_TrackingRate;
        [Tooltip("The percentage of tracking speed applied over the spell's lifetime.")]
        public AnimationCurve Graph_TrackingRateOverTime;
        #endregion

        public override string ToString()
        {
            return "SpellStat";
        }
    }
    
    [System.Serializable]
    public struct HitBehavior
    {
        [Tooltip("Whether the spell reacts to collisions with this type of object")]
        public bool CanHit;
        [Tooltip("The behavior of the spell when it detects collision")]
        public SpellCollisionBehavior CollisionBehavior;
        [Tooltip("Does the spell deal damage to this type of object?")]
        public bool Damages;
        [Tooltip("Does the spell deal bonus damage to this object?")]
        public bool DealsBonusDamage;
        [Tooltip("The amount of bonus damage dealt.")]
        public AnimationCurve Graph_BonusDamage;
        [Tooltip("What stat we are scaling by.")]
        public StatScalingParam SP_BonusDamage;

        public static bool operator ==(HitBehavior h1, HitBehavior h2)
        {
            if (h1.CanHit != h2.CanHit)
                return false;
            
            if (h1.CollisionBehavior != h2.CollisionBehavior)
                return false;

            if (h1.Damages != h2.Damages)
                return false;

            if (h1.DealsBonusDamage != h2.DealsBonusDamage)
                return false;

            if (h1.Graph_BonusDamage != h2.Graph_BonusDamage)
                return false;
            
            if (h1.SP_BonusDamage != h2.SP_BonusDamage)
                return false;
            
            return true;
        }

        public static bool operator !=(HitBehavior h1, HitBehavior h2)
        {
            return !(h1 == h2);
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        private bool Equals(HitBehavior _hitBehavior)
        {
            return this == _hitBehavior;
        }

        public override int GetHashCode()
        {
            return CanHit.GetHashCode() + CollisionBehavior.GetHashCode() + Damages.GetHashCode() + DealsBonusDamage.GetHashCode() + Graph_BonusDamage.GetHashCode() + SP_BonusDamage.GetHashCode();
        }
    }
}
