using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using System.Linq;

public class AudioMaster : MonoBehaviour
{
	public const float DEFAULT_FADE_IN = 0.5f;
	public const float DEFAULT_FADE_OUT = 1f;

	#region Static instance
	private static AudioMaster _instance;
	public static AudioMaster instance
	{
		get
		{
			if (_instance == null)
				_instance = FindObjectOfType<AudioMaster>();
			return _instance;
		}
	}
	#endregion

	private static List<AudioSource> audioSources = new List<AudioSource>();
	private static AudioSource currentBackgroundSource;

	[Header("Soundtracks")]
	public AudioClip mainSceneClip;
	public AudioClip battleClip;
	public AudioClip menuClip;

	[Header("Sound effects")]
	public AudioClip correctHandClip;
	public AudioClip wrongHandClip;
    public AudioClip wave;
    public AudioClip[] steps;
    public AudioClip[] handMoves;

	private void Awake()
	{
        if (instance != this)
        {
            Destroy(this.gameObject);
			return;
        }
        else
        {
            DontDestroyOnLoad(this.gameObject);
        }

		audioSources.AddRange(GetComponentsInChildren<AudioSource>().ToList());
	}

	#region Background song
	public static void StartMainSong(float fadeOut = DEFAULT_FADE_OUT, float fadeIn = DEFAULT_FADE_IN)
	{
		StartBackgroundSong(GetClipSource(instance.mainSceneClip, true), fadeOut, fadeIn);
	}

	public static void StartBattleSong(float fadeOut = DEFAULT_FADE_OUT, float fadeIn = DEFAULT_FADE_IN)
	{
		StartBackgroundSong(GetClipSource(instance.battleClip, true), fadeOut, fadeIn);
	}
	
	public static void StartMenuSong(float fadeOut = DEFAULT_FADE_OUT, float fadeIn = DEFAULT_FADE_IN)
	{
		StartBackgroundSong(GetClipSource(instance.menuClip, true), fadeOut, fadeIn);
	}

	private static void StartBackgroundSong(AudioSource newBackgroundSource, float fadeOutTime, float fadeInTime)
	{
		if (newBackgroundSource == currentBackgroundSource)
			return;

		if (currentBackgroundSource != null)
		{
			currentBackgroundSource.DOFade(0f, fadeOutTime);
			ResetBackgroundSource(currentBackgroundSource, currentBackgroundSource.volume);
		}

		float newBackgroundVolume = newBackgroundSource.volume;
		newBackgroundSource.volume = 0f;
		newBackgroundSource.Play();
		newBackgroundSource.DOFade(newBackgroundVolume, fadeInTime);

		currentBackgroundSource = newBackgroundSource;
	}

	private static void ResetBackgroundSource(AudioSource backgroundSource, float originalVolume)
	{
		Sequence sequence = DOTween.Sequence();
		sequence.AppendInterval(1f);
		sequence.AppendCallback(backgroundSource.Stop);
		sequence.AppendCallback(() => backgroundSource.volume = originalVolume);
	}
	#endregion

	#region Sound effects
    public static void PlayCorrectHandClip(int pitchLevel)
	{
        AudioSource source = GetClipSource(instance.correctHandClip);
        source.pitch = 1f + ((float)(pitchLevel-1) / 10f);
        source.Play();
	}

    public static void ResetCorrectHandPitch()
    {
        GetClipSource(instance.correctHandClip).pitch = 1f;
    }

	public static void PlayWrongHandClip()
	{
		GetClipSource(instance.wrongHandClip).Play();
	}

    public static void PlayGestureMoveClip()
    {
        GetClipSource(instance.handMoves[Random.Range(0, instance.handMoves.Length)]).Play();
    }

    public static void PlayStepClip()
    {
        GetClipSource(instance.steps[Random.Range(0, instance.steps.Length)]).Play();
    }

    public static void PlayWaveClip()
    {
        GetClipSource(instance.wave).Play();
    }
	#endregion

	#region Tools
	// Get clip source. If none exists, creates one.
	private static AudioSource GetClipSource(AudioClip sourcedClip, bool loop = false)
	{
		for (int i = 0; i < audioSources.Count; i++)
		{
			if (audioSources[i].clip == sourcedClip)
			{
				return audioSources[i];
			}
		}

		GameObject newGameObject = new GameObject(sourcedClip.name);
		newGameObject.transform.parent = instance.transform;

		AudioSource newSource = newGameObject.AddComponent<AudioSource>();
		newSource.clip = sourcedClip;
		newSource.playOnAwake = false;
		newSource.loop = loop;

		audioSources.Add(newSource);

		return newSource;
	}
	#endregion
}