using MvCamCtrl.NET;
using OpenCvSharp;
using OpenCvSharp.Dnn;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace YSDetection
{
    public class HikCamera
    {
        [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
        public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

        MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
        int nRet = MyCamera.MV_OK;
        MyCamera.cbExceptiondelegate pCallBackFunc;
        private MyCamera device = new MyCamera();
        bool m_bGrabbing = false;
        Thread m_hReceiveThread = null;
        MyCamera.MV_FRAME_OUT_INFO_EX m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();
        MyCamera.MV_CC_DEVICE_INFO stDevInfo = new MyCamera.MV_CC_DEVICE_INFO();
        // ch:用于从驱动获取图像的缓存 | en:Buffer for getting image from driver
        UInt32 m_nBufSizeForDriver = 0;
        IntPtr m_BufForDriver = IntPtr.Zero;
        private static Object BufForDriverLock = new Object();
        bool _snapFlag = false;
        public MyCamera.cbOutputExdelegate ImageCallback;
        MyCamera.MV_FRAME_OUT _frame = new MyCamera.MV_FRAME_OUT();
        readonly ManualResetEvent _snapHandle = new ManualResetEvent(false);

        //ImageSet _bufferImgSet = null;
        ManualResetEvent _bufferHandle = new ManualResetEvent(false);
        public Action<DateTime, Mat, int> OnHImageOutput { get; set; }
        /// <summary>
        /// 相机拍照计数
        /// </summary>
        public volatile int SnapshotCount = 0;
        public bool Ifsucess = false;
        public HikCamera() { }
        private void DeviceListAcq()
        {
            // ch:创建设备列表 | en:Create Device List
            System.GC.Collect();
           // cbDeviceList.Items.Clear();
           // m_stDeviceList.nDeviceNum = 0;
            int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList);
            if (0 != nRet)
            {
               // ShowErrorMsg("Enumerate devices fail!", 0);
                return;
            }

            // ch:在窗体列表中显示设备名 | en:Display device name in the form list
            for (int i = 0; i < m_stDeviceList.nDeviceNum; i++)
            {
                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));

                    if (gigeInfo.chUserDefinedName != "")
                    {
                        //cbDeviceList.Items.Add("GEV: " + gigeInfo.chUserDefinedName + " (" + gigeInfo.chSerialNumber + ")");
                    }
                    else
                    {
                       // cbDeviceList.Items.Add("GEV: " + gigeInfo.chManufacturerName + " " + gigeInfo.chModelName + " (" + gigeInfo.chSerialNumber + ")");
                    }
                }
                else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                {
                    MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                    if (usbInfo.chUserDefinedName != "")
                    {
                        //cbDeviceList.Items.Add("U3V: " + usbInfo.chUserDefinedName + " (" + usbInfo.chSerialNumber + ")");
                    }
                    else
                    {
                        //cbDeviceList.Items.Add("U3V: " + usbInfo.chManufacturerName + " " + usbInfo.chModelName + " (" + usbInfo.chSerialNumber + ")");
                    }
                }
            }

            // ch:选择第一项 | en:Select the first item
            if (m_stDeviceList.nDeviceNum != 0)
            {
                //cbDeviceList.SelectedIndex = 0;
            }
        }

        public bool Start()
        {
            #region 枚举相机后根据IP地址匹配连接相机
            //将IP地址转换为字节数组 
            uint intAddress = 0;
            if (/*(IIConfig.HikCameraType == EnumHelper.HikCameraType.Gige) &&*/ (!string.IsNullOrWhiteSpace("169.254.127.84")))
            {
                byte[] IPArr = IPAddress.Parse("169.254.127.84").GetAddressBytes();
                for (int i = 0; i < IPArr.Length; i++)
                {
                    intAddress += (uint)(IPArr[i] << (IPArr.Length - 1 - i) * 8);
                }
            }

            MyCamera.MV_CC_DEVICE_INFO_LIST m_pDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();

            int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_pDeviceList);
            if (0 != nRet)
            {
                throw new Exception($"Enumerate devices fail!");
            }
            else
            {
                //LogAsync(DateTime.Now, $"搜索获取{m_pDeviceList.nDeviceNum.ToInt()}台相机在线", "");
            }

            bool isCameraFound = false;
            for (int i = 0; i < m_pDeviceList.nDeviceNum; i++)
            {
                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_pDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                IntPtr buffer = IntPtr.Zero;

                if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    buffer = Marshal.UnsafeAddrOfPinnedArrayElement(device.SpecialInfo.stGigEInfo, 0);
                    MyCamera.MV_GIGE_DEVICE_INFO stGigEDev = (MyCamera.MV_GIGE_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MyCamera.MV_GIGE_DEVICE_INFO));

                    if (stGigEDev.nCurrentIp == intAddress)
                    {
                        stDevInfo = device;
                        isCameraFound = true;
                    }
                }
                else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                {
                    //buffer = Marshal.UnsafeAddrOfPinnedArrayElement(device.SpecialInfo.stUsb3VInfo, 0);
                    //MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                    //if (usbInfo.chSerialNumber == IConfig.SerialNum)
                    //{
                    //    stDevInfo = device;
                    //    isCameraFound = true;
                    //}
                }

                //释放内存空间
                //Marshal.FreeHGlobal(buffer);

                if (isCameraFound)
                    break;
            }

            if (!isCameraFound)
            {
                throw new Exception($"相机未能找到");
            }

            #endregion

            // ch:创建设备 | en: Create device
            nRet = device.MV_CC_CreateDevice_NET(ref stDevInfo);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception($"Create device failed:{nRet:x8}");
            }

            // ch:打开设备 | en:Open device
            nRet = device.MV_CC_OpenDevice_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception($"Open device failed:{nRet:x8}");
            }


            // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
            if (stDevInfo.nTLayerType == MyCamera.MV_GIGE_DEVICE)
            {
                int nPacketSize = device.MV_CC_GetOptimalPacketSize_NET();
                if (nPacketSize > 0)
                {
                    nRet = device.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                    if (nRet != MyCamera.MV_OK)
                    {
                        Console.WriteLine("Warning: Set Packet Size failed {0:x8}", nRet);
                    }
                }
                else
                {
                    Console.WriteLine("Warning: Get Packet Size failed {0:x8}", nPacketSize);
                }
            }

            // ch:注册异常回调函数 | en:Register Exception Callback
            nRet = device.MV_CC_RegisterExceptionCallBack_NET(pCallBackFunc, IntPtr.Zero);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception($"Register expection callback failed:{nRet}");
            }
            GC.KeepAlive(pCallBackFunc);

            // ch:设置采集连续模式 | en:Set Continues Aquisition Mode
            device.MV_CC_SetEnumValue_NET("AcquisitionMode", 2);// ch:工作在连续模式 | en:Acquisition On Continuous Mode
            if (false)
            {
                device.MV_CC_SetEnumValue_NET("TriggerMode", 0);    // ch:连续模式 | en:Continuous

                // ch:注册回调函数 | en:Register image callback
                ImageCallback = new MyCamera.cbOutputExdelegate(ImageCallbackFunc);
                nRet = device.MV_CC_RegisterImageCallBackEx_NET(ImageCallback, IntPtr.Zero);
                if (MyCamera.MV_OK != nRet)
                {
                    throw new Exception("Register image callback failed!");
                }
            }
            else
            {
                // ch:设置触发模式为off || en:set trigger mode as off
                nRet = device.MV_CC_SetEnumValue_NET("TriggerMode", 1);
                if (MyCamera.MV_OK != nRet)
                {
                    throw new Exception("Set TriggerMode failed!");
                }

                if (false)
                {
                    // ch:触发源选择:0 - Line0; | en:Trigger source select:0 - Line0;
                    //           1 - Line1;
                    //           2 - Line2;
                    //           3 - Line3;
                    //           4 - Counter;
                    //           7 - Software;
                    nRet = device.MV_CC_SetEnumValue_NET("TriggerSource", 0);
                    if (MyCamera.MV_OK != nRet)
                    {
                        throw new Exception("Set Line0 Trigger failed!");
                    }

                    // ch:注册回调函数 | en:Register image callback
                    ImageCallback = new MyCamera.cbOutputExdelegate(ImageCallbackFunc);
                    nRet = device.MV_CC_RegisterImageCallBackEx_NET(ImageCallback, IntPtr.Zero);
                    if (MyCamera.MV_OK != nRet)
                    {
                        throw new Exception("Register image callback failed!");
                    }
                }
                else
                {
                    nRet = device.MV_CC_SetEnumValue_NET("TriggerSource", 7);
                    if (MyCamera.MV_OK != nRet)
                    {
                        throw new Exception("Set Software Trigger failed!");
                    }
                }
            }
            // ch:开启抓图 || en: start grab image
            nRet = device.MV_CC_StartGrabbing_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception($"Start grabbing failed:{nRet:x8}");
            }
            else
            {
                Ifsucess = true;
            }
            return true;
        }
        public void Stop()
        {
            if(Ifsucess)
            {
                // IIConfig.PropertyChanged -= IIConfig_PropertyChanged;


                // ch:停止抓图 | en:Stop grab image
                nRet = device.MV_CC_StopGrabbing_NET();
                if (MyCamera.MV_OK != nRet)
                {
                    throw new Exception($"Stop grabbing failed{nRet:x8}");
                }

                // ch:关闭设备 | en:Close device
                nRet = device.MV_CC_CloseDevice_NET();
                if (MyCamera.MV_OK != nRet)
                {
                    throw new Exception($"Close device failed{nRet:x8}");
                }

                // ch:销毁设备 | en:Destroy device
                nRet = device.MV_CC_DestroyDevice_NET();
                if (MyCamera.MV_OK != nRet)
                {
                    throw new Exception($"Destroy device failed:{nRet:x8}");
                }
            }
            Ifsucess = false;
            
           
        }
        void ImageCallbackFunc(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
        {
            try
            {
                //                if (false)
                //                {
                //                    // SnapshotCount++;
                //                    Interlocked.Increment(ref SnapshotCount);


                //                    // Console.WriteLine($">>>> {IIConfig.Name} 相机硬触发...");

                //                    //LogAsync(DateTime.Now, LogLevel.Information, $"{Name}相机硬触发");
                //                    //Stopwatch sw = new Stopwatch();
                //                    //sw.Start();

                //                    int nWidth = pFrameInfo.nWidth;
                //                    int nHeight = pFrameInfo.nHeight;

                //                    //HImage hImage = new HImage();
                //                    //HObject Hobj = new HObject();
                //                    //IntPtr pTemp = IntPtr.Zero;
                //                    //hImage.GenImage1((HTuple)"byte", nWidth, nHeight, pData);

                //                    //var imgSet = new ImageSet
                //                    //{
                //                    //    HImage = hImage,
                //                    //    ImageSaveOption = IConfig.ImageSaveOption.Copy()
                //                    //};


                //                    //测试图片颜色

                //                    //if (IsColor(pFrameInfo.enPixelType))
                //                    //{
                //                    //    //pFrameInfo.enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;

                //                    //    // 彩色图
                //                    //    switch (pFrameInfo.enPixelType)
                //                    //    {
                //                    //        case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
                //                    //            imgSet.HImage = imgSet.HImage.CfaToRgb("bayer_gr", "bilinear");
                //                    //            imgSet.HImage = imgSet.HImage.ConvertHObjectToHImage();
                //                    //            break;
                //                    //        case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
                //                    //            imgSet.HImage = imgSet.HImage.CfaToRgb("bayer_rg", "bilinear");
                //                    //            imgSet.HImage = imgSet.HImage.ConvertHObjectToHImage();
                //                    //            break;
                //                    //        case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
                //                    //            imgSet.HImage = imgSet.HImage.CfaToRgb("bayer_gb", "bilinear");
                //                    //            imgSet.HImage = imgSet.HImage.ConvertHObjectToHImage();
                //                    //            break;
                //                    //        case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
                //                    //            imgSet.HImage = imgSet.HImage.CfaToRgb("bayer_bg", "bilinear");
                //                    //            imgSet.HImage = imgSet.HImage.ConvertHObjectToHImage();
                //                    //            break;
                //                    //    }

                //                    //}


                //                    if (pFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
                //                    {
                //                        // Console.WriteLine("image pixel format is rgb8...");
                //                        //pTemp = pData;
                //                        //HOperatorSet.GenImageInterleaved(out Hobj, (HTuple)pTemp, (HTuple)"rgb", (HTuple)pFrameInfo.nWidth, (HTuple)pFrameInfo.nHeight, -1, "byte", 0, 0, 0, 0, -1, 0);
                //                        //HobjectToRGBHimage(Hobj, ref hImage);
                //                    }

                //                    // Console.WriteLine($">>>> {IIConfig.Name} 相机数据转换完成 ...");

                //                    //if (!IsMonoData(pFrameInfo.enPixelType))
                //                    //{
                //                    //    pImageBuf = Marshal.AllocHGlobal((int)stFrameInfo.nWidth * stFrameInfo.nHeight * 3);
                //                    //    HOperatorSet.GenImageInterleaved(out Hobj, (HTuple)pTemp, (HTuple)"rgb", (HTuple)pFrameInfo.nWidth, (HTuple)pFrameInfo.nHeight, -1, "byte", 0, 0, 0, 0, -1, 0);
                //                    //}


                //#if false
                //                    if (pFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
                //                    {
                //                        //加入内部队列
                //                        InitialImageSet(imgSet);

                //                        //业务图片处理加入外部队列
                //                        var outImgSet = CopyImageSet(imgSet);

                //                        if (IIConfig.IsDirectHardwareTrigger)
                //                        {
                //                            //传感器直接触发 收到图片后 传出
                //                            OnHImageOutput?.Invoke(DateTime.Now, this, outImgSet);
                //                        }
                //                        else
                //                        {
                //                            _bufferImgSet = outImgSet;
                //                            //_bufferImgSetQueue.Enqueue(outImgSet);
                //                            _bufferHandle.Set();
                //                        }
                //                        ClearImageSet(imgSet);
                //                    }
                //                    else
                //                    {
                //                        Console.WriteLine($">>>> 像素类型:{pFrameInfo.enPixelType}");
                //                    }

                //#else
                //                    //加入内部队列
                //                    InitialImageSet(imgSet);

                //                    //业务图片处理加入外部队列
                //                    var outImgSet = CopyImageSet(imgSet);


                //                    //  LogAsync(DateTime.Now, LogLevel.Information, $"{Name}相机取像, 次数:{SnapshotCount}");

                //                    // Console.WriteLine($">>>> {IIConfig.Name} 相机回调 OnHImageOutput ...");
                //                    if (IIConfig.IsDirectHardwareTrigger)
                //                    {
                //                        // 传感器直接触发 收到图片后 传出
                //                        OnHImageOutput?.Invoke(DateTime.Now, this, outImgSet);
                //                    }
                //                    else
                //                    {
                //                        _bufferImgSet = outImgSet;
                //                        // _bufferImgSetQueue.Enqueue(outImgSet);
                //                        _bufferHandle.Set();
                //                    }
                //                    //处理完图片 会清理内部队列的图像
                //                    DisplayAndSaveOriginImage(imgSet.Id);
                //                    //ClearImageSet(imgSet);
                //#endif
                //                }
                //else
                //{
                if (_snapFlag)
                {
                    _snapFlag = false;
                    _frame = new MyCamera.MV_FRAME_OUT
                    {
                        stFrameInfo = pFrameInfo,
                        pBufAddr = pData
                    };
                    _snapHandle.Set();
                }
                //  }
            }
            catch (Exception ex)
            {
                //LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}相机取像异常,{ex.GetExceptionMessage()}");
            }
        }
        public void Snapshot()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            //  ImageSet set = new ImageSet();
            // set.SnapshotCount = SnapshotCount;
            // set.SnapshotFileName = SnapshotFileNames;

            //InitialImageSet(set);
            MyCamera.MV_FRAME_OUT frameInfo = new MyCamera.MV_FRAME_OUT();
            nRet = MyCamera.MV_OK;
            if (true)
            {
                // ch: 触发命令 || en: Trigger command
                nRet = device.MV_CC_SetCommandValue_NET("TriggerSoftware");
                if (MyCamera.MV_OK != nRet)
                {
                    throw new Exception($"相机拍照触发失败:{nRet}");
                }

                nRet = device.MV_CC_GetImageBuffer_NET(ref frameInfo, 1000);
                nRet = device.MV_CC_FreeImageBuffer_NET(ref frameInfo);
            }
            else
            {
                _snapHandle.Reset();
                _snapFlag = true;
                _snapHandle.WaitOne();
                //lock (_imgCallBackLock)
                {
                    frameInfo.stFrameInfo = _frame.stFrameInfo;
                    frameInfo.pBufAddr = _frame.pBufAddr;
                }
            }

            // ch:获取一帧图像 | en:Get one image
            if (MyCamera.MV_OK == nRet)
            {
                if (frameInfo.pBufAddr != IntPtr.Zero)
                {
                    if (nRet == MyCamera.MV_OK)
                    {
                        var pFrameInfo = frameInfo.stFrameInfo;
                        Mat mat=new Mat(pFrameInfo.nHeight, pFrameInfo.nWidth, MatType.CV_8UC3, frameInfo.pBufAddr);
                        OnHImageOutput?.Invoke(DateTime.Now, mat, SnapshotCount);
                        mat.ImWrite("D://123.jpg");
                        //HImage hImage = new HImage();
                        //HObject Hobj = new HObject();
                        //IntPtr pTemp = IntPtr.Zero;
                        //hImage.GenImage1("byte", pFrameInfo.nWidth, pFrameInfo.nHeight, frameInfo.pBufAddr);

                        ////hImage.ConvertHImageTo16GrayBitmap();
                        //var imgSet = new ImageSet
                        //{
                        //    HImage = hImage,
                        //    ImageSaveOption = IConfig.ImageSaveOption.Copy()
                        //};
                        //if (pFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
                        //{
                        //    // Console.WriteLine("image pixel format is rgb8...");
                        //    pTemp = frameInfo.pBufAddr;
                        //    HOperatorSet.GenImageInterleaved(out Hobj, (HTuple)pTemp, (HTuple)"rgb", (HTuple)pFrameInfo.nWidth, (HTuple)pFrameInfo.nHeight, -1, "byte", 0, 0, 0, 0, -1, 0);
                        //    HobjectToRGBHimage(Hobj, ref hImage);
                        //}


                        //加入内部队列
                        //InitialImageSet(imgSet);

                        ////业务图片处理加入外部队列
                        //var outImgSet = CopyImageSet(imgSet);
                        //OnHImageOutput?.Invoke(DateTime.Now, this, outImgSet);
                        //LogAsync(DateTime.Now, LogLevel.Information, $"{Name}相机取像, 次数:{SnapshotCount}");
                        //DisplayAndSaveOriginImage(imgSet.Id);
                        //HikToBitmap(pFrameInfo, frameInfo.pBufAddr);
                        //Generate8GrayImageByPointer((int)nWidth, (int)nHeight, frameInfo.pBufAddr, "");
                    }
                }
            }
            else
            {
                throw new Exception($"Grap Image Failed:{nRet:x8}");
            }

            sw.Stop();
            //LogAsync(DateTime.Now, LogLevel.Information, $"取像耗时:{sw.ElapsedMilliseconds} ms");
        }
    }
}