using CPF.Drawing;
using CPF.Skia;
using ESBasic.ObjectManagement.Managers;
using Oraycn.ChatRendering;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace OVCS.ClientLinux.Controls.ChatBox
{
    class CpfRender : ISysRender
    {
        private string placeHolder = "!";

        private Dictionary<int, Image> emotionList;

        private ObjectManager<int, Font> fontManager = new ObjectManager<int, Font>();

        private static ObjectManager<System.Drawing.Color, SolidColorBrush> BrushManager = new ObjectManager<System.Drawing.Color, SolidColorBrush>();

        private static ObjectManager<System.Drawing.Color, SolidColorBrush> BrushManagerSelect = new ObjectManager<System.Drawing.Color, SolidColorBrush>();

        public CpfRender(Dictionary<int, object> dic)
        {
            Dictionary<int, Image> emotions = new Dictionary<int, Image>();
            foreach (KeyValuePair<int, object> pair in dic)
            {
                emotions.Add(pair.Key, (Image)pair.Value);
            }
            this.emotionList = emotions;
        }

        private DrawingContext drawingContext;

        public void SetDrawingContext(object _drawingContext)
        {
            this.drawingContext = (DrawingContext)_drawingContext;
            drawingContext.AntialiasMode = AntialiasMode.AntiAlias;
        }
        public void SetComputeGraphics(object _computeGraphics)
        {
            
        }

        private SolidColorBrush GetSolidColorBrush(System.Drawing.Color color)
        {
            SolidColorBrush brush = BrushManager.Get(color);
            if (brush == null)
            {
                brush = new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
                BrushManager.Add(color, brush);
            }
            return brush;
        }

        private SolidColorBrush GetSolidColorBrush4Select(System.Drawing.Color color)
        {
            SolidColorBrush brush = BrushManagerSelect.Get(color);
            if (brush == null)
            {
                brush = new SolidColorBrush(Color.FromArgb(66, color.R, color.G, color.B));
                BrushManagerSelect.Add(color, brush);
            }
            return brush;
        }

        private Font GetFont(int fontSize , string fontName = null)
        {
            if (fontName == null) { fontName = RenderSettings.TextFontName; }
            Font font = fontManager.Get(fontSize);
            if(font.FontSize == 0 || font.FontFamily != fontName)
            {
                font = new Font(fontName, fontSize);
                fontManager.Add(fontSize,font);
            }   
            return font;
        }

        public void DrawString(System.Drawing.Color color, System.Drawing.Point position, string str, int fontSize, int maxTextWidth = 0, string fontName = null)
        {
            if(drawingContext == null) { return; } 
            if(maxTextWidth <= 0) { maxTextWidth = 10000; }
            if(fontName == null) { fontName = RenderSettings.TextFontName; }
            SolidColorBrush solidColorBrush = this.GetSolidColorBrush(color);
            drawingContext.DrawString(new Point(position.X, position.Y), solidColorBrush, str, this.GetFont(fontSize , fontName), TextAlignment.Left, maxTextWidth);
        }

        public void DrawImage(object image, System.Drawing.Rectangle rectangle)
        {
            Rect rect = new Rect(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
            this.DrawImage(image, rect);
        }

        public void DrawImage(object image, Rect rectangle)
        {
            Image img = image as Image;
            if (drawingContext == null || img == null)
            {
                return;
            }
            this.drawingContext.DrawImage(img, rectangle, new Rect(0, 0, img.Width, img.Height));
        }

        public void FillEllipse(System.Drawing.Color color, System.Drawing.Point center, int radiusX, int radiusY)
        {
            if (drawingContext == null) { return; }
            SolidColorBrush solidColorBrush = this.GetSolidColorBrush(color);
            this.drawingContext.FillEllipse(solidColorBrush, new CPF.Drawing.Point(center.X, center.Y), radiusX, radiusY);
        }

        public void FillRectangle(System.Drawing.Color color, System.Drawing.Rectangle rectangle)
        {
            if (drawingContext == null)
            {
                return;
            }
            SolidColorBrush solidColorBrush = this.GetSolidColorBrush(color);
            this.drawingContext.FillRectangle(solidColorBrush, new Rect(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height));
        }

        public void FillTriangle(System.Drawing.Color color, System.Drawing.Point pt1, System.Drawing.Point pt2, System.Drawing.Point pt3)
        {
            if (drawingContext == null)
            {
                return;
            }
            PathGeometry pathGeometry = new PathGeometry();
            pathGeometry.BeginFigure(pt1.X, pt1.Y);
            pathGeometry.LineTo(pt2.X, pt2.Y);
            pathGeometry.LineTo(pt3.X, pt3.Y);
            pathGeometry.EndFigure(true);
            this.drawingContext.FillPath(this.GetSolidColorBrush(color), pathGeometry);
        }

        public void DrawArc(System.Drawing.Color color, int radius, System.Drawing.Point start, System.Drawing.Point end, bool clockwise)
        {
            if (drawingContext == null)
            {
                return;
            }
            PathGeometry pathGeometry = new PathGeometry();
            pathGeometry.BeginFigure(start.X, start.Y);
            pathGeometry.ArcTo(new Point(end.X, end.Y), new CPF.Drawing.Size(radius, radius), 0, clockwise, false);
            drawingContext.DrawPath(this.GetSolidColorBrush(color), new Stroke(2), pathGeometry);
        }

        public void FillArc(System.Drawing.Color color, int radius, System.Drawing.Point center, System.Drawing.Point start, System.Drawing.Point end, bool clockwise)
        {
            if (drawingContext == null)
            {
                return;
            }
            PathGeometry pathGeometry = new PathGeometry();
            pathGeometry.BeginFigure(center.X, center.Y);
            pathGeometry.LineTo(start.X, start.Y);
            pathGeometry.ArcTo(new Point(end.X, end.Y), new CPF.Drawing.Size(radius, radius), 0, clockwise, false);
            pathGeometry.EndFigure(true);
            drawingContext.FillPath(this.GetSolidColorBrush(color), pathGeometry);
        }


        /// <summary>
        /// 计算文字尺寸
        /// </summary>
        /// <param name="str"></param>
        /// <param name="fontSize"></param>
        /// <returns></returns>
        public System.Drawing.Size ComputeStringRenderSize(string str, int fontSize)
        {
            if (drawingContext == null || string.IsNullOrEmpty(str))
            {
                return new System.Drawing.Size(0, 0);
            }
            Font font = this.GetFont(fontSize);
            Size size = drawingContext.DrawingFactory.MeasureString(str, font);
            return new System.Drawing.Size((int)size.Width, (int)size.Height);
        }

        /// <summary>
        /// 计算文字尺寸，限定最大宽度
        /// </summary>
        /// <param name="str"></param>
        /// <param name="fontSize"></param>
        /// <param name="maxWidth"></param>
        /// <returns></returns>
        public System.Drawing.Size ComputeStringRenderSize(string str, int fontSize, int maxWidth)
        {
            if (drawingContext == null || string.IsNullOrEmpty(str))
            {
                return new System.Drawing.Size(0, 0);
            }
            Font font = this.GetFont(fontSize);
            Size size = drawingContext.DrawingFactory.MeasureString(str, font, maxWidth);
            return new System.Drawing.Size((int)size.Width, (int)size.Height);
        }


        #region TextEmotion 
        public System.Drawing.Size ComputeTextEmotionRenderSize(string fontName, int fontSize, System.Drawing.Size emotionSize, int maxTextWidth, string contentStr, Dictionary<uint, uint> emotionDic)
        {
            RenderModel renderModel = this.GetContentStr4Draw(fontSize, emotionSize.Width, contentStr, emotionDic, maxTextWidth);
            return this.ComputeStringRenderSize(renderModel.content, fontSize, maxTextWidth);
        }

        public void RenderTextEmotion(System.Drawing.Color backgroundColor, System.Drawing.Color color, System.Drawing.Point position, string fontName, int fontSize, System.Drawing.Size emotionSize, int maxTextWidth, string contentStr, Dictionary<uint, uint> imageDic)
        {
            Point initialPoint = new Point(position.X, position.Y);
            Size emoSize = new Size(fontSize + 3, fontSize + 3);
            if (imageDic == null || imageDic.Count == 0)
            {
                this.DrawString(color, position, contentStr, fontSize, maxTextWidth);
            }
            else
            {
                SolidColorBrush brush = this.GetSolidColorBrush(color);
                SolidColorBrush backColorBrush = this.GetSolidColorBrush(backgroundColor);
                RenderModel renderModel = this.GetContentStr4Draw(fontSize, emoSize.Width, contentStr, imageDic, maxTextWidth);
                string clearTest = renderModel.content;
                int length = clearTest.Length;
                this.DrawString(color, position, clearTest, fontSize, maxTextWidth);
                Dictionary<uint, uint> clearImgDic = renderModel.imageDic;
                List<uint> keys = new List<uint>(clearImgDic.Keys);
                keys.Sort((x, y) => { return x.CompareTo(y); });
                Point point = new Point(initialPoint.X, initialPoint.Y);
                uint startIndex = 0;
                for (int i = 0; i < keys.Count; i++)
                {
                    uint count = keys[i] - startIndex;
                    string str = clearTest.Substring((int)startIndex, (int)count);
                    point = this.ComputeText(initialPoint, str, fontSize, maxTextWidth);
                    float emotionOffsetX = GetEmotionOffsetX(emoSize.Width, fontSize);
                    point.X = point.X + emotionOffsetX;
                    this.DrawEmotion(initialPoint, emotionList[(int)clearImgDic[keys[i]]], point, emoSize, fontSize, str, backColorBrush);
                }
            }
        }

        private float GetEmotionOffsetX(float emotionWidth, int fontSize)
        {
            Rect rect = MeasureHelper.MeasureLastCharRect($"{placeHolder}木{placeHolder}", this.GetFont(fontSize), 188);
            float emotionOffsetX = (float)((rect.X / 2 - emotionWidth / 2));
            return emotionOffsetX;
        }


        private void DrawEmotion(Point initialPoint, Image emotionSource, Point drawPos, Size size, int fontSize, string content, Brush brush)
        {
            bool lineFeed = false;
            if (content.EndsWith("\n"))
            {
                lineFeed = true;
            }
            int lineHeight = (int)GetFont(fontSize).DefaultLineHeight; 
            if (lineFeed)
            {
                drawPos = new Point(initialPoint.X, (float)(drawPos.Y + lineHeight));
            }
            Rect rect = MeasureHelper.MeasureLastCharRect($"{placeHolder}木{placeHolder}", this.GetFont(fontSize), 199);
            drawingContext.FillRectangle(brush, new Rect(drawPos, new Size((float)(rect.X), RenderSettings.TextFontSize + 3)));
            this.DrawImage(emotionSource, new Rect(drawPos, size));
        }



        private Point ComputeText(Point initialPoint, string str, int emSize, double maxTextWidth)
        {
            if (string.IsNullOrEmpty(str))
            {
                return new Point(initialPoint.X, initialPoint.Y + 1);
            }
            return this.GetLastCharPositon(initialPoint, str, this.GetFont(emSize), (float)maxTextWidth);
        }

        public Point GetLastCharPositon(Point pt, string str, Font font, float maxWidth)
        {
            Rect rect = MeasureHelper.MeasureLastCharRect(str, font, maxWidth);
            Point topRight = rect.TopRight;
            topRight.Offset(pt.X, pt.Y);
            return topRight;
        }

        public string RenderTextEmotionSelected(System.Drawing.Color color, System.Drawing.Point position, string fontName, int fontSize, System.Drawing.Size emotionSize, int maxTextWidth, string contentStr, Dictionary<uint, uint> imageDic, System.Drawing.Point selectionStart, System.Drawing.Point selectionEnd, System.Drawing.Color colorSelectionBack)
        {
            Font font = this.GetFont(fontSize);
            int lineHeight = (int)font.DefaultLineHeight;
            Point pt = new Point(position.X, position.Y);
            Point ptStart = new Point(selectionStart.X, selectionStart.Y);
            Point ptEnd = new Point(selectionEnd.X, selectionEnd.Y);
            RenderModel renderModel = GetContentStr4Draw(fontSize, emotionSize.Width, contentStr, imageDic, maxTextWidth);
            string clearTest = renderModel.content;
            if (ptStart.Equals(ptEnd)) { return ""; }
            int startIndex = 0;
            int endIndex = clearTest.Length;
            int charIndex_Down = -1;
            bool goToRight = !this.ExistLeft(ptStart, ptEnd, lineHeight);
            Point lastCharPoint = this.GetLastCharPositon(pt, clearTest, font, maxTextWidth); 
            if (!this.ExistLeft(lastCharPoint, ptStart, lineHeight))//点击的初始点在最后一个字符后面,
            {
                charIndex_Down = clearTest.Length;
            }
            if (this.ExistLeft(pt, ptStart, lineHeight))//点击的字符再第一个字符左边
            {
                charIndex_Down = 0;
            }
            if (charIndex_Down == -1)
            {
                charIndex_Down = this.GetCharIndex(clearTest, pt, ptStart, goToRight, ref startIndex, ref endIndex, font, maxTextWidth);
            }
            int charIndex_Move = this.GetCharIndex(clearTest, pt, ptEnd, goToRight, ref startIndex, ref endIndex, font, maxTextWidth);

            if (charIndex_Down < charIndex_Move)
            {
                startIndex = charIndex_Down;
                endIndex = charIndex_Move;
            }
            else
            {
                startIndex = charIndex_Move;
                endIndex = charIndex_Down;
            }
            Dictionary<uint, uint> dic = renderModel.imageDic;
            Dictionary<uint, uint>.KeyCollection keys = dic.Keys;

            bool isModifyStart = false;
            bool isModifyEnd = false; 
            foreach (uint key in keys)
            {
                if (key < startIndex && startIndex < key + 2 && !isModifyStart)
                {
                    startIndex = (int)key;
                    isModifyStart = true;
                }
                if (key < endIndex && endIndex < key + 2 && !isModifyEnd)
                {
                    endIndex = (int)(key + 2);
                    isModifyEnd = true;
                }
            } 

            #region 绘制文字选中的背景
            SolidColorBrush backBrush = this.GetSolidColorBrush4Select(colorSelectionBack);
            ObjectManager<float, Rect> objectManager = MeasureHelper.GetSelectRects(clearTest, pt, font, maxTextWidth, startIndex, endIndex);
 
            List<Rect> rects = objectManager.GetAll();
            foreach (Rect rect in rects)
            {
                drawingContext.FillRectangle(backBrush, rect);
            } 
            #endregion 
            StringBuilder str = new StringBuilder(); 
            for (int i = 0; i < clearTest.Length; i++)
            {
                if (i >= endIndex)
                {
                    break;
                }
                str.Append(clearTest[i].ToString());
                if (i >= startIndex && i < endIndex)
                {
                    if (clearTest[i] == '木' && i - 1 >= 0)
                    { 
                        if (clearTest[i - 1] == '!' && dic.ContainsKey((uint)i-1))
                        {
                            uint emojiIndex = dic[(uint)i-1];
                            string emojiToken = $"[{(emojiIndex.ToString()).PadLeft(3, '0')}]";
                            str.Remove(str.Length-2, 2);
                            str.Append(emojiToken);
                        }
                    }
                }
            }
            string realStr = str.ToString().Remove(0,startIndex);
            realStr = realStr.Replace("\n",""); 
            return realStr;
        }


        private ObjectManager<string, RenderModel> renderModelManager = new ObjectManager<string, RenderModel>();

        public static bool EqualDictionary(Dictionary<uint, uint> sourceDictionary, Dictionary<uint, uint> targetDictionary)
        {
            //空字典直接返回False,即使是两个都是空字典,也返回False
            if (sourceDictionary == null || targetDictionary == null)
            {
                return false;
            }

            if (object.ReferenceEquals(sourceDictionary, targetDictionary))
            {
                return true;
            }

            if (sourceDictionary.Count != targetDictionary.Count)
            {
                return false;
            }
            //比较两个字典的Key与Value
            foreach (var item in sourceDictionary)
            {
                //如果目标字典不包含源字典任意一项,则不相等
                if (!targetDictionary.ContainsKey(item.Key))
                {
                    return false;
                }

                //如果同一个字典项的值不相等,则不相等
                if (!targetDictionary[item.Key].Equals(item.Value))
                {
                    return false;
                }
            }
            return true;
        }


        //获取要绘制的字符串
        private RenderModel GetContentStr4Draw(int fontSize, double emotionWidth, string contentStr, Dictionary<uint, uint> imageDic, int maxWidth)
        {
            emotionWidth = fontSize + 3;
            RenderModel renderModel = renderModelManager.Get(contentStr);
            if (renderModel != null)
            {
                if (imageDic != null && imageDic.Count == 0)
                {
                    return renderModel;
                }
                if (maxWidth == renderModel.maxWidth && renderModel.fontName == RenderSettings.TextFontName && renderModel.fontSize == fontSize)
                {
                    bool isExist = EqualDictionary(imageDic, renderModel.initialImageDic);
                    if (isExist)
                    {
                        return renderModel;
                    }
                }
            }
            StringBuilder stringBuilder = new StringBuilder(contentStr);
            Dictionary<uint, uint> dic = new Dictionary<uint, uint>();
            Dictionary<uint, uint> dicCopy = new Dictionary<uint, uint>();
            int length = contentStr.Length;
            if (imageDic != null)
            {
                List<uint> list = new List<uint>();
                foreach (uint key in imageDic.Keys)
                {
                    Dictionary<uint, uint>.KeyCollection keys = imageDic.Keys;
                    dic.Add(key, imageDic[key]);
                    list.Add(key);
                }
                if (dic.Count > 0)
                {
                    stringBuilder.Length = 0;
                    list.Sort((x, y) => { return x.CompareTo(y); });
                    for (int i = 0; i < dic.Keys.Count; i++)
                    {
                        uint index_start = i == 0 ? 0 : list[i - 1] + 1;
                        uint count = list[i] - index_start;
                        string str = contentStr.Substring((int)index_start, (int)count);
                        stringBuilder.Append(str);
                        bool isLineFeed = IsLineFeed(RenderSettings.TextFontName, fontSize, stringBuilder.ToString(), maxWidth, emotionWidth);
                        if (isLineFeed)
                        {
                            stringBuilder.Append("\n");
                        }
                        stringBuilder.Append($"{placeHolder}木");
                        uint emojiIndex = dic[list[i]];
                        int index_temp = stringBuilder.ToString().Length - 2;
                        dicCopy.Add((uint)index_temp, emojiIndex);
                        if (i == list.Count - 1)
                        {
                            uint index = list[list.Count - 1];
                            if (index < contentStr.Length)
                            {
                                string laststr = contentStr.Substring((int)index + 1, (int)(contentStr.Length - index - 1));
                                if (index < contentStr.Length - 1)
                                {
                                    stringBuilder.Append(laststr);
                                }
                            }
                        }
                    }
                }
            }
            string clear = stringBuilder.ToString();
            int length1 = clear.Length;
            renderModel = new RenderModel(contentStr, imageDic, clear, dicCopy, maxWidth, RenderSettings.TextFontName, fontSize);
            renderModelManager.Add(contentStr, renderModel);
            return renderModel;
        }

        private int GetCharIndex(string text, Point initialPoint, Point point, bool goToRight, ref int startIndex, ref int endIndex, Font font, int maxWidth)
        {
            int lineHeight = (int)font.DefaultLineHeight;
            Rect rect = MeasureHelper.MeasureLastCharRect(text, font, maxWidth);
            Point point1 = new Point(rect.X, rect.Y);
            point1.Offset(initialPoint.X, initialPoint.Y);
            rect.X = point1.X;
            rect.Y = point1.Y;
            if (rect.Contains(point) || rect.Y + lineHeight < point.Y || (rect.X < point.X && rect.Y <= point.Y))
            {
                return endIndex;
            }
            if (endIndex - startIndex == 1 || startIndex - endIndex == 1)
            {
                return goToRight ? endIndex - 1 : startIndex;
            }
            int centerIndex = startIndex + (endIndex - startIndex) / 2;

            Rect bounds = MeasureHelper.MeasureLastCharRect(text.Substring(0, centerIndex), font, maxWidth);

            Point point2 = new Point(bounds.X, bounds.Y);
            point2.Offset(initialPoint.X, initialPoint.Y);
            bounds.X = point2.X;
            bounds.Y = point2.Y;
            Point basePoint = new Point((bounds.Right - bounds.Left) / 2 + bounds.Left, bounds.Y);
            if (this.ExistLeft(basePoint, point, lineHeight))
            {
                return this.GetCharIndex(text, initialPoint, point, false, ref startIndex, ref centerIndex, font, maxWidth);
            }
            else
            {
                return this.GetCharIndex(text, initialPoint, point, true, ref centerIndex, ref endIndex, font, maxWidth);
            }

        }



        private bool ExistLeft(Point basePoint, Point point, int lineHeigth)
        {
            if (basePoint.Y > point.Y)
            {
                return true;
            }
            if (basePoint.Y + lineHeigth < point.Y)
            {
                return false;
            }
            if (basePoint.X > point.X)
            {
                return true;
            }
            return false;
        }

        private bool IsLineFeed(string fontName, int fontSize, string contentStr, int maxWidth, double emotionWidth)
        {
            Rect rect = MeasureHelper.MeasureLastCharRect(contentStr, this.GetFont(fontSize), maxWidth);
            Rect rect1 = MeasureHelper.MeasureLastCharRect(contentStr + $"{placeHolder}木{placeHolder}", this.GetFont(fontSize), maxWidth);
            return rect.Y - rect1.Y != 0;
        }       
        #endregion
    }

    internal class MeasureHelper
    {

        private static ObjectManager<string, SKTypeface> SKTypefaceManager = new ObjectManager<string, SKTypeface>();

        private static SKTypeface GetFontType()
        {
            try
            { 
                SKTypeface sKTypeface = MeasureHelper.SKTypefaceManager.Get(RenderSettings.TextFontName); 
                if (sKTypeface == null)
                {
                    string path = $"{RenderSettings.TtfResourceDirPath}/{RenderSettings.TextFontName}.ttf";
                      
                    sKTypeface = SKTypeface.FromFile(path);  
                    MeasureHelper.SKTypefaceManager.Add(RenderSettings.TextFontName, sKTypeface); 
                }
                return sKTypeface;
            }
            catch(Exception ex)
            {                
                return null;
            }
        }


        /// <summary>
        /// 计算一个字符的宽度
        /// </summary>
        /// <param name="charText"></param>
        /// <param name="font"></param>
        /// <returns></returns>
        public static double MeasureCharWidth(Char charText, Font font)
        {
            using (SKPaint sKPaint = new SKPaint())
            {
                sKPaint.TextEncoding = SKTextEncoding.Utf8;
                sKPaint.IsAntialias = true;

                sKPaint.Typeface = MeasureHelper.GetFontType();

                sKPaint.TextSize = font.FontSize;
                IList<ValueTuple<string, float>> list = sKPaint.MeasureAllChar(charText.ToString());
                return list[0].Item2;
            }
        }

        /// <summary>
        /// 计算最后一个字符的Rect
        /// </summary>
        /// <param name="str"></param>
        /// <param name="font"></param>
        /// <param name="maxWidth"></param>
        /// <returns></returns>
        public static Rect MeasureLastCharRect(string str, Font font, float maxWidth)
        {
            Rect result;
           
            using (SKPaint sKPaint = new SKPaint())
            {
                sKPaint.TextEncoding = SKTextEncoding.Utf8;
                sKPaint.IsAntialias = true;

                sKPaint.Typeface = MeasureHelper.GetFontType();
                sKPaint.TextSize = font.FontSize;
                sKPaint.Style = (SKPaintStyle)font.FontStyle;
               
                    if (str.Length == 0)
                {
                    str = "!";
                }
                bool flag = (str.Length == 1 || (str.Length == 2 && char.IsSurrogate(str[0]))) && str != " ";
                float heigth = 0;
                int spaceCount = 0;

                if (flag)
                {
                    float num = sKPaint.MeasureString(str);
                    float fontSpacing = sKPaint.FontSpacing;
                    result = new Rect(new Point(0, heigth), new Size(num, fontSpacing));
                }
                else
                {
                    float thinkness = 0;
                    string[] array = str.Split(new char[]
                    {
                        (char)10
                    });

                    int length = str.Length;
                    float width = 0f;
                    for (int i = 0; i < array.Length; i++)
                    {
                        string text = array[i].Trim(new char[]
                        {
                            (char)13
                            });
                            bool flag2 = i == array.Length - 1 && string.IsNullOrEmpty(text);
                            if (flag2)
                            {
                                break;
                            } 
                            IList<ValueTuple<string, float>> list = sKPaint.MeasureAllChar(text);
                             
                            if (font.FontFamily == "微软雅黑" && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                            { 
                                thinkness = (float)sKPaint.FontMetrics.StrikeoutThickness;
                            } 
                            int num3 = 0;
                            bool flag4;
                            do
                            {
                                ValueTuple<int, float, string> valueTuple = BreakText(list, num3, (float)Math.Ceiling((double)maxWidth), false);

                                bool flag3 = num3 + valueTuple.Item1 <= list.Count;
                                if (flag3)
                                {
                                    num3 += valueTuple.Item1;
                                }
                                width = Math.Max(width, valueTuple.Item2);
                                heigth += sKPaint.FontSpacing;
                                spaceCount++;
                                flag4 = (num3 <= list.Count - 1);
                                if (i == array.Length - 1 & !flag4)
                                {
                                    float lastCharWidth = list[list.Count - 1].Item2;
                                    Rect rect = new Rect(new Point(valueTuple.Item2 - lastCharWidth, (float)(heigth - sKPaint.FontSpacing - (spaceCount - 1) * thinkness)), new Size(lastCharWidth, sKPaint.FontSpacing));

                                    
                                    return rect;
                                }
                            }
                            while (flag4);

                    }
                    result = new Rect(new Point(0, (float)((spaceCount - 1) * (sKPaint.FontSpacing - thinkness))), new Size(width, heigth));
                }
            }

            return result;
        }



        /// <summary>
        /// 获取从开始点到起始点经过每一行的矩形集合
        /// </summary>
        /// <param name="str"></param>
        /// <param name="font"></param>
        /// <param name="maxWidth"></param>
        /// <param name="startIndex"></param>
        /// <param name="endIndex"></param>
        /// <returns></returns>
        public static ObjectManager<float, Rect> GetSelectRects(string str, Point position, Font font, float maxWidth, int startIndex, int endIndex)
        {
            float lastOffsetY = 0;
            ObjectManager<float, Rect> objectManager = new ObjectManager<float, Rect>();
            for (int i = startIndex; i < endIndex; i++)
            {
                string content = str.Substring(0, i + 1);
                string lastChar = content.Substring(content.Length - 1, 1);
                if (lastChar == "\n")
                {
                    continue;
                }
                Rect rect = MeasureHelper.GetRect(content, position, font, maxWidth);
                Rect lineRect = new Rect();
                if (lastOffsetY == 0)
                {
                    lastOffsetY = rect.Y;
                }
                if (rect.Y < lastOffsetY)
                {
                    continue;
                }
                if (!objectManager.Contains(rect.Y))
                {
                    lineRect = rect;
                }
                else
                {
                    lineRect = objectManager.Get(rect.Y);
                    lineRect.Width += rect.X + rect.Width - lineRect.X - lineRect.Width;
                    lineRect.Height = rect.Height;
                }
                objectManager.Add(lineRect.Y, lineRect);
            }
            return objectManager;
        }

        public static Rect GetRect(string str, Point position, Font font, float maxWidth)
        {
            Rect rect = MeasureHelper.MeasureLastCharRect(str, font, maxWidth);
            Point point = new Point(rect.X, rect.Y);
            point.Offset(position.X, position.Y);
            rect.X = point.X;
            rect.Y = point.Y;
            return rect;
        }


        public static Rect GetBounds4CharIndex(string str, Font font, float maxWidth, int charIndex)
        {
            Rect result;

            using (SKPaint sKPaint = new SKPaint())
            {
                sKPaint.TextEncoding = SKTextEncoding.Utf8;
                sKPaint.IsAntialias = true;


                sKPaint.Typeface = MeasureHelper.GetFontType();


                sKPaint.TextSize = font.FontSize;
                bool flag = str.Length == 1 || (str.Length == 2 && char.IsSurrogate(str[0]));
                if (flag)
                {
                    float num = sKPaint.MeasureString(str.Remove(charIndex));
                    float fontSpacing = sKPaint.FontSpacing;
                    result = new Rect(new Size(num, fontSpacing));
                }
                else
                {
                    string[] array = str.Split(new char[]
                    {
                        (char)10
                    });
                    float width = 0f;
                    float heigth = 1f;
                    for (int i = 0; i < array.Length; i++)
                    {
                        string text = array[i].Trim(new char[]
                        {
                            (char)13
                        });
                        bool flag2 = i == array.Length - 1 && text == "";
                        if (flag2)
                        {
                            break;
                        }
                        IList<ValueTuple<string, float>> list = sKPaint.MeasureAllChar(text);
                        int num3 = 0;
                        bool flag4;
                        do
                        {
                            ValueTuple<int, float, string> valueTuple = BreakText(list, num3, (float)Math.Ceiling((double)maxWidth), false);

                            if (valueTuple.Item1 >= charIndex)
                            {
                                float x = 0;
                                for (int j = 0; j < charIndex; j++)
                                {
                                    x += list[num3 + j].Item2;
                                }
                                return new Rect(new Point(x, heigth), new Size(list[charIndex].Item2, sKPaint.FontSpacing));
                            }

                            bool flag3 = num3 + valueTuple.Item1 <= list.Count;
                            if (flag3)
                            {
                                num3 += valueTuple.Item1;
                            }
                            width = Math.Max(width, valueTuple.Item2);
                            heigth += sKPaint.FontSpacing;
                            flag4 = (num3 <= list.Count - 1);
                            charIndex -= valueTuple.Item1;

                        }
                        while (flag4);
                    }
                    result = new Rect(new Size(width, heigth));
                }
            }
            return result;
        }

        static ValueTuple<int, float, string> BreakText(IList<ValueTuple<string, float>> list, int start, float maxWidth, bool getText = true)
        {
            float num = 0f;
            int num2 = 0;
            for (int i = start; i < list.Count; i++)
            {
                num2++;
                num += list[i].Item2;
                bool flag = num > maxWidth;
                if (flag)
                {
                    bool flag2 = i != start;
                    if (flag2)
                    {
                        num -= list[i].Item2;
                        num2--;
                    }
                    break;
                }
            }
            string item = string.Empty;
            bool flag3 = num2 > 0 & getText;
            if (flag3)
            {
                StringBuilder stringBuilder = new StringBuilder();
                for (int j = start; j < start + num2; j++)
                {
                    stringBuilder.Append(list[j].Item1);
                }
                item = stringBuilder.ToString();
            }
            return new ValueTuple<int, float, string>(num2, num, item);
        }

    }

    internal class RenderModel
    {
        internal RenderModel(string _initialContent, Dictionary<uint, uint> _initialImageDic, string _content, Dictionary<uint, uint> _imageDic, int _maxWidth, string _fontName, int fontSize)
        {
            this.initialContent = _initialContent;
            this.initialImageDic = _initialImageDic;
            this.content = _content;
            this.imageDic = _imageDic;
            this.maxWidth = _maxWidth;
            this.fontName = _fontName;
            this.fontSize = fontSize;
        }

        internal string initialContent { get; set; }

        internal Dictionary<uint, uint> initialImageDic { get; set; }

        internal string content { get; set; }

        internal Dictionary<uint, uint> imageDic { get; set; }

        internal int maxWidth { get; set; }

        internal string fontName { get; set; }

        internal int fontSize { get; set; }
    }
}
