﻿using Impact.Interactions;
using Impact.Objects;
using UnityEngine;
using Impact.Utility.ObjectPool;

namespace Impact.Integration.Wwise
{
    /// <summary>
    /// The result of a Wwise audio interaction.
    /// Handles both single collision sounds and sliding and rolling sounds.
    /// </summary>
    public class WwiseAudioInteractionResult : IContinuousInteractionResult, IPoolable
    {
        /// <summary>
        /// Invoked when a decal interaction result is processed.
        /// </summary>
        public static event System.Action<WwiseAudioInteractionResult> OnInteractionProcessed;

        /// <summary>
        /// The original interaction data this result was created from.
        /// </summary>
        public InteractionData OriginalData { get; set; }

        /// <summary>
        /// The result key for updating sliding and rolling.
        /// </summary>
        public long Key { get; set; }

        /// <summary>
        /// The Wwise event this result uses.
        /// </summary>
        public AK.Wwise.Event Event { get; set; }

        /// <summary>
        /// The overall intensity of the interaction.
        /// </summary>
        public float Intensity { get; set; }

        /// <summary>
        /// Is the intensity non-zero and is the Wise event valid?
        /// </summary>
        public bool IsValid
        {
            get { return Intensity > 0 && Event != null && Event.IsValid(); }
        }

        /// <summary>
        /// Is the audio still playing?
        /// </summary>
        public bool IsAlive
        {
            get { return currentIntensity > 0 || targetIntensity > 0; }
        }

        /// <summary>
        /// The Wwise Playing ID associated with this interaction.
        /// </summary>
        public uint PlayingID { get; private set; }

        /// <summary>
        /// The emitter object associated with this interaction.
        /// </summary>
        public WwiseEmitterPooledObject WwiseEmitterObject { get; private set; }

        private IImpactObject parent;

        private float targetIntensity;
        private float currentIntensity;

        private bool isAvailable = true;

        /// <summary>
        /// Play audio using our data.
        /// </summary>
        /// <param name="parent">The Impact Object that created this result.</param>
        public void Process(IImpactObject parent)
        {
            this.parent = parent;

            WwiseEmitterObject = WwiseEmitterPool.GetEmitterObject();

            if (WwiseEmitterObject != null)
            {
                currentIntensity = targetIntensity = Intensity;

                WwiseEmitterObject.transform.position = OriginalData.Point;
                PlayingID = Event.Post(WwiseEmitterObject.gameObject);

                AkSoundEngine.SetRTPCValueByPlayingID(ImpactWwiseRTPCs.Intensity, currentIntensity, PlayingID);
                AkSoundEngine.SetRTPCValueByPlayingID(ImpactWwiseRTPCs.Velocity, OriginalData.Velocity.magnitude, PlayingID);
                AkSoundEngine.SetRTPCValueByPlayingID(ImpactWwiseRTPCs.CompositionValue, OriginalData.CompositionValue, PlayingID);
                AkSoundEngine.SetRTPCValueByPlayingID(ImpactWwiseRTPCs.InteractionType, OriginalData.InteractionType, PlayingID);

                OnInteractionProcessed?.Invoke(this);

                //Dispose immediately for Collision interaction types
                if (OriginalData.InteractionType == InteractionData.InteractionTypeCollision)
                    Dispose();
            }
            else
            {
                Dispose();
            }
        }

        /// <summary>
        /// Update the parameters for sliding and rolling and update position.
        /// </summary>
        public void FixedUpdate()
        {
            currentIntensity = Mathf.MoveTowards(currentIntensity, targetIntensity, 0.1f);

            AkSoundEngine.SetRTPCValueByPlayingID(ImpactWwiseRTPCs.Intensity, currentIntensity, PlayingID);

            targetIntensity = 0;
        }

        /// <summary>
        /// Updates this result to adjust the parameters.
        /// </summary>
        /// <param name="newResult">The new interaction result with updated data.</param>
        public void KeepAlive(IInteractionResult newResult)
        {
            WwiseAudioInteractionResult r = newResult as WwiseAudioInteractionResult;

            WwiseEmitterObject.transform.position = r.OriginalData.Point;
            AkSoundEngine.SetRTPCValueByPlayingID(ImpactWwiseRTPCs.Velocity, r.OriginalData.Velocity.magnitude, PlayingID);
            AkSoundEngine.SetRTPCValueByPlayingID(ImpactWwiseRTPCs.CompositionValue, r.OriginalData.CompositionValue, PlayingID);

            targetIntensity = r.Intensity;
        }

        /// <summary>
        /// Clear data and stop playing.
        /// </summary>
        public void Dispose()
        {
            parent = null;
            Event = null;
            Intensity = 0;

            if (OriginalData.InteractionType != InteractionData.InteractionTypeCollision)
                AkSoundEngine.StopPlayingID(PlayingID);

            PlayingID = AkSoundEngine.AK_INVALID_PLAYING_ID;

            if (WwiseEmitterObject != null)
                WwiseEmitterObject.MakeAvailable();

            MakeAvailable();
        }

        public void Retrieve()
        {
            isAvailable = false;
        }

        public void MakeAvailable()
        {
            isAvailable = true;
        }

        public bool IsAvailable()
        {
            return isAvailable;
        }
    }
}