
using Godot;
using System.Collections.Generic;

namespace Rokojori
{ 
  [Tool]
  [GlobalClass]
  public partial class FontDefinition: Resource
  { 
    [ExportToolButton( "Clear Cache" )]
    public Callable refreshButton => Callable.From( 
      ()=>
      {
        ClearCache();
      }
    );

    [Export]
    public FontGlyph[] glyphs;

    [Export]
    public float defaultWidth = 0.1f;

    [ExportGroup("Height")]
    [Export]
    public float baseToTop;

    [Export]
    public float baseToBottom;

    [ExportGroup("Kerning")]
    [Export]
    public float defaultKerning;

    [Export]
    public FontKerningPair[] kerningPairs;

    HashSet<string> _emptyGlyphs = new HashSet<string>();
    Dictionary<string,FontGlyph> _glyphMap = new Dictionary<string, FontGlyph>();
    Dictionary<string,float> _kerning = new Dictionary<string, float>();

    void ClearCache()
    {
      _emptyGlyphs = new HashSet<string>();
      _glyphMap = new Dictionary<string, FontGlyph>();
      _kerning = new Dictionary<string, float>();
    }

  

    public float GetWidth( string characters )
    {
      var width = 0f;
            
      for ( int i = 0; i < characters.Length; i++ )
      {

        width += GetCharacterWidth( characters[ i ] + "" ); 

        if ( i == 0 )
        { 
          continue;
        } 

        var kerning = GetKerning( characters[ i - 1 ], characters[ i ] );

        width += kerning; 
      }

      return width;
    } 

    public List<float> GetOffsets( string characters )
    {
      var positions = new List<float>();
      var position = 0f;
            
      for ( int i = 0; i < characters.Length; i++ )
      {
        if ( i != 0 )
        {
          position += GetKerning( characters[ i - 1 ], characters[ i ] );
        }

        positions.Add( position );

        position += GetCharacterWidth( characters[ i ] + "" );      
      }

      return positions;
    }

    public float GetKerning( string characterBefore, string characterAfter )
    {
      var gBefore = GetFontGlyph( characterBefore );
      var gAfter  = GetFontGlyph( characterAfter );

      return GetKerning( gBefore == null ? -1 : gBefore.kerningGroup, gAfter == null ? -1 : gAfter.kerningGroup );
    }

    public float GetKerning( int before, int after )
    {
      var kernID = before + "|" + after;
      
      if ( _kerning.ContainsKey( kernID ) )
      {
        return _kerning[ kernID ];
      }

      var kp = kerningPairs.Find( kp => kp.kerningGroupBefore == before && kp.kerningGroupAfter == after );
      _kerning[ kernID ] =  kp == null ? defaultKerning : kp.kerningWidth;        
      
      return _kerning[ kernID ];
    }

    public FontGlyph GetFontGlyph( string character )
    {
      if ( _glyphMap.ContainsKey( character ) )
      {
        return _glyphMap[ character ]; 
      }

      if ( _emptyGlyphs.Contains( character ) )
      {
        return null;
      }

      var glyph = glyphs.Find( g => g.characters == character );

      if ( glyph == null )
      {
        _emptyGlyphs.Add( character );
        return null;
      }

      _glyphMap[ character ] = glyph;

      return glyph;
    }

    public float GetCharacterWidth( string character )
    {
      var g = GetFontGlyph( character );

      return g == null ? defaultWidth : g.width;
    }
  }
}