using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FairyGUI;
using UnityEngine;
using UnityEngine.Events;

namespace ChillConnect
{
    public sealed class AudioManager : BaseUnityManager<AudioManager>
    {
        public const bool IsUnscaleTime = true;
        private Dictionary<string, AudioClip> audioClipCacheDict = new Dictionary<string, AudioClip>();
        private GameObject newEffectSourcesRoot;
        private GameObject dynamicEffectSourcesRoot;
        [HideInInspector] public AudioListener audioListener;
        [HideInInspector] public AudioSource bgmSource;
        [HideInInspector] public AudioSource effectSource;
        [HideInInspector] public List<AudioSource> dynamicEffectSources = new List<AudioSource>();
        [HideInInspector] public List<AudioSource> unscaleTimeSounds = new List<AudioSource>();
        [HideInInspector] public float currUISoundVolume = 1;

        private bool isOpenBGM;

        public bool IsOpenBGM
        {
            get { return isOpenBGM; }
            set
            {
                isOpenBGM = value;
                PlayerPrefsKit.WriteBool(PrefsKeyConst.AudioMgr_isOpenBGM, isOpenBGM);
                if (!bgmSource) return;

                bgmSource.enabled = isOpenBGM;
                if (isOpenBGM)
                {
                    bgmSource.Play();
                }
                else
                {
                    bgmSource.Pause();
                }
            }
        }

        private bool isOpenEffect;

        public bool IsOpenEffect
        {
            get { return isOpenEffect; }
            set
            {
                isOpenEffect = value;
                PlayerPrefsKit.WriteBool(PrefsKeyConst.AudioMgr_isOpenEffect, isOpenEffect);


                GRoot.inst.soundVolume = isOpenEffect ? currUISoundVolume : 0f;
                if (isOpenEffect)
                {
                    GRoot.inst.EnableSound();
                }
                else
                {
                    GRoot.inst.DisableSound();
                }
            }
        }

        private void InitAudioMode()
        {
            isOpenBGM = PlayerPrefsKit.ReadBool(PrefsKeyConst.AudioMgr_isOpenBGM, true);
            isOpenEffect = PlayerPrefsKit.ReadBool(PrefsKeyConst.AudioMgr_isOpenEffect, true);

            GRoot.inst.soundVolume = 1;
            currUISoundVolume = GRoot.inst.soundVolume;
            GRoot.inst.soundVolume = isOpenEffect ? currUISoundVolume : 0f;
        }

        public override void Init()
        {
            base.Init();
            AppDispatcher.Instance.AddListener(AppMsg.TimeScale_Change, OnTimeScaleChange);

            audioListener = gameObject.AddComponent<AudioListener>();

            bgmSource = gameObject.AddComponent<AudioSource>();
            bgmSource.playOnAwake = false;
            bgmSource.loop = true;

            effectSource = gameObject.AddComponent<AudioSource>();
            effectSource.playOnAwake = false;
            effectSource.loop = false;

            newEffectSourcesRoot = new GameObject("NewEffectSources");
            newEffectSourcesRoot.transform.SetParent(gameObject.transform, false);
            dynamicEffectSourcesRoot = new GameObject("DynamicEffectSources");
            dynamicEffectSourcesRoot.transform.SetParent(gameObject.transform, false);

            InitAudioMode();
        }

        public override void Dispose()
        {
            base.Dispose();
            AppDispatcher.Instance.RemoveListener(AppMsg.TimeScale_Change, OnTimeScaleChange);
        }

        public void InitDefaultButtonClickSound(string btnSound)
        {
            string defaultSound = AudioConst.gameclick;
            if (!string.IsNullOrEmpty(btnSound))
            {
                defaultSound = btnSound;
            }

            LoadKit.Instance.LoadAudio("Audio", defaultSound, audioClip =>
            {
                if (audioClip != null)
                {
                    UIConfig.buttonSound = new NAudioClip(audioClip);
                    UIConfig.buttonSoundVolumeScale = 1;
                }
            });
        }

        private void OnTimeScaleChange(object value)
        {
            if (unscaleTimeSounds.Count != 0)
            {
                for (int i = 0; i < unscaleTimeSounds.Count; i++)
                {
                    AudioSource audioSource = unscaleTimeSounds[i];
                    if (audioSource)
                    {
                        audioSource.pitch = 1;
                    }
                }
            }
        }

        public void StopBGM()
        {
            bgmSource.Stop();
        }

        public void PlayBGM(string audioName, bool isUnscaleTime = IsUnscaleTime)
        {
            if (string.IsNullOrEmpty(audioName)) return;

            string curName = bgmSource.clip == null ? null : bgmSource.clip.name;

            if (curName != audioName)
            {
                AudioClip currClip = null;
                if (audioClipCacheDict.TryGetValue(audioName, out var value))
                {
                    currClip = value;
                }
                else
                {
                    try
                    {
                        currClip = LoadKit.Instance.LoadAsset<AudioClip>("Audio", audioName);
                    }
                    catch (Exception)
                    {
                    }
                }


                if (currClip != null)
                {
                    bgmSource.clip = currClip;
                    if (!isOpenBGM)
                    {
                        return;
                    }

                    if (isUnscaleTime)
                    {
                        if (!unscaleTimeSounds.Contains(bgmSource))
                        {
                            unscaleTimeSounds.Add(bgmSource);
                        }
                    }
                    else
                    {
                        if (unscaleTimeSounds.Contains(bgmSource))
                        {
                            unscaleTimeSounds.Remove(bgmSource);
                        }
                    }

                    bgmSource.pitch = isUnscaleTime ? 1 : Time.timeScale;
                    SetBGMSound();
                    bgmSource.Play();
                }
            }
        }
        public void SetBGMSound()
        {
            if (bgmSource == null)
            {
                return;
            }
            float volume = PlayerPrefs.GetFloat("musicSound", 1.0f);
            bgmSource.volume = volume;
        }

        public AudioSource PlayDynamicEffect(string audioName, float delay = 0, bool isLoop = false,
            bool isUnscaleTime = IsUnscaleTime)
        {
            if (!isOpenEffect || string.IsNullOrEmpty(audioName)) return null;
            if (GRoot.inst.soundVolume <= 0f) return null;
            AudioSource effectSourceCom = null;
            for (int i = 0; i < dynamicEffectSources.Count; i++)
            {
                AudioSource sourceItem = dynamicEffectSources[i];
                if (!sourceItem.isPlaying)
                {
                    effectSourceCom = sourceItem;
                    break;
                }
            }

            if (effectSourceCom == null)
            {
                effectSourceCom = dynamicEffectSourcesRoot.AddComponent<AudioSource>();
                effectSourceCom.playOnAwake = false;
                dynamicEffectSources.Add(effectSourceCom);
            }

            if (effectSourceCom.loop != isLoop)
            {
                effectSourceCom.loop = isLoop;
            }


            string curName;
            AudioClip currClip = null;
            if (effectSourceCom.clip == null)
            {
                curName = null;
            }
            else
            {
                currClip = effectSourceCom.clip;
                curName = currClip.name;
            }

            var playAction = new UnityAction(delegate
            {
                if (currClip != null)
                {
                    effectSourceCom.clip = currClip;

                    if (isUnscaleTime)
                    {
                        if (!unscaleTimeSounds.Contains(effectSourceCom))
                        {
                            unscaleTimeSounds.Add(effectSourceCom);
                        }
                    }
                    else
                    {
                        if (unscaleTimeSounds.Contains(effectSourceCom))
                        {
                            unscaleTimeSounds.Remove(effectSourceCom);
                        }
                    }

                    effectSourceCom.pitch =
                        isUnscaleTime ? 1 : Time.timeScale;
                    if (delay == 0)
                    {
                        effectSourceCom.Play();
                    }
                    else
                    {
                        effectSourceCom.PlayDelayed(delay);
                    }
                }
            });

            if (curName != audioName)
            {
                if (audioClipCacheDict.ContainsKey(audioName))
                {
                    currClip = audioClipCacheDict[audioName];
                    playAction();
                }
                else
                {
                    var path = new StringBuilder("Audio");
                    var strs = audioName.Split('.');
                    var assetName = strs.Last();
                    if (strs.Length > 1)
                    {
                        var assetUrl = audioName.Replace($".{assetName}", string.Empty);
                        path.Append(".").Append(assetUrl);
                    }

                    LoadKit.Instance.LoadAudio(path.ToString(), assetName, clip =>
                    {
                        currClip = clip;
                        audioClipCacheDict[audioName] = clip;
                        playAction();
                    });
                }
            }
            else
            {
                try
                {
                    if (delay == 0)
                    {
                        effectSourceCom.Play();
                    }
                    else
                    {
                        effectSourceCom.PlayDelayed(delay);
                    }
                }
                catch (Exception)
                {
                }
            }

            return effectSourceCom;
        }
    }
}