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

namespace JLGames.RocketDriver.Actions.Audio
{
    public class AudioSoundHandler : MonoBehaviour
    {
        //系统级设置
        private AudioSettingsSys m_SysSettings;

        //声音设置
        private AudioSettingsUser m_UserSettings;

        //播放中列表
        private readonly List<AudioSound> m_PlayingSounds = new List<AudioSound>();

        //准备销毁列表
        private readonly List<AudioSound> m_DestoryingSounds = new List<AudioSound>();

        //已经销毁列表，重用对象
        private readonly List<AudioSound> m_DestoryedSounds = new List<AudioSound>();

        private IEnumerator m_EnumeratorUpdate;

        public void InitSysSettings(AudioSettingsSys sysSettings)
        {
            m_SysSettings = sysSettings;
        }

        public void SetUserSettings(AudioSettingsUser userSettings)
        {
            m_UserSettings = userSettings;
        }

        public void PlaySound(string named, AudioClip sound, Transform attachTransform, float volumeScale, bool pausable)
        {
            InnerPlaySoundAudio(named, sound, attachTransform, volumeScale, pausable);
            InnerTryStartUpdateCoroutine();
        }

        public void PauseSounds(string named)
        {
            InnerPauseSoundAudio(named);
        }

        public void GoesOnSounds(string named)
        {
            InnerGoseOnSoundAudio(named);
        }

        public void StopSounds(string named)
        {
            InnerStopPlayingSoundAudio(named);
            InnerTryTransferList();
        }

        public void ApplyVolume()
        {
            if (m_PlayingSounds.Count <= 0)
            {
                return;
            }

            foreach (var sound in m_PlayingSounds)
            {
                sound.SetVolume(m_UserSettings.VolumeSound);
            }
        }

        public void ApplyMuted()
        {
            if (m_PlayingSounds.Count <= 0)
            {
                return;
            }

            foreach (var sound in m_PlayingSounds)
            {
                sound.SetMuted(m_UserSettings.IsMutedSound);
            }
        }

        public void Reset()
        {
            InnerTryStopUpdateCoroutine();
            foreach (var sound in m_DestoryingSounds)
            {
                sound.Destory();
            }

            m_DestoryingSounds.Clear();
            foreach (var sound in m_PlayingSounds)
            {
                sound.Destory();
            }

            m_PlayingSounds.Clear();
            m_DestoryedSounds.Clear();
        }

        void OnDestroy()
        {
            Reset();
        }

        #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()
        {
            while (m_PlayingSounds.Count > 0 || m_DestoryingSounds.Count > 0)
            {
                yield return null;
                InnerTryTransferList();
                InnerTryDestoryFirst();
                InnerTryLateUpdateAttach();
            }

            m_EnumeratorUpdate = null;
        }

        /// <summary>
        /// 转移对象：把已经销毁的转移到destoryedSounds中，把准备销毁的转移到destoryingSounds中。
        /// </summary>
        private void InnerTryTransferList()
        {
            if (m_PlayingSounds.Count <= 0)
            {
                return;
            }

            int index;
            for (index = m_PlayingSounds.Count - 1; index >= 0; index--)
            {
                var sound = m_PlayingSounds[index];
                if (sound.IsDestoryed)
                {
                    InnerTransfer(m_PlayingSounds, m_DestoryedSounds, index);
                    continue;
                }

                if (sound.IsStop)
                {
                    InnerTransfer(m_PlayingSounds, m_DestoryingSounds, index);
                    continue;
                }
            }
        }

        /// <summary>
        /// 销毁一个不播放的音效，并放回到destoryedSounds列表中重用
        /// </summary>
        private void InnerTryDestoryFirst()
        {
            if (m_DestoryingSounds.Count <= 0)
            {
                return;
            }

            m_DestoryingSounds[0].Destory();
            InnerTransfer(m_DestoryingSounds, m_DestoryedSounds, 0);
        }

        /// <summary>
        /// 更新音源的位置
        /// </summary>
        private void InnerTryLateUpdateAttach()
        {
            if (m_PlayingSounds.Count <= 0)
            {
                return;
            }

            foreach (var sound in m_PlayingSounds)
            {
                if (sound.IsAttached)
                {
                    sound.SetPosition(sound.AttachTransform.position);
                }
            }
        }

        #endregion

        #region private functions

        /// <summary>
        /// 播放一个音效
        /// </summary>
        /// <param name="named">Named.</param>
        /// <param name="soundClip"></param>
        /// <param name="attachTransform">Attach transform.</param>
        /// <param name="precentage">Precentage.</param>
        /// <param name="pausable">If set to <c>true</c> pausable.</param>
        private void InnerPlaySoundAudio(string named, AudioClip soundClip, Transform attachTransform, float precentage, bool pausable)
        {
            if (!InnerValidMax(named)) return;

            var sound = InnerPopDestoryedSound();
            var soundSource = InnerCreateSoundSource(named, soundClip, pausable);
            soundSource.volume = precentage * m_UserSettings.VolumeSound;
            sound.Named = named;
            sound.Source = soundSource;
            sound.AttachTransform = attachTransform;
            m_PlayingSounds.Add(sound);
            sound.Play();
        }

        /// <summary>
        /// 暂停播放中的音效
        /// </summary>
        private void InnerPauseSoundAudio(string named)
        {
            if (m_PlayingSounds.Count <= 0) return;

            foreach (var sound in m_PlayingSounds)
            {
                if (string.IsNullOrEmpty(named) || sound.Named == named)
                {
                    sound.Pause();
                }
            }
        }

        /// <summary>
        /// 继续播放中的音效
        /// </summary>
        private void InnerGoseOnSoundAudio(string named)
        {
            if (m_PlayingSounds.Count <= 0) return;

            foreach (var sound in m_PlayingSounds)
            {
                if (string.IsNullOrEmpty(named) || sound.Named == named)
                {
                    sound.UnPause();
                }
            }
        }

        /// <summary>
        /// 停止播放中的音效
        /// </summary>
        private void InnerStopPlayingSoundAudio(string named)
        {
            if (m_PlayingSounds.Count <= 0) return;

            foreach (var sound in m_PlayingSounds)
            {
                if (string.IsNullOrEmpty(named) || sound.Named == named)
                {
                    sound.Stop();
                }
            }
        }

        /// <summary>
        /// 验证
        /// </summary>
        /// <returns><c>true</c>, if max was valided, <c>false</c> otherwise.</returns>
        /// <param name="named">Named.</param>
        private bool InnerValidMax(string named)
        {
            var maxCount = m_PlayingSounds.Count;
            if (maxCount >= m_SysSettings.MaxSoundCount)
            {
//                Debug.Log("Sound Max");
                return false;
            }

            var sameCount = 0;
            foreach (var sound in m_PlayingSounds)
            {
                if (sound.Named == named /* && sound.source.isPlaying*/)
                {
                    sameCount++;
                }
            }

            if (sameCount >= m_SysSettings.MaxSameSound)
            {
//                Debug.Log("Sound Same Max");
                return false;
            }

            return true;
        }

        /// <summary>
        /// 对象从列表中转移
        /// </summary>
        /// <param name="sourceList">Source list.</param>
        /// <param name="targetList">Target list.</param>
        /// <param name="obj">Object.</param>
        private void InnerTransfer(List<AudioSound> sourceList, List<AudioSound> targetList, AudioSound obj)
        {
            if (sourceList.Contains(obj))
            {
                sourceList.Remove(obj);
            }

            if (!targetList.Contains(obj))
            {
                sourceList.Add(obj);
            }
        }

        /// <summary>
        /// 对象从列表中转移
        /// </summary>
        /// <param name="sourceList">Source list.</param>
        /// <param name="targetList">Target list.</param>
        /// <param name="index">Index.</param>
        private void InnerTransfer(List<AudioSound> sourceList, List<AudioSound> targetList, int index)
        {
            var obj = sourceList[index];
            sourceList.RemoveAt(index);
            targetList.Add(obj);
        }

        /// <summary>
        /// 从DestoryedList中选择一个对象，如果列表为空，则创建一个
        /// </summary>
        /// <returns>The destoryed sound.</returns>
        private AudioSound InnerPopDestoryedSound()
        {
            AudioSound sound;
            if (m_DestoryedSounds.Count > 0)
            {
                var index = m_DestoryedSounds.Count - 1;
                sound = m_DestoryedSounds[index];
                m_DestoryedSounds.RemoveAt(index);
            }
            else
            {
                sound = new AudioSound();
            }

            return sound;
        }

        /// <summary>
        /// 初始化一个AudioSource
        /// </summary>
        /// <returns>The sound source.</returns>
        /// <param name="named">Named.</param>
        /// <param name="soundClip"></param>
        /// <param name="pausable">If set to <c>true</c> pausable.</param>
        private AudioSource InnerCreateSoundSource(string named, AudioClip soundClip, bool pausable)
        {
//            DebugUtil.Log("InnerCreateSoundSource:", named, pausable);
            var soundObject = new GameObject("Sound_" + named);
            var soundSource = soundObject.AddComponent<AudioSource>();
            TransformUtil.AddChildTo(soundObject.transform, transform);
            soundSource.outputAudioMixerGroup = m_SysSettings.SoundAudioMixerGroup;
            soundSource.priority = 128;
            soundSource.loop = false;
            soundSource.playOnAwake = false;
            soundSource.mute = m_UserSettings.IsMutedSound;
            soundSource.ignoreListenerPause = !pausable;
            soundSource.clip = soundClip;
            return soundSource;
        }

        #endregion
    }
}