using Quik.CommandMachine;
using Quik.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Quik.Typography
{
    public static class Typesetter
    {
        private ref struct LineEnumerator
        {
            private ReadOnlySpan<char> Entire, Segment;
            private bool Final;

            public ReadOnlySpan<char> Current => Segment;

            public LineEnumerator(ReadOnlySpan<char> value)
            {
                Entire = value;
                Segment = ReadOnlySpan<char>.Empty;
                Final = false;
            }

            public void Reset()
            {
                Segment = ReadOnlySpan<char>.Empty;
                Final = false;
            }

            public bool MoveNext()
            {
                if (Final)
                {
                    return false;
                }
                else if (Segment == ReadOnlySpan<char>.Empty)
                {
                    int index = Entire.IndexOf('\n');
                    if (index == -1)
                    {
                        Segment = Entire;
                    }
                    else
                    {
                        Segment = Entire.Slice(0, index);
                    }

                    return true;
                }
                else
                {
                    Entire.Overlaps(Segment, out int offset);

                    if (offset + Segment.Length >= Entire.Length)
                    {
                        return false;
                    }

                    ReadOnlySpan<char> rest = Entire.Slice(offset + Segment.Length + 1);

                    int index = rest.IndexOf('\n');
                    if (index == -1)
                    {
                        Segment = rest;
                        Final = true;
                    }
                    else
                    {
                        Segment = rest.Slice(0, index);
                    }
                    return true;
                }
            }
        }

        public static void TypesetHorizontalDirect(this CommandList list, string str, QVec2 origin, float size, QFont font)
        {
            Dictionary<QImage, FontDrawInfo> drawInfo = new Dictionary<QImage, FontDrawInfo>();
            var enumerator = new LineEnumerator(str.AsSpan());
           
            QVec2 pen = origin;
            while (enumerator.MoveNext())
            {
                ReadOnlySpan<char> line = enumerator.Current;
                float rise = 0.0f;
                float fall = 0.0f;

                // Find out all the code pages required, and the line height.
                foreach (Rune r in line.EnumerateRunes())
                {
                    int codepoint = r.Value;
                    font.Get(codepoint, size, out FontGlyph glyph);
                    float crise = glyph.Metrics.HorizontalBearing.Y;
                    float cfall = glyph.Metrics.Size.Y - crise;

                    rise = Math.Max(crise, rise);
                    fall = Math.Max(cfall, fall);
                }

                pen += new QVec2(0, rise);

                foreach (Rune r in line.EnumerateRunes())
                {
                    FontDrawInfo info;
                    int codepoint = r.Value;

                    font.Get(codepoint, size, out FontGlyph glyph);
                    ref readonly QGlyphMetrics metrics = ref glyph.Metrics;
                    QImage image = glyph.Image;

                    if (image == null)
                    {
                        pen += new QVec2(metrics.Advance.X, 0);
                        continue;
                    }

                    if (!drawInfo.TryGetValue(image, out info))
                    {
                        info = new FontDrawInfo();
                        info.Image = image;
                        info.rectangles = new List<QRectangle>();
                        drawInfo[image] = info;
                    }

                    QRectangle dest = new QRectangle(
                        pen + new QVec2(metrics.HorizontalBearing.X + metrics.Size.X,  metrics.Size.Y - metrics.HorizontalBearing.Y),
                        pen + new QVec2(metrics.HorizontalBearing.X, -metrics.HorizontalBearing.Y));

                    info.rectangles.Add(dest);
                    info.rectangles.Add(glyph.UVs);

                    pen.X += metrics.Advance.X;
                }

                pen.X = origin.X;
                pen.Y += fall;
            }

            // Now for each rectangle we can dispatch draw calls.
            foreach (FontDrawInfo info in drawInfo.Values)
            {
                list.Image(info.Image, info.rectangles.ToArray(), true);
            }
        }

        private struct FontDrawInfo
        {
            public QImage Image;
            public List<QRectangle> rectangles;
        }
    }
}