using System;
using System.Buffers;
using System.IO;
using ReFuel.FreeType;
using Dashboard.Media.Color;
using Dashboard.Media.Font;
using OpenTK.Mathematics;

namespace Dashboard.Media.Defaults
{
    public class QFontFreeType : QFont
    {
        private MemoryStream ms;
        private FTFace face;

        public override FontFace Face => throw new NotImplementedException();

        public QFontFreeType(Stream stream)
        {
            ms = new MemoryStream();
            stream.CopyTo(ms);

            FTError e = FT.NewMemoryFace(Ft, ms.GetBuffer(), ms.Length, 0, out face);
            if (e != FTError.None)
            {
                throw new Exception("Could not load font face from stream.");
            }
        }

        public override bool HasRune(int rune)
        {
            return FT.GetCharIndex(face, (ulong)rune) != 0;
        }

        protected override QImage Render(out QGlyphMetrics metrics, int codepoint, float size, in FontRasterizerOptions options)
        {
            FT.SetCharSize(face, 0, (long)Math.Round(64*size), 0, (uint)Math.Round(options.Resolution));

            uint index = FT.GetCharIndex(face, (ulong)codepoint);
            FT.LoadGlyph(face, index, FTLoadFlags.Default);

            ref readonly FTGlyphMetrics ftmetrics = ref face.Glyph.Metrics;
            metrics = new QGlyphMetrics(codepoint,
                new Vector2(ftmetrics.Width/64f, ftmetrics.Height/64f),
                new Vector2(ftmetrics.HorizontalBearingX/64f, ftmetrics.HorizontalBearingY/64f),
                new Vector2(ftmetrics.VerticalBearingX/64f, ftmetrics.VerticalBearingY/64f),
                new Vector2(ftmetrics.HorizontalAdvance/64f, ftmetrics.VerticalAdvance/64f)
            );

            FT.RenderGlyph(face.Glyph, options.Sdf ? FTRenderMode.Sdf : FTRenderMode.Normal);
            ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap;

            if (bitmap.Width == 0 || bitmap.Pitch == 0 || bitmap.Buffer == IntPtr.Zero)
            {
                return null;
            }

            QImageBuffer image = new QImageBuffer(QImageFormat.AlphaU8, (int)bitmap.Width, (int)bitmap.Rows);
            image.LockBits2d(out QImageLock lk, QImageLockOptions.Default);

            unsafe
            {
                Buffer.MemoryCopy((void*)bitmap.Buffer, (void*)lk.ImagePtr, lk.Width * lk.Height, bitmap.Width * bitmap.Rows);
            }

            image.UnlockBits();
            return image;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                ms.Dispose();
            }

            FT.DoneFace(face);
        }

        private static FTLibrary Ft => FTProvider.Ft;
    }
}