using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using yoseen = YoseenSDKCS;

namespace YoseenDemoCustomDraw
{
    public class IRVideoView : UserControl
    {
        readonly IRVideoViewD2D _d2d;

        public IRVideoView()
        {
            _d2d = new IRVideoViewD2D();
            this.BackColor = Color.Black;

            _previewInfo.CustomCallback = onFrame;
            _previewInfo.DataType = (int)yoseen.xxxdatatype.video;
            onFrameUI_act = new Action<yoseen.DataFrame>(onFrameUI);

            this.SizeChanged += IRVideoView_SizeChanged;
            this.DoubleBuffered = true;
        }

        private void IRVideoView_SizeChanged(object sender, EventArgs e)
        {
            _d2d.InitCanvasWH(this.Width, this.Height);
        }

        #region update
        void onFrame(int errorCode, ref yoseen.DataFrame dataFrame, IntPtr customData)
        {
            if (0 == errorCode)
            {
                this.BeginInvoke(onFrameUI_act, dataFrame);
            }
            else
            {

            }
        }

        readonly Action<yoseen.DataFrame> onFrameUI_act;
        void onFrameUI(yoseen.DataFrame dataFrame)
        {
            if (!_isWorking) return;

            Bitmap bmp = _d2d.Bmp;
            BitmapData bmpData = bmp.LockBits(_d2d.BmpSrcRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
            yoseen.Win32Helper.memcpy(bmpData.Scan0, dataFrame.Bmp, _d2d.BmpDataSize);
            bmp.UnlockBits(bmpData);
            _mouseResult = (yoseen.xxxmouseresult)Marshal.PtrToStructure(dataFrame.H264 + 424, typeof(yoseen.xxxmouseresult));
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Graphics g = e.Graphics;
            if (_isWorking)
            {
                //draw basic
                Bitmap bmp = _d2d.Bmp;
                g.DrawImage(bmp, _d2d.BmpDstRect);

                //draw mouse
                if (_mouseEnable)
                {
                    int i;
                    for (i = 0; i < 8; i += 2)
                    {
                        g.DrawLine(IRVideoViewD2D.ConstPen, _mouseCross[i], _mouseCross[i + 1]);
                    }
                    string s = _mouseResult.temp.ToString("F1");
                    //string s = string.Format("{0}, {1}, {2:F1}", _mouseResult.x, _mouseResult.y, _mouseResult.temp);
                    g.DrawString(s, IRVideoViewD2D.ConstFont, IRVideoViewD2D.ConstBrush, _mouseTextPos);
                }

            }
            else
            {
                g.Clear(Color.Black);
            }
        }

        #endregion

        #region mouse
        const float ConstMouseCross_Radius1 = 8;
        const float ConstMouseCross_Radius2 = 24;
        const float ConstMouseText_Delta = 10;
        const float ConstMouseText_Width = 50;
        const float ConstMouseText_Height = 30;

        DateTime _mouseLastTime;
        TimeSpan _mouseInterval = TimeSpan.FromMilliseconds(200);
        bool _mouseEnable;
        yoseen.CtlX _ctlx;

        int _mouseX, _mouseY;
        PointF[] _mouseCross = new PointF[8];
        PointF _mouseTextPos;

        yoseen.xxxmouseresult _mouseResult;

        void sendMouseXY(float x, float y)
        {
            Task.Factory.StartNew(() =>
            {
                int ret;
                _ctlx.Type = yoseen.CtlXType.CtlXType_SetMouseMea;
                _ctlx.MouseMea.x = x;
                _ctlx.MouseMea.y = y;
                ret = yoseen.YoseenSDK.Yoseen_SendControlX(_userHandle, ref _ctlx);
                return ret;
            });
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (!_isWorking) return;

            _mouseX = e.X;
            _mouseY = e.Y;
            DateTime dt = DateTime.Now;
            if (dt - _mouseLastTime < _mouseInterval) return;
            _mouseLastTime = dt;
            float w = _d2d.CanvasWidth;
            float h = _d2d.CanvasHeight;
            float x = _mouseX;
            float y = _mouseY;

            //
            _mouseCross[0].X = x - ConstMouseCross_Radius2; _mouseCross[0].Y = y;
            _mouseCross[1].X = x - ConstMouseCross_Radius1; _mouseCross[1].Y = y;

            _mouseCross[2].X = x + ConstMouseCross_Radius2; _mouseCross[2].Y = y;
            _mouseCross[3].X = x + ConstMouseCross_Radius1; _mouseCross[3].Y = y;

            _mouseCross[4].X = x; _mouseCross[4].Y = y - ConstMouseCross_Radius2;
            _mouseCross[5].X = x; _mouseCross[5].Y = y - ConstMouseCross_Radius1;

            _mouseCross[6].X = x; _mouseCross[6].Y = y + ConstMouseCross_Radius2;
            _mouseCross[7].X = x; _mouseCross[7].Y = y + ConstMouseCross_Radius1;

            //
            float tx = x + ConstMouseText_Delta;
            if (tx + ConstMouseText_Width > w) tx = x - ConstMouseText_Delta - ConstMouseText_Width;
            float ty = y + ConstMouseText_Delta;
            if(ty+ConstMouseText_Height>h)ty=y -ConstMouseText_Delta - ConstMouseText_Height;
            _mouseTextPos.X = tx;
            _mouseTextPos.Y = ty;

            //
            x = x / w; y = y / h;
            sendMouseXY(x, y);
            //Console.WriteLine($"x {x}, y {y}");
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            _mouseEnable = false;
            Cursor.Show();
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            _mouseEnable = true;
            Cursor.Hide();
        }
        #endregion


        yoseen.CameraBasicInfo _basicInfo;
        yoseen.YoseenLoginInfo _loginInfo;
        yoseen.YoseenPreviewInfo _previewInfo;

        int _userHandle = -1;
        int _previewHandle = -1;
        bool _isWorking = false;
        public bool IsWorking => _isWorking;
        bool _isSaving = false;
        public bool IsSaving => _isSaving;

        public void SetConfig(string ip)
        {
            _loginInfo.CameraAddr = ip;
        }

        public int StartWork()
        {
            if (_isWorking) return yoseen.EError.EError_InvalidState;

            //
            int ret;
            ret = yoseen.YoseenSDK.Yoseen_Login(ref _loginInfo, ref _basicInfo);
            if (0 > ret) return ret;
            _userHandle = ret;
            ret = yoseen.YoseenSDK.Yoseen_StartPreview(_userHandle, ref _previewInfo);
            if (0 > ret) goto error1;
            _previewHandle = ret;

            //
            _d2d.InitDataWH(_previewInfo.OutputWidth, _previewInfo.OutputHeight);
            _isWorking = true;
            _isSaving = false;
            return 0;

            error1:
            if (0 <= _userHandle)
            {
                yoseen.YoseenSDK.Yoseen_Logout(_userHandle);
            }
            return ret;
        }

        public void StopWork()
        {
            if (!_isWorking) return;

            _isWorking = false;
            _isSaving = false;
            yoseen.YoseenSDK.Yoseen_StopPreview(_previewHandle); _previewHandle = -1;
            yoseen.YoseenSDK.Yoseen_Logout(_userHandle); _userHandle = -1;
            this.Invalidate();
        }

        public int SaveFrame(string fn)
        {
            if (!_isWorking) return (int)yoseen.EError.EError_InvalidState;
            int ret;
            ret = yoseen.YoseenSDK.Yoseen_SaveFrame(_userHandle, fn, 1);
            return ret;
        }

        public int BeginSave(string fn)
        {
            if (!(_isWorking && !_isSaving)) return yoseen.EError.EError_InvalidState;

            int ret;
            ret = yoseen.YoseenSDK.Yoseen_BeginSave(_previewHandle, fn, (int)yoseen.xxxmediafile.mp4);
            if (0 == ret)
            {
                _isSaving = true;
            }
            return ret;
        }

        public void EndSave()
        {
            if (!_isSaving) return;
            int ret;
            ret = yoseen.YoseenSDK.Yoseen_EndSave(_previewHandle, (int)yoseen.xxxmediafile.mp4);
            _isSaving = false;
        }
    }

    public class IRVideoViewD2D
    {
        //
        public static Font ConstFont = new Font("Times New Roman", 24);
        public static Pen ConstPen = new Pen(Brushes.Red, 2);
        public static Brush ConstBrush = new SolidBrush(Color.Red);

        //
        public int DataWidth;
        public int DataHeight;

        public int CanvasWidth;
        public int CanvasHeight;

        public Bitmap Bmp;
        public Rectangle BmpSrcRect;
        public Rectangle BmpDstRect;
        public int BmpDataSize;

        public IRVideoViewD2D()
        {

        }

        public void InitDataWH(int dataWidth, int dataHeight)
        {
            if (DataWidth == dataWidth && DataHeight == dataHeight) return;

            DataWidth = dataWidth;
            DataHeight = dataHeight;
            Bmp = new Bitmap(dataWidth, dataHeight, PixelFormat.Format32bppRgb);
            BmpSrcRect.Width = dataWidth; BmpSrcRect.Height = dataHeight;
            BmpDataSize = dataWidth * dataHeight * 4;
        }

        public void InitCanvasWH(int canvasWidth, int canvasHeight)
        {
            CanvasWidth = canvasWidth;
            CanvasHeight = canvasHeight;

            BmpDstRect.Width = canvasWidth;
            BmpDstRect.Height = canvasHeight;
        }
    }
}
