/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */
using UnityEngine;
namespace Live2D.Cubism.Framework.MouthMovement
{
    /// 
    /// Real-time  input from s.
    /// 
    [RequireComponent(typeof(CubismMouthController))]
    public sealed class CubismAudioMouthInput : MonoBehaviour
    {
        /// 
        /// Audio source to sample.
        /// 
        [SerializeField]
        public AudioSource AudioInput;
        /// 
        /// Sampling quality.
        /// 
        [SerializeField]
        public CubismAudioSamplingQuality SamplingQuality;
        /// 
        /// Audio gain.
        /// 
        [Range(1.0f, 10.0f)]
        public float Gain = 1.0f;
        /// 
        /// Smoothing.
        /// 
        [Range(0.0f, 1.0f)]
        public float Smoothing;
        /// 
        /// Current samples.
        /// 
        private float[] Samples { get; set; }
        /// 
        /// Last root mean square.
        /// 
        private float LastRms { get; set; }
        /// 
        /// Buffer for  velocity.
        /// 
        // ReSharper disable once InconsistentNaming
        private float VelocityBuffer;
        /// 
        /// Targeted .
        /// 
        private CubismMouthController Target { get; set; }
        /// 
        /// True if instance is initialized.
        /// 
        private bool IsInitialized
        {
            get { return Samples != null; }
        }
        /// 
        /// Makes sure instance is initialized.
        /// 
        private void TryInitialize()
        {
            // Return early if already initialized.
            if (IsInitialized)
            {
                return;
            }
            // Initialize samples buffer.
            switch (SamplingQuality)
            {
                case (CubismAudioSamplingQuality.VeryHigh):
                {
                        Samples = new float[256];
                        break;
                    }
                case (CubismAudioSamplingQuality.Maximum):
                {
                    Samples = new float[512];
                    break;
                }
                default:
                {
                    Samples = new float[256];
                    break;
                }
            }
            // Cache target.
            Target = GetComponent();
        }
        #region Unity Event Handling
        /// 
        /// Samples audio input and applies it to mouth controller.
        /// 
        private void Update()
        {
            // 'Fail' silently.
            if (AudioInput == null)
            {
                return;
            }
            // Sample audio.
            var total = 0f;
            AudioInput.GetOutputData(Samples, 0);
            for (var i = 0; i < Samples.Length; ++i)
            {
                var sample = Samples[i];
                total += (sample * sample);
            }
            // Compute root mean square over samples.
            var rms = Mathf.Sqrt(total / Samples.Length) * Gain;
            // Clamp root mean square.
            rms = Mathf.Clamp(rms, 0.0f, 1.0f);
            // Smooth rms.
            rms = Mathf.SmoothDamp(LastRms, rms, ref VelocityBuffer, Smoothing * 0.1f);
            // Set rms as mouth opening and store it for next evaluation.
            Target.MouthOpening = rms;
            LastRms = rms;
        }
        /// 
        /// Initializes instance.
        /// 
        private void OnEnable()
        {
            TryInitialize();
        }
        #endregion
    }
}