﻿using DarkTonic.MasterAudio;
using Impact.Interactions;
using Impact.Utility;
using System.Collections.Generic;
using UnityEngine;

namespace Impact.Integration.MasterAudio

{    /// <summary>
     /// Interaction for playing audio with FMOD.
     /// </summary>
    [CreateAssetMenu(fileName = "New MasterAudio Interaction", menuName = "Impact/MasterAudio/MasterAudio Interaction", order = 0)]
    public class MasterAudioInteraction : ImpactInteractionBase
    {
        private const string interactionResultPoolKey = "MasterAudioInteractionResult";

        public enum CollisionSoundGroupSelectionMode
        {
            Velocity,
            Random
        }

        [System.Serializable]
        public struct CollisionSoundGroup
        {
            [SoundGroup]
            public string SoundGroup;
        }

        [SerializeField]
        private Range _velocityRange = new Range(2, 9);
        [SerializeField]
        private Range _randomPitchRange = new Range(0.9f, 1.1f);

        [SerializeField]
        private bool _scaleVolumeWithVelocity = true;
        [SerializeField]
        private AnimationCurve _velocityVolumeScaleCurve = AnimationCurve.Linear(0, 0, 1, 1);

        [SerializeField]
        private float _collisionNormalInfluence = 1;
        [SerializeField]
        private float _slideVelocityPitchMultiplier = 0.025f;

        [SerializeField]
        private CollisionSoundGroupSelectionMode _collisionSoundGroupSelectionMode = CollisionSoundGroupSelectionMode.Random;
        [SerializeField]
        private List<CollisionSoundGroup> _collisionSoundGroups = new List<CollisionSoundGroup>();

        [SerializeField]
        [SoundGroup]
        private string _slideSoundGroup;
        [SerializeField]
        [SoundGroup]
        private string _rollSoundGroup;

        /// <summary>
        /// The range input velocities will be compared to when calculating the collision intensity.
        /// </summary>
        public Range VelocityRange
        {
            get { return _velocityRange; }
            set { _velocityRange = value; }
        }

        /// <summary>
        /// Random range for the pitch.
        /// </summary>
        public Range RandomPitchRange
        {
            get { return _randomPitchRange; }
            set { _randomPitchRange = value; }
        }

        /// <summary>
        /// Should the output volume be scaled relative to the velocity?
        /// </summary>
        public bool ScaleVolumeWithVelocity
        {
            get { return _scaleVolumeWithVelocity; }
            set { _scaleVolumeWithVelocity = value; }
        }

        /// <summary>
        /// The curve used to get the scalar value for the volume if ScaleVolumeWithVelocity is true.
        /// </summary>
        public AnimationCurve VelocityVolumeScaleCurve
        {
            get { return _velocityVolumeScaleCurve; }
            set { _velocityVolumeScaleCurve = value; }
        }

        /// <summary>
        /// How much the collision normal affects the collision intensity.
        /// </summary>
        public float CollisionNormalInfluence
        {
            get { return _collisionNormalInfluence; }
            set { _collisionNormalInfluence = value; }
        }

        /// <summary>
        /// A value to increase the sliding and rolling pitch as the velocity increases.
        /// </summary>
        public float SlideVelocityPitchMultiplier
        {
            get { return _slideVelocityPitchMultiplier; }
            set { _slideVelocityPitchMultiplier = value; }
        }

        /// <summary>
        /// How collision audio clips should be selected.
        /// </summary>
        public CollisionSoundGroupSelectionMode CollisionSoundGroupMode
        {
            get { return _collisionSoundGroupSelectionMode; }
            set { _collisionSoundGroupSelectionMode = value; }
        }

        /// <summary>
        /// The list of collision sound groups.
        /// </summary>
        public List<CollisionSoundGroup> CollisionSoundGroups
        {
            get { return _collisionSoundGroups; }
        }

        /// <summary>
        /// The name of the sound group to play when sliding.
        /// </summary>
        public string SlideSoundGroup
        {
            get { return _slideSoundGroup; }
            set { _slideSoundGroup = value; }
        }

        /// <summary>
        /// The name of the sound group to play when rolling.
        /// </summary>
        public string RollSoundGroup
        {
            get { return _rollSoundGroup; }
            set { _rollSoundGroup = value; }
        }

        public override IInteractionResult GetInteractionResult<T>(T interactionData)
        {
            //Immediately break out if intensity is less than the velocity range minimum, since any result would be invalid anyways.
            float intensity = ImpactInteractionUtilities.GetCollisionIntensity(interactionData, CollisionNormalInfluence);
            if (intensity < VelocityRange.Min)
                return null;

            long key = 0;
            if (!ImpactInteractionUtilities.GetKeyAndValidate(interactionData, this, out key))
                return null;

            MasterAudioInteractionResult result;

            if (ImpactManagerInstance.TryGetInteractionResultFromPool(interactionResultPoolKey, out result))
            {
                result.OriginalData = InteractionDataUtilities.ToInteractionData(interactionData);
                result.Interaction = this;

                float normalizedIntensity = VelocityRange.Normalize(intensity);
                result.VolumeScale = getVolumeScale(normalizedIntensity) * interactionData.CompositionValue;
                result.SoundGroup = getSoundGroup(interactionData.InteractionType, normalizedIntensity);

                result.Key = key;

                result.Pitch = RandomPitchRange.RandomInRange();

                return result;
            }

            return null;
        }

        /// <summary>
        /// Gives an updated pitch based on an initial pitch and velocity.
        /// </summary>
        /// <param name="originalPitch">The pitch of the original result.</param>
        /// <param name="velocity">The current velocity.</param>
        /// <returns>The updated pitch based on the original pitch and velocity.</returns>
        public float UpdatePitch(float originalPitch, Vector3 velocity)
        {
            return originalPitch + velocity.magnitude * SlideVelocityPitchMultiplier;
        }

        private float getVolumeScale(float normalizedIntensity)
        {
            if (ScaleVolumeWithVelocity)
            {
                return VelocityVolumeScaleCurve.Evaluate(normalizedIntensity);
            }
            else
            {
                return 1;
            }
        }

        private string getSoundGroup(int interactionType, float normalizedIntensity)
        {
            if (interactionType == InteractionData.InteractionTypeCollision)
                return getCollisionSoundGroup(normalizedIntensity).SoundGroup;
            else if (interactionType == InteractionData.InteractionTypeSlide)
                return SlideSoundGroup;
            else if (interactionType == InteractionData.InteractionTypeRoll)
                return RollSoundGroup;

            return null;
        }

        private CollisionSoundGroup getCollisionSoundGroup(float normalizedIntensity)
        {
            if (CollisionSoundGroups.Count == 0)
                return new CollisionSoundGroup();

            if (_collisionSoundGroupSelectionMode == CollisionSoundGroupSelectionMode.Random)
            {
                return getRandomCollisionSoundGroup();
            }
            else
            {
                int index = (int)(Mathf.Clamp01(normalizedIntensity) * (CollisionSoundGroups.Count - 1));
                return CollisionSoundGroups[index];
            }
        }

        private CollisionSoundGroup getRandomCollisionSoundGroup()
        {
            if (CollisionSoundGroups.Count == 0)
                return new CollisionSoundGroup();

            int index = UnityEngine.Random.Range(0, CollisionSoundGroups.Count);
            return CollisionSoundGroups[index];
        }

        public override void Preload()
        {
            ImpactManagerInstance.CreateInteractionResultPool<MasterAudioInteractionResult>(interactionResultPoolKey);
        }
    }
}
