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

using yoseen = YoseenSDKCS;

namespace YoseenDemoCustomDraw
{
    public partial class ShellView : Form
    {
        #region 字符串常量
        const string Const_hwndShell = "YoseenDemo, CustomDraw";
        const string Const_hwndPreview = "Preview";
        const string Const_btnUrl = "Url";

        const string Const_btnLogin = "Login";
        const string Const_btnLogin2 = "Logout";
        const string Const_btnPlayVideo = "Play Video";
        const string Const_btnPlayTemp = "Play Temp";
        const string Const_btnStop = "Stop";

        const string Const_btnSaveJpgx = "Save .jpg";
        const string Const_btnSaveMp4 = "Begin Save .mp4";
        const string Const_btnSaveMp42 = "End Save .mp4";
        const string Const_btnSaveStream = "Begin Save .stream";
        const string Const_btnSaveStream2 = "End Save .stream";


        const string Const_cmbTest_SetMeasureTemplate = "Measure Template";
        const string Const_cmbTest_ManualFFC = "Manual FFC";
        const string Const_btnTest = "Test";

        void myInvoke<T>(Action<T> act, T t)
        {
            this.Invoke(act, t);
        }
        #endregion

        #region 子控件, 建立, 布局
        TempCanvas tempCanvas;

        Button btnUrl;
        TextBox tbUrl;

        Button btnLogin;
        Button btnPlayVideo;
        Button btnPlayTemp;
        Button btnStop;

        Button btnSaveJpgx;
        Button btnSaveMp4;
        Button btnSaveStream;
        ComboBox cmbTest;
        Button btnTest;

        TextBox tbEcho;

        Font _font;
        public ShellView()
        {
            InitializeComponent();

            this.SuspendLayout();

            //
            _font = new Font("Times New Roman", 9, FontStyle.Regular);
            this.Font = _font;
            this.Text = Const_hwndShell;

            //0
            tempCanvas = new TempCanvas();
            this.Controls.Add(tempCanvas);

            //1
            btnUrl = new Button();
            btnUrl.Text = Const_btnUrl;
            btnUrl.Click += btnUrl_Click;
            this.Controls.Add(btnUrl);

            tbUrl = new TextBox();
            this.Controls.Add(tbUrl);

            //2
            btnLogin = new Button();
            btnLogin.Text = Const_btnLogin;
            btnLogin.Click += btnLogin_Click;
            this.Controls.Add(btnLogin);

            btnPlayVideo = new Button();
            btnPlayVideo.Text = Const_btnPlayVideo;
            btnPlayVideo.Click += btnPlayVideo_Click;
            this.Controls.Add(btnPlayVideo);

            btnPlayTemp = new Button();
            btnPlayTemp.Text = Const_btnPlayTemp;
            btnPlayTemp.Click += btnPlayTemp_Click;
            this.Controls.Add(btnPlayTemp);

            btnStop = new Button();
            btnStop.Text = Const_btnStop;
            btnStop.Click += btnStop_Click;
            this.Controls.Add(btnStop);

            //3
            btnSaveJpgx = new Button();
            btnSaveJpgx.Text = Const_btnSaveJpgx;
            btnSaveJpgx.Click += btnSaveJpgx_Click;
            this.Controls.Add(btnSaveJpgx);

            btnSaveMp4 = new Button();
            btnSaveMp4.Text = Const_btnSaveMp4;
            btnSaveMp4.Click += btnSaveMp4_Click;
            this.Controls.Add(btnSaveMp4);

            btnSaveStream = new Button();
            btnSaveStream.Text = Const_btnSaveStream;
            btnSaveStream.Click += btnSaveStream_Click;
            this.Controls.Add(btnSaveStream);

            cmbTest = new ComboBox();
            cmbTest.Items.Add(Const_cmbTest_SetMeasureTemplate);
            cmbTest.Items.Add(Const_cmbTest_ManualFFC);
            cmbTest.SelectedIndex = 0;
            this.Controls.Add(cmbTest);

            btnTest = new Button();
            btnTest.Text = Const_btnTest;
            btnTest.Click += btnTest_Click;
            this.Controls.Add(btnTest);

            //4
            tbEcho = new TextBox();
            this.Controls.Add(tbEcho);

            //
            this.Width = 800;
            this.Height = 800;
            layout();
            this.ResumeLayout(false);

            this.Resize += ShellView_Resize;

            _previewCallback = new yoseen.YoseenPreviewCallback(_previewCallbackFunc);
            _delUpdateData = new DEL_UpdateData(_funcUpdataData);
            //
            _mtStruct = yoseen.MeasureTemplate.Create();
        }

        void setBounds(Control ctl, int x, int y, int width, int height)
        {
            int dx = width / 6;
            int dy = height / 8;
            ctl.SetBounds(x + dx, y + dy, width - 2 * dx, height - 2 * dy);
        }

        void _layoutPreview(int x, int y, int width, int height)
        {
            int w2 = _previewInfo.OutputWidth;
            int h2 = _previewInfo.OutputHeight;

            int x3 = x, y3 = y, w3 = width, h3 = height;
            if (w2 != 0 && h2 != 0)
            {
                if (width * h2 > w2 * height)
                {
                    w3 = height * w2 / h2;
                    h3 = height;
                    y3 = y;
                    x3 = x + (width - w3) / 2;
                }
                else
                {
                    w3 = width;
                    h3 = width * h2 / w2;
                    x3 = x;
                    y3 = y + (height - h3) / 2;
                }
            }

            tempCanvas.SetBounds(x3, y3, w3, h3);
        }

        void layout()
        {
            Size s = this.ClientSize;
            int width = s.Width;
            int height = s.Height;

            //
            double[] xarr = { 1, 1, 1, 1, 1 };
            double[] yarr = { 10, 1, 1, 1, 1 };
            double dx = width / 5.0;
            double dy = height / 14.0;

            //00
            int x, y;
            x = 0; y = 0; height = Convert.ToInt32(dy * yarr[0]);
            //_layoutPreview(x, y, width, height);

            //10,11
            y += height; width = Convert.ToInt32(dx * xarr[0]); height = Convert.ToInt32(dy * yarr[1]);
            setBounds(btnUrl, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * (xarr[1] + xarr[2] + xarr[3] + xarr[4]));
            tbUrl.SetBounds(x, y, width, height);

            //20,
            x = 0; y += height; width = Convert.ToInt32(dx * xarr[0]); height = Convert.ToInt32(dy * yarr[2]);
            setBounds(btnLogin, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * xarr[1]);
            setBounds(btnPlayVideo, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * xarr[2]);
            setBounds(btnPlayTemp, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * xarr[3]);
            setBounds(btnStop, x, y, width, height);

            //30
            x = 0; y += height; width = Convert.ToInt32(dx * xarr[0]); height = Convert.ToInt32(dy * yarr[3]);
            setBounds(btnSaveJpgx, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * xarr[1]);
            setBounds(btnSaveMp4, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * xarr[2]);
            setBounds(btnSaveStream, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * xarr[3]);
            setBounds(cmbTest, x, y, width, height);

            x += width; width = Convert.ToInt32(dx * xarr[4]);
            setBounds(btnTest, x, y, width, height);

            //40
            x = 0; y += height; width = this.Width; height = Convert.ToInt32(dy * yarr[4]);
            tbEcho.SetBounds(x, y, width, height);
        }

        void ShellView_Resize(object sender, EventArgs e)
        {
            layout();
        }

        #endregion

        int _userHandle = -1;
        int _previewHandle = -1;
        yoseen.YoseenLoginInfo _loginInfo;
        yoseen.CameraBasicInfo _cameraBasicInfo;
        ShellViewFlags _flags = 0;

        void btnUrl_Click(object sender, EventArgs e)
        {
            yoseen.DiscoverCameraResp2[] dcrs = yoseen.YoseenSDK.Yoseen_DiscoverCameras2(0x01);
            string s = null;
            if (dcrs.Length == 0)
            {
                s = "discover no cameras";
            }
            else
            {
                s = "discover cameras, ";
                int i = 0;
                for (; i < dcrs.Length; i++)
                {
                    s += yoseen.YoseenUtil.uint2str(dcrs[i].CameraIp) + ", ";
                }
            }
            tbEcho.Text = s;
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            if (_userHandle >= 0)
            {
                int ret = yoseen.YoseenSDK.Yoseen_Logout(_userHandle);
                _userHandle = -1;
                btnLogin.Text = Const_btnLogin;
                tbEcho.Text = string.Format("Yoseen_Logout, ret {0}", ret);
            }
            else
            {
                _loginInfo.CameraAddr = tbUrl.Text;
                //_loginInfo.Username = "admin";
                //_loginInfo.Password = "admin";
                int userHandle = yoseen.YoseenSDK.Yoseen_Login(ref _loginInfo, ref _cameraBasicInfo);
                if (userHandle >= 0)
                {
                    _userHandle = userHandle;
                    btnLogin.Text = Const_btnLogin2;
                    tempCanvas.ChangeConfig(_cameraBasicInfo.DataWidth, _cameraBasicInfo.DataHeight);
                }
                tbEcho.Text = string.Format("Yoseen_Login, ret {0}", userHandle);
            }
        }

        #region 预览
        yoseen.YoseenPreviewInfo _previewInfo;
        yoseen.YoseenPreviewCallback _previewCallback;

        DEL_UpdateData _delUpdateData;
        void _funcUpdataData(yoseen.DataFrame dataFrame)
        {
            //必须检查预览句柄
            if (_previewHandle < 0) return;
            tempCanvas.UpdateData(ref dataFrame);
        }

        void _previewCallbackFunc(int errorCode, ref yoseen.DataFrame dataFrame, IntPtr customData)
        {
            if (0 == errorCode)
            {
                //接收数据成功, 必须使用BeginInvoke或Invoke切换到主线程去绘制数据
                this.BeginInvoke(_delUpdateData, dataFrame);
            }
            else
            {
                //接收数据失败, 预览内置自动恢复, YET_PreviewRecoverBegin-YET_PreviewRecoverEnd
                myInvoke<int>((x) =>
                {
                    tbEcho.Text = "PreviewCallback, errorCode " + errorCode;
                }, errorCode);
            }
        }

        void _stopPlay()
        {
            tempCanvas.EndUpdate();

            if (_previewHandle >= 0)
            {
                int ret = yoseen.YoseenSDK.Yoseen_StopPreview(_previewHandle);
                _previewHandle = -1;
                tbEcho.Text = string.Format("Yoseen_StopPreview, ret {0}", ret);
            }
        }

        void _startPlay(int dataType)
        {
            _stopPlay();

            //
            _previewInfo.DataType = dataType;
            _previewInfo.CustomData = IntPtr.Zero;
            _previewInfo.CustomCallback = _previewCallback;
            //_previewInfo.Hwnd = hwndPreview.Handle;
            int previewHandle = yoseen.YoseenSDK.Yoseen_StartPreview(_userHandle, ref _previewInfo);
            tbEcho.Text = string.Format("Yoseen_StartPreview, ret {0}", previewHandle);
            if (previewHandle < 0)
            {
                return;
            }
            _previewHandle = previewHandle;
            tempCanvas.BeginUpdate(_cameraBasicInfo.DataWidth, _cameraBasicInfo.DataHeight);

            //
            layout();
        }

        private void btnPlayVideo_Click(object sender, EventArgs e)
        {
            _startPlay((int)yoseen.xxxdatatype.video);
        }

        private void btnPlayTemp_Click(object sender, EventArgs e)
        {
            _startPlay((int)yoseen.xxxdatatype.temp);
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            _stopPlay();
        }
        #endregion

        #region 保存
        private void btnSaveJpgx_Click(object sender, EventArgs e)
        {
            if (_userHandle < 0) return;
            string fn = string.Format("{0}\\{1:yyyyMMdd_HHmmss}.jpg", AppPaths.Data, DateTime.Now);
            int ret = yoseen.YoseenSDK.Yoseen_SaveFrame(_userHandle, fn, 1);
            tbEcho.Text = string.Format("Yoseen_SaveFrame, fn {0}, ret {1}", fn, ret);
        }

        private void btnSaveMp4_Click(object sender, EventArgs e)
        {
            if (0 > _previewHandle) return;
            int ret;
            if (_flags.HasFlag(ShellViewFlags.SaveMp4))
            {
                ret = yoseen.YoseenSDK.Yoseen_EndSave(_previewHandle, (int)yoseen.xxxmediafile.mp4);
                _flags &= (~ShellViewFlags.SaveMp4);

                btnSaveMp4.Text = Const_btnSaveMp4;
                tbEcho.Text = string.Format("Yoseen_EndSave, ret {0}", ret);
            }
            else
            {
                string fn = string.Format("{0}\\{1:yyyyMMdd_HHmmss}.mp4", AppPaths.Data, DateTime.Now);
                ret = yoseen.YoseenSDK.Yoseen_BeginSave(_previewHandle, fn, (int)yoseen.xxxmediafile.mp4);
                if (0 == ret)
                {
                    _flags |= ShellViewFlags.SaveMp4;
                    btnSaveMp4.Text = Const_btnSaveMp42;
                }
                tbEcho.Text = string.Format("Yoseen_BeginSave, fn {0}, ret {1}", fn, ret);
            }
        }

        private void btnSaveStream_Click(object sender, EventArgs e)
        {
            if (0 > _previewHandle) return;
            int ret;
            if (_flags.HasFlag(ShellViewFlags.SaveStream))
            {
                ret = yoseen.YoseenSDK.Yoseen_EndSave(_previewHandle, (int)yoseen.xxxmediafile.stream);
                _flags &= (~ShellViewFlags.SaveStream);

                btnSaveStream.Text = Const_btnSaveStream;
                tbEcho.Text = string.Format("Yoseen_EndSave, ret {0}", ret);
            }
            else
            {
                string fn = string.Format("{0}\\{1:yyyyMMdd_HHmmss}.stream", AppPaths.Data, DateTime.Now);
                ret = yoseen.YoseenSDK.Yoseen_BeginSave(_previewHandle, fn, (int)yoseen.xxxmediafile.stream);
                if (0 == ret)
                {
                    _flags |= ShellViewFlags.SaveStream;
                    btnSaveStream.Text = Const_btnSaveStream2;
                }
                tbEcho.Text = string.Format("Yoseen_BeginSave, fn {0}, ret {1}", fn, ret);
            }
        }
        #endregion

        #region 测试
        yoseen.MeasureTemplate _mtStruct;
        private void btnTest_Click(object sender, EventArgs e)
        {
            if (0 > _userHandle) return;

            int sel = cmbTest.SelectedIndex;
            int ret;
            switch (sel)
            {
                //设置测温模板, 视频流预览
                case 0:
                    {
                        _mtStruct.Head.Width = _cameraBasicInfo.DataWidth;
                        _mtStruct.Head.Height = _cameraBasicInfo.DataHeight;

                        int width = _cameraBasicInfo.DataWidth;
                        int height = _cameraBasicInfo.DataHeight;

                        //
                        ushort[] xydata = _mtStruct.ShieldArray[0].xydata;
                        xydata[0] = (ushort)(width / 8); xydata[1] = (ushort)(height / 8);
                        xydata[2] = (ushort)(width * 2 / 8); xydata[3] = (ushort)(height / 8);
                        xydata[4] = (ushort)(width * 2 / 8); xydata[5] = (ushort)(height * 2 / 8);
                        xydata[6] = (ushort)(width / 8); xydata[7] = (ushort)(height * 2 / 8);
                        _mtStruct.Head.ShieldFlags = 0x01;

                        //
                        _mtStruct.GMO.measureflags = yoseen.xxxmeasureflags.track_max | yoseen.xxxmeasureflags.plot_max;
                        _mtStruct.LMOArray[0].measureflags = yoseen.xxxmeasureflags.track_max | yoseen.xxxmeasureflags.plot_max;
                        _mtStruct.LMOArray[0].emmi = 97;
                        _mtStruct.LMOArray[0].measuretype = yoseen.xxxmeasuretype.polygon;
                        _mtStruct.LMOArray[0].points = 4;
                        xydata = _mtStruct.LMOArray[0].xydata;
                        xydata[0] = (ushort)(width * 3 / 8); xydata[1] = (ushort)(height * 4 / 8);
                        xydata[2] = (ushort)(width * 5 / 8); xydata[3] = (ushort)(height * 4 / 8);
                        xydata[4] = (ushort)(width * 5 / 8); xydata[5] = (ushort)(height * 7 / 8);
                        xydata[6] = (ushort)(width * 3 / 8); xydata[7] = (ushort)(height * 7 / 8);
                        _mtStruct.Head.LMOFlags = 0x01;

                        //
                        IntPtr ptrMT = yoseen.Win32Helper.malloc(1024 * 1024);
                        Marshal.StructureToPtr(_mtStruct, ptrMT, false);
                        ret = yoseen.YoseenSDK.Yoseen_UploadMem(_userHandle, 0, ptrMT, yoseen.MeasureTemplate.ConstSize);
                        yoseen.Win32Helper.free(ptrMT);
                        break;
                    }


                //手动挡板校零
                case 1:
                    {
                        yoseen.Ctl ctl = new yoseen.Ctl();
                        ctl.Type = yoseen.CtlType.CtlType_ManualFFC;
                        ret = yoseen.YoseenSDK.Yoseen_SendControl(_userHandle, ref ctl);
                        break;
                    }

                default:
                    break;
            }

        }
        #endregion

        /// <summary>
        /// 关闭窗口, 停止预览, 防止跨线程问题
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosing(CancelEventArgs e)
        {
            _stopPlay();
            base.OnClosing(e);
        }
    }

    [Flags]
    enum ShellViewFlags
    {
        SaveMp4 = 0x0001,
        SaveStream = 0x0002,
    }
}
