using System.Text;
using ZXing.Common;
using ZXing;
using ZXing.Datamatrix.Encoder;
using SkiaSharp;
using ZXing.Datamatrix;

namespace 常用工具集.Utility.Qrcode
{
    public class DataMatrixUtils
    {
        /// <summary>
        /// 默认size为12
        /// </summary>
        /// <param name="msg">DataMatrix内容</param>
        /// <param name="codeSizeInPixels">图片长宽</param>
        /// <returns></returns>
        public static SKBitmap EncodeDataMatrix(DmtxSymbolSize size, string msg, int codeSizeInPixels, out string svg)
        {

            var writer = new DataMatrixWriter();
            var hints = new DatamatrixEncodingOptions()
            {
                //CharacterSet = "utf-8",
                Width = codeSizeInPixels,
                Height = codeSizeInPixels,
                SymbolShape = SymbolShapeHint.FORCE_SQUARE,
                Margin = 0,
                PureBarcode = true,
                MinSize = new Dimension(12, 12),
                MaxSize = new Dimension(12, 12)
            };
            switch (size)
            {
                case DmtxSymbolSize.DmtxSymbol10x10: hints.MaxSize = new Dimension(10, 10); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol12x12: hints.MaxSize = new Dimension(12, 12); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol14x14: hints.MaxSize = new Dimension(14, 14); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol16x16: hints.MaxSize = new Dimension(16, 16); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol18x18: hints.MaxSize = new Dimension(18, 18); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol20x20: hints.MaxSize = new Dimension(20, 20); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol22x22: hints.MaxSize = new Dimension(22, 22); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol24x24: hints.MaxSize = new Dimension(24, 24); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol26x26: hints.MaxSize = new Dimension(26, 26); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol32x32: hints.MaxSize = new Dimension(32, 32); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol36x36: hints.MaxSize = new Dimension(36, 36); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol40x40: hints.MaxSize = new Dimension(40, 40); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol44x44: hints.MaxSize = new Dimension(44, 44); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol48x48: hints.MaxSize = new Dimension(48, 48); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol52x52: hints.MaxSize = new Dimension(52, 52); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol64x64: hints.MaxSize = new Dimension(64, 64); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol72x72: hints.MaxSize = new Dimension(72, 72); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol80x80: hints.MaxSize = new Dimension(80, 80); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol88x88: hints.MaxSize = new Dimension(88, 88); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol96x96: hints.MaxSize = new Dimension(96, 96); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol104x104: hints.MaxSize = new Dimension(104, 104); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol120x120: hints.MaxSize = new Dimension(120, 120); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol132x132: hints.MaxSize = new Dimension(132, 132); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol144x144: hints.MaxSize = new Dimension(144, 144); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol8x18: hints.SymbolShape = SymbolShapeHint.FORCE_NONE; hints.MaxSize = new Dimension(8, 18); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol8x32: hints.SymbolShape = SymbolShapeHint.FORCE_NONE; hints.MaxSize = new Dimension(8, 32); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol12x26: hints.SymbolShape = SymbolShapeHint.FORCE_NONE; hints.MaxSize = new Dimension(12, 26); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol12x36: hints.SymbolShape = SymbolShapeHint.FORCE_NONE; hints.MaxSize = new Dimension(12, 36); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol16x36: hints.SymbolShape = SymbolShapeHint.FORCE_NONE; hints.MaxSize = new Dimension(16, 36); hints.MinSize = hints.MaxSize; break;
                case DmtxSymbolSize.DmtxSymbol16x48: hints.SymbolShape = SymbolShapeHint.FORCE_NONE; hints.MaxSize = new Dimension(16, 48); hints.MinSize = hints.MaxSize; break;
            }
            BitMatrix matrix = writer.encode(msg, BarcodeFormat.DATA_MATRIX, hints.Width, hints.Height, hints.Hints);
            //Dictionary<EncodeHintType, object> hints = new Dictionary<EncodeHintType, object>();
            //hints.Add(EncodeHintType.CHARACTER_SET, "utf-8");
            //hints.Add(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            //hints.Add(EncodeHintType.MARGIN, 1);
            //hints.Add(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
            //hints.Add(EncodeHintType.WIDTH, 16);
            //hints.Add(EncodeHintType.HEIGHT, 16);
            //hints.Add(EncodeHintType.MIN_SIZE, 16);
            //hints.Add(EncodeHintType.MAX_SIZE, 16);
            //BitMatrix matrix = new MultiFormatWriter().encode(msg, BarcodeFormat.DATA_MATRIX, codeSizeInPixels, codeSizeInPixels, hints);
            //matrix = deleteWhite(matrix);//删除白边 
            SKBitmap bitmap = new SKBitmap(matrix.Width, matrix.Height);
            SKCanvas canvas = new SKCanvas(bitmap);
            canvas.Clear();
            SKPaint paint = new SKPaint()
            {
                Color = SKColors.Black,
                IsAntialias = true,
                Style = SKPaintStyle.Fill
            };
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            sb.AppendLine($"<svg version=\"1.1\" id=\"图层_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\r\n\t viewBox=\"0 0 {matrix.Width} {matrix.Height}\" style=\"enable-background:new 0 0 {matrix.Width} {matrix.Height};\" xml:space=\"preserve\">");
            sb.AppendLine("<style type=\"text/css\">.st0{fill:#E60012;}</style>");
            bool[,] handled = new bool[matrix.Width, matrix.Height];//默认全部false
            for (int x = 0; x < matrix.Width; x++)
            {
                for (int y = 0; y < matrix.Height; y++)
                {
                    if (matrix[x, y] && !handled[x, y])
                    {
                        //从当前位置找矩形区域
                        FindRect(x, y, matrix, ref handled, out int width, out int height);
                        //x从x到x+width
                        //y从y到y+height 
                        //这些区域是已处理的
                        for (int i = 0; i < width; i++)
                        {
                            for (int j = 0; j < height; j++)
                            {
                                handled[x + i, y + j] = true;
                            }
                        }
                        sb.AppendLine($"<rect class=\"st0\" x=\"{x}\" y=\"{y}\" width=\"{width}\" height=\"{height}\" />");
                        canvas.DrawRect(x, y, width, height, paint);
                    }
                }
            }
            sb.AppendLine("</svg>");
            svg = sb.ToString();
            canvas.Dispose();
            return bitmap;
        }

        /// <summary>
        /// 找到未处理的矩形区域
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="matrix"></param>
        /// <param name="handled"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        private static void FindRect(int x, int y, BitMatrix matrix, ref bool[,] handled, out int width, out int height)
        {
            width = 1;
            height = 1;
            bool flag = false;
            int maxOffset = FindXMax(matrix, x, y, ref handled);
        A:
            if (maxOffset == 0)
            {
                return;
            }
            while (true)
            {
                height++;
                if ((y + height) >= matrix.Height)
                {
                    break;
                }
                if (!matrix[x, y + height] || handled[x, y + height])
                {
                    break;
                }
                bool allTrue = true;//默认最后一行全是true
                for (int i = 0; i < maxOffset; i++)
                {
                    if (!matrix[x + i, y + height])
                    {
                        allTrue = false;
                        break;
                    }
                    if (handled[x + i, y + height])
                    {
                        allTrue = false;
                        break;
                    }
                }
                if (allTrue)
                {
                    flag = true;
                }
                else
                {
                    if (flag || height == 2)
                    {
                        height--;
                        break;
                    }
                    maxOffset--;
                    flag = false;
                    goto A;
                }
            }
            width = maxOffset + 1;
        }

        private static int FindXMax(BitMatrix matrix, int x, int y, ref bool[,] handled)
        {
            int offset = 0;
            while (true)
            {
                if ((x + offset) > matrix.Width)
                {
                    break;
                }
                if (!matrix[x + offset, y])
                {
                    offset--;
                    break;
                }
                offset++;
            }
            return offset;
        }


        ///// <summary>
        ///// 去除白边
        ///// </summary>
        ///// <param name="matrix"></param>
        ///// <returns></returns>
        //private static BitMatrix deleteWhite(BitMatrix matrix)
        //{
        //    int[] rec = matrix.getEnclosingRectangle();
        //    int resWidth = rec[2] + 1;
        //    int resHeight = rec[3] + 1;

        //    BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        //    resMatrix.clear();
        //    for (int i = 0; i < resWidth; i++)
        //    {
        //        for (int j = 0; j < resHeight; j++)
        //        {
        //            if (matrix[i + rec[0], j + rec[1]])
        //                resMatrix[i, j] = true;
        //        }
        //    }
        //    return resMatrix;
        //}

        //public static SKBitmap EncodeDataMatrix(string msg, out string svg)
        //{
        //    return EncodeDataMatrix(DmtxSymbolSize.DmtxSymbol14x14, msg, 900, out svg);
        //}
    }

    public enum DmtxSymbolSize
    {
        DmtxSymbol10x10,
        DmtxSymbol12x12,
        DmtxSymbol14x14,
        DmtxSymbol16x16,
        DmtxSymbol18x18,
        DmtxSymbol20x20,
        DmtxSymbol22x22,
        DmtxSymbol24x24,
        DmtxSymbol26x26,
        DmtxSymbol32x32,
        DmtxSymbol36x36,
        DmtxSymbol40x40,
        DmtxSymbol44x44,
        DmtxSymbol48x48,
        DmtxSymbol52x52,
        DmtxSymbol64x64,
        DmtxSymbol72x72,
        DmtxSymbol80x80,
        DmtxSymbol88x88,
        DmtxSymbol96x96,
        DmtxSymbol104x104,
        DmtxSymbol120x120,
        DmtxSymbol132x132,
        DmtxSymbol144x144,
        DmtxSymbol8x18,
        DmtxSymbol8x32,
        DmtxSymbol12x26,
        DmtxSymbol12x36,
        DmtxSymbol16x36,
        DmtxSymbol16x48
    }
}
