using Engine.Asset.Font.Metadata;
using Engine.Graphics.Texture;
using OpenTK.Mathematics;

namespace Engine.Asset.Font;

public class Font
{
  public StaticTexture AtlasTexture => _atlasTexture;
  public Metadata.Metadata Metadata => _metadata;
  public Vector2 UnitRange => _unitRange;

  private readonly StaticTexture _atlasTexture;
  private readonly Metadata.Metadata _metadata;

  private readonly Dictionary<int, Glyph> _glyphs = new();
  private readonly Dictionary<int, GlyphData> _glyphData = new();
  private readonly Dictionary<(int, int), Kerning> _kernings = new();
  private readonly Vector2 _unitRange;

  public Font(StaticTexture parAtlasTexture, Metadata.Metadata parMetadata)
  {
    _atlasTexture = parAtlasTexture;
    _metadata = parMetadata;
    _unitRange = new Vector2(_metadata.Atlas.DistanceRange / _metadata.Atlas.Width,
      _metadata.Atlas.DistanceRange / _metadata.Atlas.Height);

    LoadGlyphs();
    LoadKernings();
  }

  public Glyph? GetGlyph(int parUnicode)
  {
    return _glyphs.GetValueOrDefault(parUnicode);
  }

  public GlyphData? GetGlyphData(int parUnicode)
  {
    return _glyphData.GetValueOrDefault(parUnicode);
  }

  public Kerning? GetKerning(int parUnicode1, int parUnicode2)
  {
    return _kernings.GetValueOrDefault((parUnicode1, parUnicode2));
  }

  public FontIterator Iterator(string parText)
  {
    return new FontIterator(this, parText);
  }

  public Vector2 Measure(string parText)
  {
    var fontIterator = Iterator(parText);
    _ = fontIterator.ToList();

    return new Vector2(fontIterator.MaxWidth, fontIterator.MaxHeight);
  }

  private void LoadGlyphs()
  {
    foreach (var glyph in _metadata.Glyphs)
    {
      _glyphs.Add(glyph.Unicode, glyph);

      if (glyph.PlaneBounds == null || glyph.AtlasBounds == null)
      {
        continue;
      }

      _glyphData.Add(glyph.Unicode, new GlyphData(_metadata, glyph));
    }
  }

  private void LoadKernings()
  {
    foreach (var kerning in _metadata.Kerning)
    {
      _kernings.Add((kerning.Unicode1, kerning.Unicode2), kerning);
    }
  }

  public record GlyphData
  {
    public Vector2[] Positions { get; }
    public Vector2[] UVs { get; }

    public GlyphData(in Metadata.Metadata parMetadata, in Glyph parGlyph)
    {
      var size = new Vector2(parMetadata.Atlas.Width, parMetadata.Atlas.Height);
      Positions =
      [
        new Vector2(parGlyph.PlaneBounds!.Left, parGlyph.PlaneBounds.Bottom),
        new Vector2(parGlyph.PlaneBounds.Left, parGlyph.PlaneBounds.Top),
        new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Bottom),
        new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Top),
      ];

      UVs =
      [
        new Vector2(parGlyph.AtlasBounds!.Left, parGlyph.AtlasBounds.Bottom) / size,
        new Vector2(parGlyph.AtlasBounds.Left, parGlyph.AtlasBounds.Top) / size,
        new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Bottom) / size,

        new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Top) / size,
      ];
    }
  }
}