using SharpDX.Direct2D1;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using VIZ.Framework.Core;
using VIZ.Framework.Domain;
using VIZ.Framework.Storage;
using log4net;

namespace VIZ.Framework.Common
{
    /// <summary>
    /// 视频自定义渲染
    /// ---------------------------------------------
    /// 1. 视频帧渲染
    /// 2. 矩形框信息渲染
    /// 3. 渲染帧至图片
    /// ---------------------------------------------
    /// </summary>
    public class VideoCustomRender : D2dControl
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static readonly ILog log = LogManager.GetLogger(typeof(VideoCustomRender));

        // ========================================================================================================
        // === Property ===
        // ========================================================================================================

        /// <summary>
        /// 是否启用视频帧渲染
        /// </summary>
        public bool IsRenderVideoFrameEnabled { get; set; } = true;

        /// <summary>
        /// 背景颜色
        /// </summary>
        private readonly RawColor4 BackgroundColor = ApplicationDomain.IniStorage.GetValue<VideoConfig, RawColor4>(p => p.VIDEO_BACKGROUND_COLOR);

        // ========================================================================================================

        /// <summary>
        /// 所属渲染控件
        /// </summary>
        internal VideoControl OwnerVideoControl { get; set; }

        /// <summary>
        /// 待渲染信息
        /// </summary>
        internal volatile VideoRenderInfo RenderInfo;

        /// <summary>
        /// 插件集合
        /// </summary>
        internal List<IVideoPlugin> Plugins = new List<IVideoPlugin>();

        /// <summary>
        /// 渲染锁对象
        /// </summary>
        private object render_lock_object = new object();

        /// <summary>
        ///  DirectWrite 工厂
        /// </summary>
        private SharpDX.DirectWrite.Factory directWriteFactory = new SharpDX.DirectWrite.Factory(SharpDX.DirectWrite.FactoryType.Isolated);

        // ========================================================================================================
        // === Public Function ===
        // ========================================================================================================

        /// <summary>
        /// 更新视频帧
        /// </summary>
        /// <param name="videoFrame">视频帧</param>
        public void UpdateVideoFrame(IVideoFrame videoFrame)
        {
            if (videoFrame == null)
            {
                this.RenderInfo = null;
                return;
            }

            // ----------------------------------------------------------------------------------------
            // 计算绘制区域与大小
            ImageHelper.AdjustSize((int)this.ActualWidth, (int)this.ActualHeight, videoFrame.Width, videoFrame.Height, out int width, out int height);

            int x = (int)((this.ActualWidth - width) / 2);
            int y = (int)((this.ActualHeight - height) / 2);

            // ----------------------------------------------------------------------------------------
            // 绘制信息
            VideoRenderInfo info = new VideoRenderInfo();
            info.Frame = videoFrame;
            info.ControlRect = new Rect(0, 0, this.ActualWidth, this.ActualHeight);
            info.DrawingRect = new Rect(x, y, width, height);

            lock (this.render_lock_object)
            {
                this.RenderInfo?.Dispose();
                this.RenderInfo = info;
            }
        }

        // ========================================================================================================

        /// <summary>
        /// 渲染
        /// </summary>
        /// <param name="target">渲染目标</param>
        public override void Render(RenderTarget target)
        {
            VideoRenderContext context = new VideoRenderContext();
            context.Target = target;
            context.Mode = VideoRenderMode.UI;
            context.DirectWriteFactory = this.directWriteFactory;

            this.Render(context);
        }

        /// <summary>
        /// 渲染
        /// </summary>
        /// <param name="context">渲染上下文</param>
        public void Render(VideoRenderContext context)
        {
            lock (this.render_lock_object)
            {
                context.Target.Clear(this.BackgroundColor);

                VideoRenderInfo renderInfo = this.RenderInfo;
                if (renderInfo == null)
                    return;

                context.VideoRenderInfo = renderInfo;

                // 绘制视频
                if (this.IsRenderVideoFrameEnabled)
                {
                    this.RenderVideoFrame(context);
                }

                // 插件渲染
                foreach (IVideoPlugin plugin in this.Plugins)
                {
                    if (!plugin.IsEnabled)
                        continue;

                    if (context.Mode == VideoRenderMode.Image && (context.RenderImagePlugins == null || !context.RenderImagePlugins.Contains(plugin.Name)))
                        continue;

                    plugin.Render(context);
                }

                // 渲染未裁切区域
                this.RenderUncutArea(context);
            }
        }

        /// <summary>
        /// 绘制视频
        /// </summary>
        /// <param name="context">渲染上下文</param>
        private void RenderVideoFrame(VideoRenderContext context)
        {
            BitmapProperties properties = new BitmapProperties(new SharpDX.Direct2D1.PixelFormat(Format.B8G8R8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied));
            SharpDX.Size2 size = new SharpDX.Size2(context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height);
            SharpDX.Direct2D1.Bitmap bmp = new SharpDX.Direct2D1.Bitmap(context.Target, size, context.VideoRenderInfo.Frame.DataStream, context.VideoRenderInfo.Frame.Width * 4, properties);

            if (context.Mode == VideoRenderMode.UI)
            {
                context.Target.DrawBitmap(bmp, context.VideoRenderInfo.DrawingRect.ToRawRectangleF(), 1f, BitmapInterpolationMode.Linear, null);
            }
            else
            {
                SharpDX.Mathematics.Interop.RawRectangleF rect = new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height);
                context.Target.DrawBitmap(bmp, rect, 1f, BitmapInterpolationMode.Linear, null);
            }

            bmp.Dispose();
        }

        /// <summary>
        /// 渲染未裁切区域
        /// </summary>
        /// <param name="context">渲染上下文</param>
        private void RenderUncutArea(VideoRenderContext context)
        {
            SolidColorBrush brush = new SolidColorBrush(context.Target, this.BackgroundColor);

            float c_width = (float)context.VideoRenderInfo.ControlRect.Width;
            float c_height = (float)context.VideoRenderInfo.ControlRect.Height;
            float d_left = (float)context.VideoRenderInfo.DrawingRect.Left;
            float d_top = (float)context.VideoRenderInfo.DrawingRect.Top;
            float d_right = (float)context.VideoRenderInfo.DrawingRect.Right;
            float d_bottom = (float)context.VideoRenderInfo.DrawingRect.Bottom;

            // left 
            if (d_left > 0)
            {
                RawRectangleF left = new RawRectangleF(0, 0, d_left, c_height);
                context.Target.FillRectangle(left, brush);
            }
            // right
            if (d_right < c_width)
            {
                RawRectangleF right = new RawRectangleF(d_right, 0, c_width, c_height);
                context.Target.FillRectangle(right, brush);
            }
            // top
            if (d_top > 0)
            {
                RawRectangleF top = new RawRectangleF(d_left, 0, d_right, d_top);
                context.Target.FillRectangle(top, brush);
            }
            // bottom
            if (d_bottom < c_height)
            {
                RawRectangleF bottom = new RawRectangleF(d_left, d_bottom, d_right, c_height);
                context.Target.FillRectangle(bottom, brush);
            }
        }
    }
}
