using Framework.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Framework.StatSystem
{
    /// <summary>
    /// A collection of stat id's and modifiers that can be setup using an appropriate Attributes 
    /// component to apply modifiers to stats for a set period of time.
    /// TODO, Consider expanding to make more complex such as adding conditions that change the way actions work? 
    /// </summary>
    [Serializable]
    public class StatEffect : StatEffectlike
    {
        public bool timerOn;
        private StatModifiers statModifiers; //  Stat - Modifier relationships to apply
        private UnityTimer effectTimer; // The timer used to measure how long to apply the modifiers for
        private Attributes attributes; // The attributes component to apply to

        /// <summary>
        /// Initializes a new instance of the <see cref="StatEffect"/> class.
        /// </summary>
        /// <param name="statModifiers">The stat modifiers.</param>
        public StatEffect(StatModifiers statModifiers)
        {
            this.timerOn = false;
            this.statModifiers = statModifiers;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StatEffect"/> class.
        /// </summary>
        /// <param name="statModifiers">The stat modifiers.</param>
        /// <param name="time">The time.</param>
        public StatEffect(StatModifiers statModifiers, float time) : this(statModifiers)
        {
            this.effectTimer = new UnityTimer(time, false);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StatEffect"/> class.
        /// </summary>
        /// <param name="statId">The stat identifier.</param>
        /// <param name="modifier">The modifier.</param>
        public StatEffect(StatType.Id statId, Modifierlike modifier) : this(new StatModifiers() {{statId, modifier}})
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="StatEffect"/> class.
        /// </summary>
        /// <param name="statId">The stat identifier.</param>
        /// <param name="modifier">The modifier.</param>
        /// <param name="time">The time.</param>
        public StatEffect(StatType.Id statId, Modifierlike modifier, float time)
            : this(new StatModifiers() { { statId, modifier } }, time)
        {
        }

        /// <summary>
        /// The amount of time remaining that this effect has.
        /// </summary>
        /// <returns></returns>
        public float TimeRemaining()
        {
            if (this.effectTimer != null)
            {
                return this.effectTimer.GetEnd() - this.effectTimer.GetCurrent();
            }
            else
            {
                return 0f;
            }
        }

        /// <summary>
        /// Applies this effect to the given Attributes component.
        /// </summary>
        /// <param name="attributes">The attributes.</param>
        public void Apply(Attributes attributes)
        {
            this.attributes = attributes;
            if (this.effectTimer != null)
            {
                this.timerOn = true;
            }
            for (int i = 0; i < this.statModifiers.Count; i++)
            {
                attributes.GetStat(this.statModifiers[i].id).AddModifier(this.statModifiers[i].modifier);
            }
        }

        /// <summary>
        /// Removes this effect from the given Attributes component.
        /// </summary>
        /// <param name="attributes">The attributes.</param>
        public void Remove(Attributes attributes)
        {
            for (int i = 0; i < this.statModifiers.Count; i++)
            {
                attributes.GetStat(this.statModifiers[i].id).RemoveModifier(this.statModifiers[i].modifier);
            }
        }

        /// <summary>
        /// Updates this instance.
        /// </summary>
        public void Update()
        {
            if (this.effectTimer != null && this.timerOn)
            {
                this.effectTimer.Advance();

                if (this.effectTimer.IsComplete())
                {
                    this.timerOn = false;
                    Remove();
                    this.effectTimer.Reset();
                }
            }
        }

        /// <summary>
        /// Removes this effect from a stored Attributes component.
        /// </summary>
        private void Remove()
        {
            for (int i = 0; i < this.statModifiers.Count; i++)
            {
                this.attributes.GetStat(this.statModifiers[i].id).RemoveModifier(this.statModifiers[i].modifier);
            }
        }
    }
}
