using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Audio;

[System.Serializable]
public class AudioEntry {
    public string name;
    public AudioClip clip;
    [Range(0f,2f)]
    public float pitch = 1f;
    [Range(0f,1f)]
    public float volume = 1f;
}

public class AudioManager : UniqueMonoBehaviour<AudioManager> {

    AudioSource bgm;
    AudioSource[] sfx;
    [Range(0f,1f)][SerializeField]
    float BGMMasterVolume=1f;
    [Range(0f,1f)][SerializeField]
    float SFXMasterVolume=1f;
    public float BGMMaster {
        get { if(bgm == null) return 0f; return BGMMasterVolume; }
        set {
            if(bgm == null) return;
            if(Mathf.Approximately(BGMMasterVolume,0f)) { bgm.volume = value*trackVolume; return; }
            float f = trackVolume*value; bgm.volume = f; BGMMasterVolume = value; 
        }
    }
    public float SFXMaster {
        get { if(sfx == null) return 0f; return SFXMasterVolume; }
        set {
            if(sfx == null) return;
            if(Mathf.Approximately(SFXMasterVolume,0f)) { for(int i = 0; i < sfx.Length; i++) { sfx[i].volume = value*sfxVolume[i]; } return; }
            for(int i = 0; i < sfx.Length; i++) {
                sfx[i].volume = sfxVolume[i]*value;
            }
            SFXMasterVolume = value;
        }
    }
    public AudioMixerGroup MixerBGM;
    public AudioMixerGroup MixerSFX;
    public int SFXChannels=1;
    public AudioEntry[] BGMList;
    public AudioEntry[] SFXList;
    Stack<string> stack;
    float trackVolume;
    float[] sfxVolume;

    void OnEnable() {
        bgm = gameObject.AddComponent<AudioSource>() as AudioSource;
        bgm.loop = true;
        bgm.outputAudioMixerGroup = MixerBGM;
        sfx = new AudioSource[SFXChannels];
        sfxVolume = new float[SFXChannels];
        for(int i = 0; i < SFXChannels; i++) {
            sfx[i] = gameObject.AddComponent<AudioSource>() as AudioSource;
            sfx[i].loop = false;
            sfx[i].outputAudioMixerGroup = MixerSFX;
            sfxVolume[i] = 1f;
        }
        stack = new Stack<string>();
    }

    string PrevBGM{
        get{
            string ret = "";
            if(stack.Count <= 1)
                return ret;
            string aux = stack.Pop();
            ret = stack.Peek();
            stack.Push(aux);
            return ret;
        }
    }

    public void PlayBGM(string song,float crossfade = 0f) {
        if(stack.Count==0 || song != stack.Peek()) {
            LoadBGM(song,crossfade);
            stack.Clear();
            stack.Push(song);
        }
    }

    public void PushBGM(string song,float crossfade = 0f) {
        if(stack.Count==0 || song != stack.Peek()) {
            LoadBGM(song,crossfade);
            stack.Push(song);
        }
    }

    public void PopBGM(float crossfade=0f) {
        if(stack.Count == 0)
            return;
        stack.Pop();
        if(stack.Count == 0) {
            bgm.clip = new AudioClip { };
            bgm.Play();
            return;
        }
        LoadBGM(stack.Peek(),crossfade);
    }

    void LoadBGM(string song,float crossfade) {
        AudioClip clip = null;
        float volume = bgm.volume, pitch = bgm.pitch;
        bool success = false;

        for(int i = 0; i < BGMList.Length; i++) {
            if(BGMList[i] != null && BGMList[i].name == song) {
                clip = BGMList[i].clip;
                volume = BGMList[i].volume;
                pitch = BGMList[i].pitch;
                success = true;
                break;
            }
        }

        if(!success || clip == null) {
            Debug.LogError("No song with the name " + song + " found!");
            return;
        }

        trackVolume = volume;
        volume *= BGMMasterVolume;

        Debug.Log("Changing BGM from " + (PrevBGM == "" ? "none" : PrevBGM) + " to " + song + ".");
        if(crossfade <= 0f) {
            bgm.Stop();
            bgm.clip = clip;
            bgm.pitch = pitch;
            bgm.volume = volume;
            bgm.Play();
        } else {
            StartCoroutine(Crossfade(clip,pitch,volume,crossfade));
        }        
    }

    IEnumerator Crossfade(AudioClip clip,float pitch,float volume,float time) {
        float count = 0f, originalVolume = bgm.volume;
        AudioSource second = gameObject.AddComponent<AudioSource>();
        second.clip = clip;
        second.pitch = pitch;
        second.volume = 0f;
        second.loop = true;
        second.Play();
        while(count < time) {
            count += Time.deltaTime;
            bgm.volume = Mathf.SmoothStep(0f,originalVolume,1f - (2f * count / time));
            second.volume = Mathf.SmoothStep(0f,volume,2f * count / time);
            yield return null;
        }
        bgm.Stop();
        Destroy(bgm);
        bgm = second;
        bgm.volume = volume;
    }

    public void PauseBGM(bool canUnpause = false) {
        if(!bgm.isPlaying && canUnpause) {
            bgm.UnPause();
        } else {
            bgm.Pause();
        }
    }

    public void PlaySFX(string sound, int channel=0) {
        LoadSFX(sound,channel);
    }

    void LoadSFX(string sound,int channel) {
        AudioClip clip = null;
        float volume = sfx[channel].volume, pitch = sfx[channel].pitch;
        bool success = false;

        for(int i = 0; i < SFXList.Length; i++) {
            if(SFXList[i] != null && SFXList[i].name == sound) {
                clip = SFXList[i].clip;
                volume = SFXList[i].volume;
                pitch = SFXList[i].pitch;
                success = true;
                break;
            }
        }

        if(!success || clip == null) {
            Debug.LogError("No sound with the name " + sound + " found!");
            return;
        }

        sfxVolume[channel] = volume;
        volume *= SFXMasterVolume;

        sfx[channel].pitch = pitch;
        sfx[channel].volume = volume;
        sfx[channel].PlayOneShot(clip);
    }
}

