
using Godot;
using System.Collections.Generic;

namespace Rokojori
{ 
  [Tool]
  [GlobalClass]
  public partial class FontFX:Node3D
  {   
    [ExportToolButton( "Update")]
    public Callable updateButton => Callable.From( ()=>{ _Update(); } );

    [ExportToolButton( "Clear Cache")]
    public Callable clearCacheButton => Callable.From( ()=>{ ClearCache(); } );

    [Export]
    public FontDefinition font
    { 
      get => _font; 
      set
      { 
        _font = value;
        _Update();
      }  
    }

    FontDefinition _font;    
    
    [Export]
    public LocaleText text{ get => _text;  set{ _text = value; _Update(); }  }
    LocaleText _text;

    FontFXLayouter _layouter = new FontFXLayouter();

    Transformable<FontGlyph> _layout;

    Dictionary<string,PackedScene> _glyphMap = new Dictionary<string, PackedScene>();

    void ClearCache()
    {
      _glyphMap = new Dictionary<string, PackedScene>();
    }


    PackedScene GetGlyph( string character )
    {
      if ( _glyphMap.ContainsKey( character ) )
      {
        return _glyphMap[ character ];
      }

      var g = font.GetFontGlyph( character );

      _glyphMap[ character ] = g == null ? null : g.glyphMesh.LoadScene();

      return _glyphMap[ character ];
    }

    void _Update()
    {
      if ( _font == null || _text == null )
      {
        return;
      }

      this.DestroyChildren();

      var realText = _text.currentValue;

      var lines = _layouter.Layout( realText, _font );

      var offset = 0f;

      lines.ForEach(
        ( line )=>
        {
          offset = CreateLine( line, offset );
        }
      );
    }

    float CreateLine( FontFXLine line, float offset )
    {
      line.ComptuteOffsets();

      line.groups.ForEach(
        ( g )=>
        {
          CreateGroup( g, offset );
        }
      );

      return offset + font.baseToBottom + font.baseToTop;
    }

    void CreateGroup( FontFXGroup group, float offset )
    {
      for ( int i = 0; i < group.characters.Length; i++ )
      {
        var sc = GetGlyph( group.characters[ i ] + "" );

        if ( sc == null )
        {
          continue;
        }

        var n = sc.Instantiate<Node3D>();
        n.Name = this.GetChildCount() + "-" + group.characters[ i ].MapCharacterToName();
        n.SetParent( this );
        n.SetLocalX( group.absoluteOffsets[ i ] );
        n.SetLocalY( -offset );
      }
    }
  }
}