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

namespace VIZ.Framework.Common
{
    /// <summary>
    /// OpenCv流
    /// </summary>
    public class OpenCVStream : VideoStreamBase<OpenCVStreamOption>
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(OpenCVStream));

        /// <summary>
        /// OpenCv流
        /// </summary>
        /// <param name="option">设置</param>
        public OpenCVStream(OpenCVStreamOption option) : base(option)
        {
            this.TaskDic.Add(OpenCVStreamTaskNames.RECV_VIDEO, new OpenCVStreamRecvVideoTask(this));
            this.TaskDic.Add(OpenCVStreamTaskNames.EXECUTE_VIDEO, new OpenCVStreamExecuteVideoTask(this));
        }

        /* ========================================================================================================= */
        /* === Property === */
        /* ========================================================================================================= */

        /// <summary>
        /// 地址
        /// </summary>
        public string Uri { get; private set; }

        /// <summary>
        /// 属性信息
        /// </summary>
        public OpenCVPropertyInfos PropertyInfos { get; private set; }

        /// <summary>
        /// 状态
        /// </summary>
        public OpenCVStreamStatus Status { get; private set; }

        /* ========================================================================================================= */
        /* === Internal Field === */
        /* ========================================================================================================= */

        /// <summary>
        /// 视频捕获
        /// </summary>
        internal VideoCapture VideoCapture;

        /// <summary>
        /// 读取帧锁对象
        /// </summary>
        internal object read_frame_lock_object = new object();

        /* ========================================================================================================= */
        /* === Field === */
        /* ========================================================================================================= */

        /// <summary>
        /// 任务池
        /// </summary>
        private Dictionary<OpenCVStreamTaskNames, OpenCVStreamTaskBase> TaskDic = new Dictionary<OpenCVStreamTaskNames, OpenCVStreamTaskBase>();

        /* ========================================================================================================= */
        /* === Function === */
        /* ========================================================================================================= */

        /// <summary>
        /// 播放
        /// </summary>
        public void Play()
        {
            if (this.Status == OpenCVStreamStatus.None || this.Status == OpenCVStreamStatus.Play)
                return;

            double frame = this.VideoCapture.Get(VideoCaptureProperties.PosFrames);
            if (frame >= this.PropertyInfos.FrameCount)
            {
                this.Stop();
            }

            foreach (var task in this.TaskDic.Values)
            {
                task.Start();
            }

            this.Status = OpenCVStreamStatus.Play;
        }

        /// <summary>
        /// 暂停
        /// </summary>
        public void Pause()
        {
            if (this.Status == OpenCVStreamStatus.None || this.Status == OpenCVStreamStatus.Stop)
                return;

            foreach (var task in this.TaskDic.Values)
            {
                task.Stop();
            }

            this.Status = OpenCVStreamStatus.Pause;
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            if (this.Status == OpenCVStreamStatus.None)
                return;

            foreach (var task in this.TaskDic.Values)
            {
                task.Stop();
            }

            this.VideoCapture.Set(VideoCaptureProperties.PosFrames, 0);

            this.TriggerExecuteBlackVideoFrame();

            this.Status = OpenCVStreamStatus.Stop;
        }

        /// <summary>
        /// 设置位置
        /// </summary>
        /// <param name="position">帧位置</param>
        public void SetPosition(int position)
        {
            if (this.Status == OpenCVStreamStatus.None)
                return;

            lock (this.read_frame_lock_object)
            {
                this.VideoCapture.Set(VideoCaptureProperties.PosFrames, position);

                while (this.VideoFrameQueue.Count > 0)
                {
                    if (this.VideoFrameQueue.TryDequeue(out IVideoFrame frame))
                    {
                        frame.Dispose();
                    }
                }
            }
        }

        /// <summary>
        /// 切换源
        /// </summary>
        /// <param name="uri">源</param>
        public void ChangeUri(string uri)
        {
            this.Stop();

            this.Uri = uri;
            this.VideoCapture?.Dispose();
            this.VideoCapture = null;

            if (string.IsNullOrWhiteSpace(uri))
            {
                this.Status = OpenCVStreamStatus.None;
                this.PropertyInfos = null;
                return;
            }

            try
            {
                this.VideoCapture = new VideoCapture(this.Uri);
                this.InitPropertyInfos();
            }
            catch (Exception ex)
            {
                log.Error(ex);

                this.Status = OpenCVStreamStatus.None;
                this.PropertyInfos = null;
            }

            this.Status = OpenCVStreamStatus.Stop;
        }

        /// <summary>
        /// 触发执行黑色帧
        /// </summary>
        public void TriggerExecuteBlackVideoFrame()
        {
            OpenCVStreeamVideoFrame frame = new OpenCVStreeamVideoFrame(Scalar.Black);
            this.TriggerExecuteVideoFrame(frame);
        }

        /// <summary>
        /// 触发执行第一帧
        /// </summary>
        public void TriggerExecuteCurrentPosVideoFrame()
        {
            if (this.Status == OpenCVStreamStatus.None)
                return;

            OpenCVStreeamVideoFrame videoFrame = new OpenCVStreeamVideoFrame();

            lock (this.read_frame_lock_object)
            {
                Mat src = new Mat();
                Mat mat = new Mat();

                if (!this.VideoCapture.Read(src))
                {
                    src.Dispose();
                    return;
                }
                Cv2.CvtColor(src, mat, ColorConversionCodes.BGR2BGRA);

                videoFrame.Width = mat.Width;
                videoFrame.Height = mat.Height;
                videoFrame.Length = mat.Width * mat.Height * 4;
                videoFrame.TimeStamp = TimeSpan.FromMilliseconds(this.VideoCapture.PosMsec).Ticks;
                videoFrame.DataStream = new SharpDX.DataStream(videoFrame.Length, true, true);
                unsafe
                {
                    Buffer.MemoryCopy(mat.DataPointer, videoFrame.DataStream.DataPointer.ToPointer(), videoFrame.Length, videoFrame.Length);
                }

                src.Dispose();
                mat.Dispose();
            }

            this.TriggerExecuteVideoFrame(videoFrame);
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public override void Dispose()
        {
            foreach (var task in this.TaskDic.Values)
            {
                task.Dispose();
            }

            this.VideoCapture?.Dispose();
        }

        /* ========================================================================================================= */
        /* === Internal Function === */
        /* ========================================================================================================= */

        /* ========================================================================================================= */
        /* === Private Function === */
        /* ========================================================================================================= */

        /// <summary>
        /// 初始化属性信息
        /// </summary>
        private void InitPropertyInfos()
        {
            OpenCVPropertyInfos infos = new OpenCVPropertyInfos();

            // FPS
            infos.Fps = this.VideoCapture.Get(VideoCaptureProperties.Fps);
            // 帧宽度
            infos.FrameWidth = this.VideoCapture.Get(VideoCaptureProperties.FrameWidth);
            // 帧高度
            infos.FrameHeight = this.VideoCapture.Get(VideoCaptureProperties.FrameHeight);
            // 帧数量
            infos.FrameCount = this.VideoCapture.Get(VideoCaptureProperties.FrameCount);

            this.PropertyInfos = infos;
        }

    }
}
