using UnityEngine;
using System;
using System.IO;
using AX.AudioSystem;

[RequireComponent(typeof(AudioSource))]
public class AudioPlayer : MonoBehaviour
{
    [Header("音频采样率")]
    public int SamplingRate = 8000;
    [Header("音频声道数,默认为单声道")]
    public int Channels = 1;
    [Header("音频录制最大时长,按秒计")]
    public int RecordMaxTime = 60;

    /// <summary>
    /// 表示当前播放器是否正在播放。
    /// </summary>
    public bool IsPlaying { get { return !stopped && audioSource.isPlaying; } }

    /// <summary>
    /// 获得播放列表。
    /// </summary>
    public PlayList PlayList { get { return playList; } }

    /// <summary>
    /// 获得当前已经播放到播放列表中哪一条曲目了。
    /// </summary>
    public int PlayIndex
    {
        get { return playIndex; }
        set
        {
            if (value < -1 || value >= playList.Count)
                throw new InvalidOperationException("value");
            playIndex = value;
        }
    }

    /// <summary>
    /// 表示要开始播放某曲目前的事件。
    /// </summary>
    public event Action<int, Track> Playing;
    /// <summary>
    /// 表示播放某曲目结束后的事件。
    /// </summary>
    public event Action<int, Track> Played;

    private AudioSource audioSource;
    private OpusDecoder opusDecoder;
    private BetterList<float> audioBuffer;
    private BetterList<byte> opusBuffer;
    private float[] packageBuffer;
    private byte[] frameBuffer;

    private PlayList playList;
    private int playIndex;
    private bool stopped;

    private const int BufferSize = 8192;
    private byte[] buffer = new byte[BufferSize];

    void Awake()
    {
        Initialize();
    }

    void OnDisable()
    {
        Stop();
    }

    void OnDestroy()
    {
        if (opusDecoder != null)
        {
            opusDecoder.Dispose();
            opusDecoder = null;
        }
    }

    void Update()
    {
        // 自动播放列表中的语音消息
        if (!stopped && playList.Count > 0)
        {
            // 已经全部播放过
            if (playIndex != -1 && playIndex == playList.Count - 1 && playList[playIndex].Played && !playList[playIndex].Playing)
                return;

            // 正在播放
            if (IsPlaying)
                return;

            // 当前项播放完毕
            var curTrack = playList[playIndex];
            curTrack.Playing = false;
            playList[playIndex] = curTrack;

            OnPlayed(playIndex, curTrack);

            // 准备播放下一条
            var nextIndex = playIndex + 1;

            if (nextIndex != playList.Count)
            {
                var track = playList[nextIndex];

                if (!track.Played)
                {
                    playIndex = nextIndex;
                    track.Played = true;
                    track.Playing = true;

                    playList[nextIndex] = track;

                    PlayAudio(playList[nextIndex].Filename);
                }
            }
        }
    }

    /// <summary>
    /// 立即播放指定的音频文件,即使该播放器正在播放其它音频文件。
    /// </summary>
    public void Play(string audioFile)
    {
        var index = playList.IndexOf(audioFile);

        if (index == -1)
        {
            playList.Add(audioFile, true, true);
            playIndex = playList.Count - 1;
        }
        else
        {
            playIndex = index;

            var track = playList[playIndex];
            track.Played = true;
            track.Playing = true;
            playList[playIndex] = track;
        }

        if (IsPlaying)
            Stop();

        stopped = false;

        PlayAudio(audioFile);
    }

    /// <summary>
    /// 排队播放音频文件。
    /// </summary>
    public void PlayScheduled(string audioFile)
    {
        var index = playList.IndexOf(audioFile);

        if (index == -1)
            playList.Add(audioFile, false, false);

        stopped = false;
    }

    /// <summary>
    /// 播放音频。
    /// </summary>
    public void Play()
    {
        stopped = false;
    }

    public void Stop()
    {
        if (opusDecoder != null)
        {
            audioSource.Stop();

            if (audioSource.clip != null)
            {
                Destroy(audioSource.clip);
                audioSource.clip = null;
            }

            if (playIndex != -1)
            {
                var track = playList[playIndex];
                track.Playing = false;
                playList[playIndex] = track;
            }
        }

        stopped = true;
    }

    private void Initialize()
    {
        if (opusDecoder == null)
        {
            audioSource = GetComponent<AudioSource>();

            audioBuffer = new BetterList<float>(RecordMaxTime * SamplingRate * Channels + PromptTone.Data.Length);
            opusBuffer = new BetterList<byte>(200 * 1024);
            packageBuffer = new float[OpusDecoder.FrameMaxSize * Channels];
            opusDecoder = new OpusDecoder((SamplingRate)SamplingRate, (Channel)Channels);

            playList = new PlayList();
            playIndex = -1;
        }
    }

    private void PlayAudio(string audioFile)
    {
        OnPlaying(playIndex, playList[playIndex]);

        // 先从文件读取数据
        using (var stream = new FileStream(audioFile, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize))
        {
            var readLength = 0;

            while ((readLength = stream.Read(buffer, 0, BufferSize)) > 0)
                opusBuffer.AddRange(buffer, 0, readLength);
        }

        // 解析数据并解码音频
        var length = 0;

        for (var i = 0; i < opusBuffer.Count; ++i)
        {
            length = opusBuffer[i];

            if (length == 0)
                continue;

            if (frameBuffer == null || length != frameBuffer.Length)
                frameBuffer = new byte[length];

            opusBuffer.CopyTo(i + 1, frameBuffer, 0, length);

            var decoded = opusDecoder.Decode(frameBuffer, packageBuffer);

            audioBuffer.AddRange(packageBuffer, 0, decoded);

            i += length;
        }

        var data = audioBuffer.ToArray();

        audioBuffer.Clear();
        opusBuffer.Clear();

        var clip = AudioClip.Create("VoicePlayback", data.Length, Channels, SamplingRate, false);
        clip.SetData(data, 0);

        audioSource.clip = clip;
        audioSource.Play();
    }

    public void Clear()
    {
        Stop();
        PlayList.Clear();
        playIndex = -1;
    }

    protected virtual void OnPlaying(int index, Track track)
    {
        if (Playing != null)
            Playing(index, track);
    }

    protected virtual void OnPlayed(int index, Track track)
    {
        if (Played != null)
            Played(index, track);
    }
}