using System;
using System.Collections;
using NaughtyAttributes;
using UnityEngine;

namespace EscapeRoomEngine.Engine.Runtime.Environment
{
    [RequireComponent(typeof(AudioSource))]
    public class AmbienceAudio : MonoBehaviour
    {
        public static AmbienceAudio Instance { get; private set; }
        
        public float fadeDuration = 0.5f;
        public AmbienceClip initialAmbience;
        [BoxGroup("Internal")] [SerializeField] private AudioSource[] sources;

        private AudioSource CurrentSource => sources[currentSource];
        private AudioSource OtherSource => sources[(currentSource + 1) % 2];
        
        private int currentSource;
        private readonly Coroutine[] _activeFades = new Coroutine[2]; // the indices in these fades do not necessarily correspond to the sources

        private void Awake()
        {
            Instance = this;
            
            if (sources.Length != 2)
            {
                throw new Exception("There must be two sources.");
            }
            if (CurrentSource == OtherSource)
            {
                throw new Exception("Current source must be different from other source.");
            }

            foreach (var source in sources)
            {
                source.loop = true;
            }
        }

        public void StartAmbience()
        {
            CrossFadeTo(initialAmbience);
        }

        public void CrossFadeTo(AmbienceClip ambience)
        {
            // prepare fade source
            var fadeSource = OtherSource;
            fadeSource.clip = ambience.audio;
            fadeSource.Play();

            // stop any running fades
            foreach (var fade in _activeFades)
            {
                if (fade != null)
                {
                    StopCoroutine(fade);
                }
            }
            
            // start new fades
            _activeFades[0] = StartCoroutine(Fade(CurrentSource, CurrentSource.volume, 0));
            _activeFades[1] = StartCoroutine(Fade(fadeSource, fadeSource.volume, ambience.volume));

            // switch current source
            currentSource = (currentSource + 1) % 2;
        }

        private IEnumerator Fade(AudioSource source, float from, float to)
        {
            var startTime = Time.time;

            while (Math.Abs(source.volume - to) > 0.0001f)
            {
                source.volume = Mathf.Clamp01(Mathf.Lerp(
                    from,
                    to,
                    (Time.time - startTime) / fadeDuration));
                yield return null;
            }
        }
    }
}