//#define USE_MULTI_THREAD

using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using System.Security.Cryptography.Xml;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using DH.Commons.Base;



namespace DH.Devices.Vision
{


    /// <summary>
    /// 目标检测 GPU
    /// </summary>
    public class SimboDetection : SimboVisionMLBase
    {

        public override bool Load(MLInit mLInit)
        {
            bool res = false;
            try
            {
                Model = MLGPUEngine.InitModel(mLInit.ModelFile, 1, mLInit.Score_thre, mLInit.GPUId, 3, 8);

                //Model = MLEngine.InitModel(mLInit.ModelFile, 1, 0.45f, 0, 3);

                res = true;

#if USE_MULTI_THREAD
                IsCreated = true;
                if (IsCreated)
                {
                    _runHandleBefore ??= new AutoResetEvent(false);
                    _runHandleAfter ??= new ManualResetEvent(false);

                    _runTask ??= Task.Factory.StartNew(() =>
                        {
                            while (IsCreated)
                            {
                                _runHandleBefore.WaitOne();

                                if (IsCreated)
                                {
                                    _result = RunInferenceFixed(_req);
                                    _runHandleAfter.Set();
                                }
                            }
                        }, TaskCreationOptions.LongRunning);
                }
#endif
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return res;
        }
#if USE_MULTI_THREAD
        MLRequest _req = null;
        MLResult _result = null;
        public bool IsCreated { get; set; } = false;
        Task _runTask = null;
        AutoResetEvent _runHandleBefore = new AutoResetEvent(false);
        ManualResetEvent _runHandleAfter = new ManualResetEvent(false);
        object _runLock = new object();
#endif
        [HandleProcessCorruptedStateExceptions]
        public override MLResult RunInference(MLRequest req)
        {
#if USE_MULTI_THREAD
            MLResult mlResult = null;
            lock (_runLock)
            {
                _result = new MLResult();

                _req = req;

                _runHandleAfter.Reset();
                _runHandleBefore.Set();
                _runHandleAfter.WaitOne();

                mlResult = _result;
            }

            return mlResult;
#else
            return RunInferenceFixed(req);
#endif
        }
        private void ConvertJsonResult(string json,  ref MLResult result)
        {
            // json = "{\"FastDetResult\":[{\"cls_id\":0,\"cls\":\"liewen\",\"fScore\":0.654843,\"rect\":[175,99,110,594]},{\"cls_id\":0,\"cls\":\"liewen\",\"fScore\":0.654589,\"rect\":[2608,19,104,661]},{\"cls_id\":0,\"cls\":\"liewen\",\"fScore\":0.654285,\"rect\":[1275,19,104,662]},{\"cls_id\":0,\"cls\":\"liewen\",\"fScore\":0.620762,\"rect\":[1510,95,107,600]},{\"cls_id\":0,\"cls\":\"liewen\",\"fScore\":0.617812,\"rect\":[2844,93,106,602]}]}";
            //
            Console.WriteLine("检测结果JSON:" + json);
#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
            HYoloResult detResult = JsonConvert.DeserializeObject<HYoloResult>(json);
#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
            if (detResult == null)
            {
                return;
            }

            int iNum = detResult.HYolo.Count;
#pragma warning disable CS0219 // 变量已被赋值,但从未使用过它的值
            int IokNum = 0;
#pragma warning restore CS0219 // 变量已被赋值,但从未使用过它的值
            for (int ix = 0; ix < iNum; ix++)
            {
                var det = detResult.HYolo[ix];

                var rect = det.rect;
                DetectionResultDetail detectionResultDetail = new DetectionResultDetail();
                // detectionResultDetail.LabelNo = det.classId;
                //todo: 标签名相对应
                detectionResultDetail.LabelDisplay = det.classname;
                detectionResultDetail.Rect = new Rectangle(rect[0], rect[1], rect[2], rect[3]);
                detectionResultDetail.Score = det.fScore;
                detectionResultDetail.LabelName = det.classname;
                detectionResultDetail.Area = rect[2] * rect[3];
                detectionResultDetail.InferenceResult = ResultState.DetectNG;
              
                    result.ResultDetails.Add(detectionResultDetail);
                

            }

        }

        [HandleProcessCorruptedStateExceptions]
        public MLResult RunInferenceFixed(MLRequest req)
        {
            MLResult mlResult = new MLResult();
            Mat originMat = new Mat();
            Mat detectMat = new Mat();

#pragma warning disable CS0168 // 声明了变量,但从未使用过
            try
            {
                if (req.mImage == null)
                {
                    mlResult.IsSuccess = false;
                    mlResult.ResultMessage = "异常:mat为null,无法执行推理!";
                    return mlResult;
                }

                // resize
                detectMat = req.mImage;//1ms



                int iWidth = detectMat.Cols;
                int iHeight = detectMat.Rows;

                // 如果是单通道图像,转换为三通道 RGB 格式
                if (detectMat.Channels() == 1)
                {
                    // 将灰度图像转换为RGB格式(三通道)

                    Cv2.CvtColor(detectMat, originMat, ColorConversionCodes.GRAY2BGR);

                }
                else if (detectMat.Channels() == 3)
                {
                    // 如果已经是三通道(BGR),则直接转换为RGB

                    Cv2.CvtColor(detectMat, originMat, ColorConversionCodes.BGR2RGB);

                }

                //输入数据转化为字节
                var inputByte = new byte[originMat.Total() * 3];//这里必须乘以通道数,不然数组越界,也可以用w*h*c,差不多
                Marshal.Copy(originMat.Data, inputByte, 0, inputByte.Length);

                byte[] labellist = new byte[40960];    //新建字节数组:label1_str label2_str 

                byte[] outputByte = new byte[originMat.Total() * 3];

                Stopwatch sw = new Stopwatch();
                sw.Start();

                //mlResult.IsSuccess = true;
                unsafe
                {
                    //mlResult.IsSuccess = MLGPUEngine.Inference(Model, inputByte, iWidth, iHeight, 3, req.in_lable_path, ref outputByte[0], ref labellist[0]);

                    mlResult.IsSuccess = MLGPUEngine.Inference2(Model, inputByte, iWidth, iHeight, 3, req.in_lable_path, ref labellist[0]);
                }

                sw.Stop();


                if (mlResult.IsSuccess)
                {
                    mlResult.ResultMessage = $"深度学习推理成功,耗时:{sw.ElapsedMilliseconds} ms";

                    //将字节数组转换为字符串
                    mlResult.ResultMap = originMat.ToBitmap();//4ms
                    string strGet = System.Text.Encoding.Default.GetString(labellist, 0, labellist.Length);

                    if (strGet == null)
                    {
                        mlResult.ResultMessage = $"异常:深度学习执行推理失败!";
                        return mlResult;
                    }

                    ConvertJsonResult(strGet,  ref mlResult);

                    return mlResult;
                }
                else
                {
                    mlResult.ResultMessage = $"异常:深度学习执行推理失败!";
                    return mlResult;
                }
            }
            catch (Exception ex)
            {
                mlResult.ResultMessage = $"深度学习执行推理异常";
                return mlResult;
            }
            finally
            {
             
                originMat?.Dispose();
#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
                originMat = null;
#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
                //maskMat?.Dispose();
                // maskMat = null;
                detectMat?.Dispose();
#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
                detectMat = null;
#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
                // maskWeighted?.Dispose();
                // maskWeighted = null;
                // GC.Collect();
            }
#pragma warning restore CS0168 // 声明了变量,但从未使用过
        }

      
    }
}