using HalconDotNet;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

namespace CanFly.Helper
{
    public class HDevEngineTool : IDisposable
    {
        #region 常量

        // path of external procedures
        readonly string ProcedurePath = Environment.CurrentDirectory + "\\Vision\\";

        #endregion

        #region 成员变量

        /// <summary>
        /// 处理过程名
        /// </summary>
        public string ProcedureName;

        /// <summary>
        /// hdev程序启动引擎
        /// </summary>
        private readonly HDevEngine myEngine;

        /// <summary>
        /// 过程载入工具 .hdvp
        /// </summary>
        private HDevProcedureCall procedureCall;

        /// <summary>
        /// 程序运行是否成功
        /// </summary>
        public bool IsSuccessful { get; set; } = false;

        /// <summary>
        /// 控制参数字典
        /// </summary>
        public Dictionary<string, HTuple> InputTupleDic { get; set; }

        /// <summary>
        /// 图形参数字典
        /// </summary>
        public Dictionary<string, HObject> InputImageDic { get; set; }

        #endregion

        #region 初始化
        /// <summary>
        /// 实例化 默认搜索路径为: 启动路径//Vision//
        /// </summary>
        public HDevEngineTool()
        {
            ProcedureName = "";
            myEngine = new HDevEngine();
            myEngine.SetProcedurePath(ProcedurePath);

            InputImageDic = new Dictionary<string, HObject>();
            InputTupleDic = new Dictionary<string, HTuple>();
        }

        /// <summary>
        /// 实例化
        /// </summary>
        /// <param name="path">外部函数搜索路径</param>
        public HDevEngineTool(string path)
        {
            myEngine = new HDevEngine();
            myEngine.SetProcedurePath(path);

            InputImageDic = new Dictionary<string, HObject>();
            InputTupleDic = new Dictionary<string, HTuple>();
        }
        #endregion



        /// <summary>
        /// 设置函数运行所需参数
        /// </summary>
        /// <param name="_tupleDictionary">控制参数</param>
        /// <param name="_imageDictionary">图形参数</param>
        public void SetDictionary(Dictionary<string, HTuple> _tupleDictionary, Dictionary<string, HObject> _imageDictionary)
        {
            InputTupleDic = _tupleDictionary;
            InputImageDic = _imageDictionary;
        }



        /// <summary>
        /// 载入过程 .hdvp
        /// </summary>
        /// <param name="procedureName">过程名</param>
        public void LoadProcedure(string procedureName)
        {
            ProcedureName = procedureName;
            try
            {
                HDevProcedure procedure = new HDevProcedure(procedureName);
                procedureCall = new HDevProcedureCall(procedure);
            }
            catch (HDevEngineException Ex)
            {
                Trace.TraceInformation("HDevProgram {0} Load fail ,Error Line : {1}, Line number: {2}, Halcon error number : {3}", Ex.ProcedureName, Ex.LineText, Ex.LineNumber, Ex.HalconError);
                return;
            }
        }



        /// <summary>
        /// 执行过程
        /// </summary>
        [HandleProcessCorruptedStateExceptions]
        public bool RunProcedure(out string errorMsg, out int timeElasped)
        {
            //lock (_runLock)
            {
                errorMsg = "";
                Stopwatch sw = new Stopwatch();
                sw.Start();
                try
                {
                    foreach (KeyValuePair<string, HTuple> pair in InputTupleDic)
                    {
                        procedureCall.SetInputCtrlParamTuple(pair.Key, pair.Value);
                    }

                    foreach (KeyValuePair<string, HObject> pair in InputImageDic)
                    {
                        procedureCall.SetInputIconicParamObject(pair.Key, pair.Value);
                    }

                    procedureCall.Execute();

                    IsSuccessful = true;
                }
                catch (HDevEngineException ex)
                {
                    IsSuccessful = false;
                    errorMsg = $"HDevProgram {ex.ProcedureName} Run fail , Line number: {ex.LineNumber}, Halcon error number : {ex.HalconError},ex:{ex.Message}";
                }
                finally
                {
                    sw.Stop();
                    timeElasped = (int)sw.ElapsedMilliseconds;
                }
                return IsSuccessful;
            }
        }

        object _runLock = new object();
        /// <summary>
        /// 执行过程
        /// </summary>
        public Tuple<bool, Dictionary<string, HTuple>, Dictionary<string, HObject>, string, int> RunProcedure(Dictionary<string, HTuple> inputHTupleDict, Dictionary<string, HObject> inputImgDict, List<string> outputHTuples = null, List<string> outputObjs = null)
        {
            lock (_runLock)
            {
                string errorMsg = "";
                int timeElasped = 0;
                bool result = false;
                Dictionary<string, HTuple> outputHTupleDict = new Dictionary<string, HTuple>();
                Dictionary<string, HObject> outputObjDict = new Dictionary<string, HObject>();

                Stopwatch sw = new Stopwatch();
                sw.Start();
                try
                {
                    if (inputHTupleDict != null && inputHTupleDict.Count > 0)
                    {
                        foreach (KeyValuePair<string, HTuple> pair in inputHTupleDict)
                        {
                            procedureCall.SetInputCtrlParamTuple(pair.Key, pair.Value);
                        }
                    }

                    if (InputImageDic != null && inputImgDict.Count > 0)
                    {
                        foreach (KeyValuePair<string, HObject> pair in inputImgDict)
                        {
                            procedureCall.SetInputIconicParamObject(pair.Key, pair.Value);
                        }
                    }

                    procedureCall.Execute();

                    result = true;
                }
                catch (HDevEngineException ex)
                {
                    result = false;
                    errorMsg += $"HDevProgram {ex.ProcedureName} Run fail , Line number: {ex.LineNumber}, Halcon error number : {ex.HalconError},ex:{ex.Message}";
                }
                finally
                {
                    sw.Stop();
                    timeElasped = (int)sw.ElapsedMilliseconds;
                }

                if (result)
                {
                    if (outputHTuples != null && outputHTuples.Count > 0)
                    {
                        outputHTuples.ForEach(t =>
                        {
                            try
                            {
                                outputHTupleDict[t] = procedureCall.GetOutputCtrlParamTuple(t);
                            }
                            catch (Exception ex)
                            {
                                result = false;
                                errorMsg += $"\r\n获取{t}结果异常:{ex.Message}";

                                outputHTupleDict[t] = null;
                            }
                        });
                    }

                    if (outputObjs != null && outputObjs.Count > 0)
                    {
                        outputObjs.ForEach(t =>
                        {
                            try
                            {
                                outputObjDict[t] = procedureCall.GetOutputIconicParamObject(t);
                            }
                            catch (Exception ex)
                            {
                                result = false;
                                errorMsg += $"\r\n获取{t}结果异常:{ex.Message}";

                                outputObjDict[t] = null;
                            }
                        });
                    }
                }
                Tuple<bool, Dictionary<string, HTuple>, Dictionary<string, HObject>, string, int> ret = new Tuple<bool, Dictionary<string, HTuple>, Dictionary<string, HObject>, string, int>(result, outputHTupleDict, outputObjDict, errorMsg, timeElasped);
                return ret;
            }
        }

        public HTuple GetResultTuple(string key)
        {
            try
            {
                if (IsSuccessful)
                {
                    return procedureCall.GetOutputCtrlParamTuple(key);
                }
                else
                {
                    return new HTuple();
                }
            }
            catch (Exception ex)
            {
                return new HTuple();
            }

        }

        public HObject GetResultObject(string key, bool ignoreError = false)
        {
            try
            {
                if (ignoreError || IsSuccessful)
                {
                    return procedureCall.GetOutputIconicParamObject(key);
                }
                else
                {
                    return new HObject();
                }
            }
            catch (Exception ex)
            {
                return new HObject();
            }
        }

        public void Dispose()
        {
            procedureCall?.Dispose();
            myEngine?.Dispose();
        }
    }

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

        public static HImage Convert8GrayBitmapToHImage(this Bitmap bmp)
        {
            HImage himage = new HImage();
            try
            {
                //判断输入图像不为null
                if (bmp == null)
                {
                    return null;
                }

                {
                    //重绘himage
                    //HImage curImage = new HImage();
                    BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
                    himage.GenImage1("byte", bmp.Width, bmp.Height, bmpData.Scan0);
                    bmp.UnlockBits(bmpData);
                    //himage = curImage;
                }
                return himage;
            }
            catch (Exception e)
            {
                return null;
            }
        }

        public static Bitmap ConvertHImageToBitmap(this HObject hImage)
        {
            HOperatorSet.CountChannels(hImage, out HTuple chanels);
            if (chanels.I == 1)
            {
                return hImage.ConvertHImageTo8GrayBitmap();
            }
            else
            {
                return hImage.ConvertHImageToRGBBitmap();
                //return hImage.HObject2BitmapRGB();
            }
        }

        public static Bitmap HObject2BitmapRGB(this HObject hObject)
        {
            ////获取图像尺寸
            HTuple width0, height0, type, width, height;
            //获取图像尺寸
            HOperatorSet.GetImageSize(hObject, out width0, out height0);
            // 创建交错格式图像
            HOperatorSet.InterleaveChannels(hObject, out HObject InterImage, "argb", "match", 255);  //"rgb", 4 * width0, 0     "argb", "match", 255

            //获取交错格式图像指针
            HOperatorSet.GetImagePointer1(InterImage, out HTuple Pointer, out type, out width, out height);
            IntPtr ptr = Pointer;
            //构建新Bitmap图像
            Bitmap res32 = new Bitmap(width / 4, height, width, PixelFormat.Format32bppArgb, ptr);  // Format32bppArgb     Format24bppRgb

            //32位Bitmap转24位
            var res24 = new Bitmap(res32.Width, res32.Height, PixelFormat.Format24bppRgb);
            Graphics graphics = Graphics.FromImage(res24);
            graphics.DrawImage(res32, new Rectangle(0, 0, res32.Width, res32.Height));

            return res24;
        }

        public static Bitmap ConvertHImageTo8GrayBitmap(this HObject hImage)
        {
            try
            {
                HTuple type, width, height, pointer;
                HOperatorSet.GetImagePointer1(hImage, out pointer, out type, out width, out height);

                Bitmap bmp = new Bitmap(width.I, height.I, PixelFormat.Format8bppIndexed);
                ColorPalette pal = bmp.Palette;
                for (int i = 0; i <= 255; i++)
                {
                    pal.Entries[i] = Color.FromArgb(255, i, i, i);
                }
                bmp.Palette = pal;

                BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

                if (width % 4 == 0)
                {
                    CopyMemory(bitmapData.Scan0, (IntPtr)pointer.D, (uint)(bitmapData.Stride * height.I));
                }
                else
                {
                    Parallel.For(0, height.I, h =>
                    {
                        CopyMemory(bitmapData.Scan0 + h * bitmapData.Stride, (IntPtr)(pointer.D + h * width.I), (uint)width.I);
                    });
                }

                bmp.UnlockBits(bitmapData);
                return bmp;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        public static Bitmap ConvertHImageToRGBBitmap(this HObject hImage)
        {
            try
            {
                HOperatorSet.GetImagePointer3(hImage, out HTuple pointRed, out HTuple pointGreen, out HTuple pointBlue, out HTuple type, out HTuple width, out HTuple height);
                Bitmap image = new Bitmap(width.I, height.I, PixelFormat.Format24bppRgb);
                BitmapData imageData = image.LockBits(new Rectangle(0, 0, width.I, height.I), ImageLockMode.ReadWrite, image.PixelFormat);
                IntPtr pR = (IntPtr)pointRed.D;
                IntPtr pG = (IntPtr)pointGreen.D;
                IntPtr pB = (IntPtr)pointBlue.D;
                Parallel.For(0, imageData.Height, h =>
                {
                    Parallel.For(0, imageData.Width, w =>
                    {
                        int dest = h * imageData.Stride + w * 3;
                        int source = h * imageData.Width + w;

                        Marshal.WriteByte(imageData.Scan0, dest, Marshal.ReadByte(pB, source));
                        Marshal.WriteByte(imageData.Scan0, dest + 1, Marshal.ReadByte(pG, source));
                        Marshal.WriteByte(imageData.Scan0, dest + 2, Marshal.ReadByte(pR, source));
                    });
                });

                image.UnlockBits(imageData);
                return image;
            }
            catch (Exception exc)
            {
                return null;
            }
        }

        public static Bitmap ConvertHImageTo16GrayBitmap(this HImage originHImage)
        {
            //IntPtr pointer = hImage.GetImagePointer1(out string type, out int width, out int height);

            //int widthIn4 = (int)Math.Ceiling(width / 4.0) * 4;

            ////Bitmap bmp = new Bitmap(widthIn4, height, PixelFormat.Format48bppRgb);
            //Bitmap showImage = new Bitmap(widthIn4, height, PixelFormat.Format48bppRgb);

            //Rectangle rect = new Rectangle(0, 0, widthIn4, height);
            ////BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format48bppRgb);
            //BitmapData showImageData = showImage.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format48bppRgb);
            //unsafe
            //{
            //    byte* data = (byte*)pointer;
            //    //byte* bitmapBuffer = (byte*)bitmapData.Scan0;
            //    byte* showBitmapBuffer = (byte*)showImageData.Scan0;

            //    Parallel.For(0, width * height, i =>
            //    {
            //        int index = (i + 1) % width + widthIn4 * ((int)Math.Floor((double)(i + 1) / width)) - 1;

            //        //showBitmapBuffer[index * 6] = bitmapBuffer[index * 6] = data[i * 2];
            //        //showBitmapBuffer[index * 6 + 1] = bitmapBuffer[index * 6 + 1] = data[i * 2 + 1];
            //        showBitmapBuffer[index * 6] = data[i * 2];
            //        showBitmapBuffer[index * 6 + 1] = data[i * 2 + 1];
            //    });
            //}

            ////bmp.UnlockBits(bitmapData);
            //showImage.UnlockBits(showImageData);

            //return showImage;

            // dev_set_draw('margin')
            //read_image(Image, '0.tif')

            HImage hImage = originHImage.Clone();

            //* 如果16位图像非常暗的话,建议在这一步进行提亮,因为后面8位图像大幅度提亮易造成色阶断裂,出现不连续的像素块
            // * scale_image(Image, Image, 25, 0)
            //hImage = hImage.ScaleImage(25.0, 0.0);

            //get_domain(Image, rectangle)
            //* 获取全图中像素灰度值的最大和最小值
            //min_max_gray(rectangle, Image, 0, Min, Max, range)
            hImage.MinMaxGray(hImage.GetDomain(), 0, out double min, out double max, out double range);

            //* 将16位图的灰度值映射到0 - 255上
            double mult = 255.0 / (max - min);
            double add = -mult * min;
            hImage = hImage.ScaleImage(mult, add);

            //* 转换为'byte'类型
            //convert_image_type(Image_scaled, ImageConverted, 'byte')
            hImage = hImage.ConvertImageType("byte");

            Bitmap showImage = hImage.ConvertHImageTo8GrayBitmap();

            hImage.Dispose();

            return showImage;

            //* 如果转换以后图像整体对比度太低的话,可以提高对比度(这里是对8位图像处理)
            //Min:= 20
            //Max:= 160
            //Mult:= 255.0 / (Max - Min)
            //Add:= -Mult * Min
            //scale_image(ImageConverted, ImageConverted_scaled, Mult, Add)
        }

        public static List<double> HTupleToDouble(this HTuple tuple)
        {
            List<double> list = new List<double>();

            for (int i = 0; i < tuple.Length; i++)
            {
                list.Add(tuple[i].D);
            }

            return list;
        }




        public static HImage ConvertHObjectToHImage(this HObject obj)
        {
            HOperatorSet.CountChannels(obj, out HTuple channels);

            HImage img = new HImage();
            if (channels.I == 1)
            {
                HTuple pointer, type, width, height;
                HOperatorSet.GetImagePointer1(obj, out pointer, out type, out width, out height);

                img.GenImage1(type, width, height, pointer);
            }
            else
            {
                HTuple pRed, pGreen, pBlue, type, width, height;
                HOperatorSet.GetImagePointer3(obj, out pRed, out pGreen, out pBlue, out type, out width, out height);

                img.GenImage3(type, width, height, pRed, pGreen, pBlue);
            }

            return img;
        }

        #region 灰度图转换为伪彩图
        public static Bitmap ConvertGrayImageToPesudoColorfulImage(this HImage hImage, double max = 0, double min = 0, double zoom = 1, bool isShowHeightTip = false, int zResolution = 100000)
        {
            hImage.GetImageSize(out int width, out int height);
            hImage.MinMaxGray(new HRegion(0.0, 0.0, width, height), 3, out HTuple roiMin, out HTuple roiMax, out _);

            if (max == 0)
            {
                max = roiMax;
            }

            if (min == 0)
            {
                min = roiMin;
            }

            double mult = 235 / (zoom * (max - min));
            double add = (0 - mult) * min * zoom + 10;
            HOperatorSet.ScaleImage(hImage, out HObject imageScaled, mult, add);
            HOperatorSet.ConvertImageType(imageScaled, out imageScaled, "byte");
            Stopwatch sw = new Stopwatch();
            sw.Start();

            HOperatorSet.GetImagePointer1(imageScaled, out HTuple pointer, out HTuple type, out _, out _);
            Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bitmap.PixelFormat);

            unsafe
            {
                byte* data = (byte*)(IntPtr)pointer;
                byte* bitmapDataBuff = (byte*)bitmapData.Scan0;

                if (width % 4 != 0)
                {
                    Parallel.For(0, height, h =>
                    {
                        Parallel.For(0, width, w =>
                        {
                            byte gray = data[h * width + w];
                            byte[] convertBytes = ConvertByteToColorfulArray(gray);

                            Marshal.Copy(convertBytes, 0, (IntPtr)(bitmapDataBuff + h * bitmapData.Stride + w * 3), 3);
                        });
                    });
                }
                else
                {
                    Parallel.For(0, width * height, i =>
                    {
                        byte gray = data[i];
                        byte[] convertBytes = ConvertByteToColorfulArray(gray);

                        Marshal.Copy(convertBytes, 0, (IntPtr)(bitmapDataBuff + i * 3), 3);
                    });
                }
            }
            bitmap.UnlockBits(bitmapData);

            if (isShowHeightTip)
            {
                List<byte> lableList = new List<byte>() { 5, 30, 60, 90, 120, 150, 180, 210, 240, 255 };
                Dictionary<double, Color> lableColorDict = lableList.ToDictionary(
                    u => (u - add) / (mult * zResolution),
                    u =>
                    {
                        byte[] colorBytes = ConvertByteToColorfulArray(u);
                        return Color.FromArgb(colorBytes[2], colorBytes[1], colorBytes[0]);
                    });

                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    int rectHeight = (int)(bitmap.Height / (5.0 * lableColorDict.Count));
                    Font font = new Font("宋体", (int)(rectHeight * 0.75), GraphicsUnit.Pixel);

                    string lable = lableColorDict.ElementAt(0).Key.ToString("f3");
                    SizeF lableSize = g.MeasureString(lable, font);
                    int rectWidth = (int)(lableSize.Width * 1.5);

                    int startX = 0;
                    int startY = 0;
                    foreach (KeyValuePair<double, Color> pair in lableColorDict)
                    {
                        g.FillRectangle(new SolidBrush(pair.Value), startX, startY, rectWidth, rectHeight);
                        g.DrawString(pair.Key.ToString("f3"), font, new SolidBrush(Color.White), (float)(startX + (rectWidth - lableSize.Width) / 2.0), (float)(startY + (rectHeight - lableSize.Height) / 2.0));

                        startY += rectHeight;
                    }
                }
            }

            sw.Stop();
            //LogAsync(DateTime.Now, EnumHelper.LogLevel.Information, $"转换耗时{sw.ElapsedMilliseconds}ms");
            return bitmap;
        }

        private static byte[] ConvertByteToColorfulArray(byte gray)
        {
            byte[] bytes = new byte[3];
            if (gray == 0)
            {
                bytes[2] = 255;
                bytes[1] = 255;
                bytes[0] = 255;
            }
            if (gray > 0 && gray <= 63)
            {
                bytes[2] = 0;
                bytes[+1] = (byte)(254 - 4 * gray);
                bytes[0] = 255;
            }
            if (gray >= 64 && gray <= 127)
            {
                bytes[2] = 0;
                bytes[1] = (byte)(4 * gray - 254);
                bytes[0] = (byte)(510 - 4 * gray);
            }
            if (gray >= 128 && gray <= 191)
            {
                bytes[2] = (byte)(4 * gray - 510);
                bytes[1] = 255;
                bytes[0] = 0;
            }
            if (gray >= 192 && gray <= 255)
            {
                bytes[2] = 255;
                bytes[1] = (byte)(1022 - 4 * gray);
                bytes[0] = 0;
            }

            return bytes;
        }
        #endregion
    }
}