using Microsoft.Xna.Framework.Audio;
using MonoGame.FourEngine.Resources;
using System;
using System.Linq;

namespace MonoGame.FourEngine.Components
{
    public class SfxComponent : Component, ISoundComponent, IDisposable
    {
        private SoundEffectInstance _instance;

        public Sfx Sfx { get; }

        /// <summary>
        /// Pitch adjustment between -1 and 1
        /// </summary>
        public float Pitch
        {
            get => _pitch;
            set
            {
                if (_pitch == value)
                    return;
                _pitch = value;
                if (_pitch < -1) _pitch = -1;
                if (_pitch > 1) _pitch = 1;
                OnPitchChanged(_pitch);
            }
        }
        private float _pitch;

        public float Pan
        {
            get => _pan;
            set
            {
                if (_pan == value)
                    return;
                _pan = value;
                if (_pan < -1) _pan = -1;
                if (_pan > 1) _pan = 1;
                OnPanChanged(_pan);
            }
        }
        private float _pan;

        public float Volume
        {
            get => _volume;
            set
            {
                if (_volume == value)
                    return;
                _volume = value;
                if (_volume < 0) _volume = 0;
                if (_volume > 1) _volume = 1;
                OnVolumeChanged(_volume);
            }
        }
        private float _volume = 1f;
        
        public SfxComponent(string fileName) : this(new SfxDefinition { FileName = fileName })
        {
        }

        public SfxComponent(SfxDefinition def)
        {
            ServiceLocator.ResourceManager.Add(ResourceTypes.Sfx, def);
            if (!string.IsNullOrEmpty(def?.FileName))
                Sfx = ServiceLocator.ResourceManager.Get<Sfx>(def.FileName);
        }

        public void Play()
        {
            if (Mute) return;
            Stop();
            _instance = Sfx.SoundEffect.CreateInstance();
            _instance.Play();
            OnPitchChanged(Pitch);
            OnPanChanged(Pan);
            OnVolumeChanged(Volume);
        }

        public void Stop() => Dispose();

        private void OnPitchChanged(float value)
        {
            if (_instance == null)
                return;
            _instance.Pitch = value;
        }

        private void OnPanChanged(float value)
        {
            if (_instance == null)
                return;
            _instance.Pan = value;
        }

        private void OnVolumeChanged(float value)
        {
            if (_instance == null || Mute)
                return;
            _instance.Volume = Mute ? 0 : value;
        }

        public void Dispose()
        {
            if (_instance == null)
                return;
            if (_instance.State != SoundState.Stopped)
                _instance.Stop(true);
            _instance.Dispose();
            _instance = null;
        }

        #region Mute
        public static bool Mute
        {
            get => _mute;
            set
            {
                _mute = value;
                OnMuteChanged();
            }
        }
        private static bool _mute;

        private static void OnMuteChanged()
        {
            var components = FourEngineGame.Current.GetScenes()
                .SelectMany(x => x.Entities)
                .SelectMany(x => x.Components)
                .Where(x => x is SfxComponent)
                .Cast<SfxComponent>();
            foreach (var component in components)
            {
                component.OnVolumeChanged(component.Volume);
            }
        }
        #endregion
    }
}