using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using log4net;

namespace VIZ.Framework.Common
{
    /// <summary>
    /// 视频控件对齐队列
    /// </summary>
    /// <remarks>
    /// 处理 VideoFrameQueue 与 DataFrameQueue 队列的帧对齐
    /// </remarks>
    public class VideoControlSyncQueue : IDisposable
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static readonly ILog log = LogManager.GetLogger(typeof(VideoControlSyncQueue));

        /// <summary>
        /// 最大队列长度
        /// </summary>
        /// <remarks>
        /// 40ms * 50 = 2s
        /// </remarks>
        public int MaxQueueCount { get; set; } = 50;

        /// <summary>
        /// 帧同步时触发
        /// </summary>
        public event EventHandler<VideoControlSyncEventArgs> OnFrameSync;

        /// <summary>
        /// 视频帧队列
        /// </summary>
        private ConcurrentQueue<IVideoControlSync> VideoFrameQueue = new ConcurrentQueue<IVideoControlSync>();

        /// <summary>
        /// 数据帧队列
        /// </summary>
        private ConcurrentQueue<IVideoControlSync> DataFrameQueue = new ConcurrentQueue<IVideoControlSync>();

        /// <summary>
        /// 视频帧待处理数据
        /// </summary>
        private volatile IVideoControlSync VideoFrame;

        /// <summary>
        /// 数据帧待处理数据
        /// </summary>
        private volatile IVideoControlSync DataFrame;

        /// <summary>
        /// 销毁
        /// </summary>
        public void Dispose()
        {
            this.VideoFrameQueue = null;
            this.DataFrameQueue = null;
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="videoFrame">视频帧</param>
        public void AppendVideoFrame(IVideoControlSync videoFrame)
        {
            this.VideoFrameQueue.Enqueue(videoFrame);
        }

        /// <summary>
        /// 添加数据帧
        /// </summary>
        /// <param name="dataFrame">数据帧</param>
        public void AppendDataFrame(IVideoControlSync dataFrame)
        {
            this.DataFrameQueue.Enqueue(dataFrame);
        }

        /// <summary>
        /// 执行不需要对齐并且处理视频帧
        /// </summary>
        public void ExecuteVideo()
        {
            IVideoControlSync video = this.VideoFrame;

            if (this.VideoFrame == null)
            {
                this.VideoFrameQueue.TryDequeue(out video);
            }

            if (video == null)
                return;

            this.triggerEvent(video, null);
            this.VideoFrame = null;
        }

        /// <summary>
        /// 执行对齐
        /// </summary>
        public void ExecuteSync()
        {
            try
            {
                while (true)
                {
                    IVideoControlSync video = this.VideoFrame;
                    IVideoControlSync data = this.DataFrame;

                    if (this.VideoFrame == null)
                    {
                        this.VideoFrameQueue.TryDequeue(out video);
                    }

                    if (this.DataFrame == null)
                    {
                        this.DataFrameQueue.TryDequeue(out data);
                    }

                    int videoQueueCount = this.VideoFrameQueue.Count;

                    // 没有视频帧时不处理
                    if (video == null)
                        return;

                    // 有视频帧 没有数据帧
                    if (data == null)
                    {
                        // 如果视频帧队列在等待队列长度范围内，那么不处理
                        if (videoQueueCount < this.MaxQueueCount)
                            return;

                        // 处理数据
                        this.triggerEvent(video, null);
                        this.VideoFrame = null;

                        return;
                    }

                    // 如果视频帧的时间戳小于数据的时间戳
                    if (video.TimeStamp < data.TimeStamp)
                    {
                        this.triggerEvent(video, null);
                        this.VideoFrame = null;

                        return;
                    }

                    // 如果视频帧的时间等于数据帧时间那么正常处理
                    if (video.TimeStamp == data.TimeStamp)
                    {
                        // 处理数据
                        this.triggerEvent(video, data);
                        this.VideoFrame = null;
                        this.DataFrame = null;

                        return;
                    }

                    // 抛弃该数据帧
                    this.DataFrame = null;
                }
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
        }

        /// <summary>
        /// 触发事件
        /// </summary>
        /// <param name="videoFrame">视频帧</param>
        /// <param name="dataFrame">数据帧</param>
        private void triggerEvent(IVideoControlSync videoFrame, IVideoControlSync dataFrame)
        {
            try
            {
                if (this.OnFrameSync == null)
                    return;

                VideoControlSyncEventArgs args = new VideoControlSyncEventArgs();
                args.VideoFrame = videoFrame;
                args.DataFrame = dataFrame;

                this.OnFrameSync.Invoke(this, args);
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
        }
    }
}
