﻿using System;
using System.Collections;
using System.Collections.Generic;
using JLGames.RocketDriver.Actions.Utils;
using UnityEngine;

namespace JLGames.RocketDriver.Actions.Audio
{
    public class AudioMusicHandler : MonoBehaviour
    {
        private AudioSettingsSys m_SysSettings; //系统级设置
        private AudioSettingsUser m_UserSettings; //用户级设置
        private readonly AudioMusicFadingIn m_CurrentMusic = new AudioMusicFadingIn(); //当前音乐
        private readonly List<AudioMusicFadingOut> m_MusicFadingsOut = new List<AudioMusicFadingOut>(); //淡出列表

        private IEnumerator m_EnumeratorUpdate;

        /// <summary>
        /// Initialize system settings.
        /// 初始化系统设置
        /// </summary>
        /// <param name="sysSettings"></param>
        public void InitSysSettings(AudioSettingsSys sysSettings)
        {
            m_SysSettings = sysSettings;
        }

        /// <summary>
        /// Set user's custom settings.
        /// 设置用户自定义设置
        /// </summary>
        /// <param name="userSettings"></param>
        public void SetUserSettings(AudioSettingsUser userSettings)
        {
            m_UserSettings = userSettings;
        }

        /// <summary>
        /// The Playing music.
        /// 当前播放中的音乐
        /// Note: Pausing as playing
        /// 注册：暂停也算播放中
        /// </summary>
        public bool IsMusicPlaying => !m_CurrentMusic.IsEmpty;

        /// <summary>
        /// The name of the currently playing music.
        /// 当前播放中的音乐名称
        /// </summary>
        public string PlayingMusicName => IsMusicPlaying ? m_CurrentMusic.Named : null;

        /// <summary>
        /// Swith music
        /// 切换音乐
        /// </summary>
        /// <param name="named"></param>
        /// <param name="music"></param>
        /// <param name="fading"></param>
        public void SwitchMusic(string named, AudioClip music, bool fading)
        {
            if (string.IsNullOrEmpty(named))
            {
                return;
            }

            if (named == m_CurrentMusic.Named)
            {
                return;
            }

            if (fading)
            {
                InnerFadingOut();
                InnerFadingIn(named, music);
                InnerTryStartUpdateCoroutine();
            }
            else
            {
                InnerStopMusicNow();
                InnerPlayMusicNow(named, music);
            }
        }

        public void StopMusic(bool fading)
        {
            if (fading)
            {
                InnerFadingOut();
                InnerTryStartUpdateCoroutine();
            }
            else
            {
                InnerStopFadingOutNow();
                InnerStopMusicNow();
                InnerTryStopUpdateCoroutine();
            }
        }

        public void PauseMusic()
        {
            if (m_CurrentMusic.IsEmpty) return;
            m_CurrentMusic.Source.Pause();
        }

        public void GoseOnMusic()
        {
            if (m_CurrentMusic.IsEmpty) return;
            m_CurrentMusic.Source.UnPause();
        }

        public void ApplyVolume()
        {
            if (m_CurrentMusic.IsEmpty)
            {
                return;
            }

            m_CurrentMusic.FadingIn = false;
            m_CurrentMusic.TargetVolume = m_UserSettings.VolumeMusic;
            m_CurrentMusic.Source.volume = m_UserSettings.VolumeMusic;
        }

        public void ApplyMuted()
        {
            if (m_CurrentMusic.IsEmpty)
            {
                return;
            }

            m_CurrentMusic.Source.mute = m_UserSettings.IsMutedMusic;
        }

        public void Reset()
        {
            InnerTryStopUpdateCoroutine();
            StopMusic(false);
        }

        #region AudioManager 协程调用

        private void InnerTryStartUpdateCoroutine()
        {
            if (m_EnumeratorUpdate != null)
            {
                return;
            }

            m_EnumeratorUpdate = InnerCoroutineUpdate();
            StartCoroutine(m_EnumeratorUpdate);
        }

        private void InnerTryStopUpdateCoroutine()
        {
            if (m_EnumeratorUpdate == null)
            {
                return;
            }

            StopCoroutine(m_EnumeratorUpdate);
            m_EnumeratorUpdate = null;
        }

        private IEnumerator InnerCoroutineUpdate()
        {
            var fd = true;
            while (fd)
            {
                yield return null;
                fd = InnerFadingInUpdate() | InnerFadingOutUpdate(); //使用非短路
            }

            m_EnumeratorUpdate = null;
        }

        private bool InnerFadingOutUpdate()
        {
            if (m_MusicFadingsOut.Count == 0)
            {
                return false;
            }

//            Debug.Log("fadingOutUpdate");
            for (var i = 0; i < m_MusicFadingsOut.Count; i++)
            {
                var music = m_MusicFadingsOut[i];
                if (music.Source == null)
                {
                    m_MusicFadingsOut.RemoveAt(i);
                    i--;
                }
                else
                {
                    music.Timer += Time.unscaledDeltaTime;
                    m_MusicFadingsOut[i] = music;
                    if (music.Timer >= music.FadingTime)
                    {
                        Destroy(music.Source.gameObject);
                        m_MusicFadingsOut.RemoveAt(i);
                        i--;
                    }
                    else
                    {
                        var k = Mathf.Clamp01(music.Timer / music.FadingTime);
                        music.Source.volume = Mathf.Lerp(music.StartVolume, 0, k);
                    }
                }
            }

            return m_MusicFadingsOut.Count > 0;
        }

        private bool InnerFadingInUpdate()
        {
            if (!m_CurrentMusic.FadingIn)
            {
                return false;
            }

//            Debug.Log("fadingInUpdate");
            m_CurrentMusic.Timer += Time.unscaledDeltaTime;
            if (m_CurrentMusic.Timer >= m_CurrentMusic.FadingTime)
            {
                //超出淡入总时间
                m_CurrentMusic.Source.volume = m_CurrentMusic.TargetVolume;
                m_CurrentMusic.FadingIn = false;
                return false;
            }
            else
            {
                var k = Mathf.Clamp01(m_CurrentMusic.Timer / m_CurrentMusic.FadingTime);
                m_CurrentMusic.Source.volume = Mathf.Lerp(0, m_CurrentMusic.TargetVolume, k);
                return true;
            }
        }

        #endregion

        #region private functions

        /// <summary>
        /// Set the current music fading out.
        /// 淡出当前音乐
        /// </summary>
        private void InnerFadingOut()
        {
            if (m_CurrentMusic.IsEmpty)
            {
                return;
            }

            //生成FadingOut对象
            var fader = new AudioMusicFadingOut
            {
                Source = m_CurrentMusic.Source,
                FadingTime = m_SysSettings.MusicFadeTime,
                Timer = 0,
                StartVolume = m_CurrentMusic.Source.volume
            };
            m_MusicFadingsOut.Add(fader);
            //清空当前记录
            m_CurrentMusic.ResetObject();
        }

        /// <summary>
        /// Play music fading in.
        /// 淡入播放 
        /// </summary>
        /// <param name="named"></param>
        /// <param name="musicClip"></param>
        /// <exception cref="Exception"></exception>
        private void InnerFadingIn(string named, AudioClip musicClip)
        {
            if (string.IsNullOrEmpty(named))
            {
                throw new Exception("fadingIn.named: " + named);
            }

            m_CurrentMusic.ResetObject();
            //设置FadingIn参数
            m_CurrentMusic.Named = named;
            var source = InnerCreateMusic(named, musicClip);
            m_CurrentMusic.Source = source;
            m_CurrentMusic.FadingTime = m_SysSettings.MusicFadeTime;
            source.volume = 0; //设置初始音量
            m_CurrentMusic.TargetVolume = m_UserSettings.VolumeMusic;
            m_CurrentMusic.FadingIn = true;
            source.Play();
        }

        /// <summary>
        /// Stop the current music immediately.
        /// 马上停止当前的播放
        /// </summary>
        private void InnerStopMusicNow()
        {
            if (m_CurrentMusic.IsEmpty)
            {
                return;
            }

            if (null == m_CurrentMusic.Source)
            {
                return;
            }

            m_CurrentMusic.Source.Stop();
            Destroy(m_CurrentMusic.Source.gameObject);
            m_CurrentMusic.ResetObject();
        }

        /// <summary>
        /// Play music immediately.
        /// 马上播放音乐
        /// </summary>
        /// <param name="named"></param>
        /// <param name="musicClip"></param>
        private void InnerPlayMusicNow(string named, AudioClip musicClip)
        {
            //设置当前播放对象参数
            m_CurrentMusic.Named = named;
            var source = InnerCreateMusic(named, musicClip);
            source.volume = m_UserSettings.VolumeMusic;
            m_CurrentMusic.Source = source;
            m_CurrentMusic.FadingIn = false;
            source.Play();
        }

        /// <summary>
        /// Turn off fade out immediately.
        /// 马上关闭淡出
        /// </summary>
        private void InnerStopFadingOutNow()
        {
            if (m_MusicFadingsOut.Count == 0)
            {
                return;
            }

            for (var i = 0; i < m_MusicFadingsOut.Count; i++)
            {
                var music = m_MusicFadingsOut[i];
                music.Source.Stop();
                Destroy(music.Source.gameObject);
            }

            m_MusicFadingsOut.Clear();
        }

        private AudioSource InnerCreateMusic(string named, AudioClip musicClip)
        {
            var music = new GameObject("Music_" + named);
            var musicSource = music.AddComponent<AudioSource>();
            TransformUtil.AddChildTo(music.transform, transform);
            //musicSource.outputAudioMixerGroup = _settings.MusicAudioMixerGroup;
            musicSource.loop = true;
            musicSource.priority = 0;
            musicSource.playOnAwake = false;
            musicSource.mute = m_UserSettings.IsMutedMusic;
            musicSource.ignoreListenerPause = true;
            musicSource.clip = musicClip;
            return musicSource;
        }

        #endregion
    }
}