using Godot;
using System.Text;
using System.Collections.Generic;

namespace Rokojori
{ 
  public class SerializedGodotObjectMember
  {
    public string name;
    public object value;

    public override string ToString()
    {
      return "{" + name + ":" + RJLog.Stringify( value ) + "}";
    }

    public static bool debug = false;

    public override bool Equals( object? obj )
    {
      var other = obj as SerializedGodotObjectMember;

      if ( other == null )
      {
        if ( debug ){ RJLog.Log( "Other is null..." ); }
        return false;
      }     

      if ( name != other.name )
      {
        if ( debug ){ RJLog.Log( "Names are different" ); }
        return false;
      }

      if ( value == null && other.value == null )
      {
        if ( debug ){ RJLog.Log( "Both null" ); }
        return true;
      }

      var result = value == other.value || 
                   value.Equals( other.value ) || 
                   Lists.AreListsAndEntriesEqual( value, other.value ) || 
                   Arrays.AreArraysAndEntriesEqual( value, other.value );

      if ( ! result && debug )
      { 
        if ( debug )
        { 
          RJLog.Log( name, "values not equals", value, other.value, value == other.value, value.Equals( other.value ) ); 
        }
      } 

      return result;
    }
  } 

  public class SerializedGodotObject
  {
    public List<SerializedGodotObjectMember> members = new List<SerializedGodotObjectMember>();

    public override string ToString()
    {
      return "SerializedGodotObject" + RJLog.Stringify( members );
    }

    public override bool Equals( object? obj )
    {
      if ( obj == null )
      {
        return false;
      }
      
      var other = obj as SerializedGodotObject;

      if ( members.Count != other.members.Count )
      {
        // RJLog.Log( "Members not equals count" );
        return false;
      }
      
      for ( int i = 0; i < members.Count; i++ )
      { 
        var member = members[ i ];

        if ( member == null )
        {
          return false ;
        }
        // RJLog.Log( member );
        var otherMember = other.members.Find( m => m.name == member.name );

        if ( otherMember == null || ! member.Equals( otherMember ) )
        {
          if ( otherMember == null )
          {
            // RJLog.Log( member.name + " is not present" );
          }
          else
          {
            // SerializedGodotObjectMember.debug = true;
            // var eq = member.Equals( otherMember );
            // // SerializedGodotObjectMember.debug = false;
            // // RJLog.Log( eq, member.name + " is different, should be:", member.value, "but is: " + otherMember.value );
          }
          
          return false;
        } 
        
      }

      return true;
    }

    public static SerializedGodotObject Create( object obj, Dictionary<object,SerializedGodotObject> cache = null )
    {
      if ( cache == null )
      {
        cache = new Dictionary<object, SerializedGodotObject>();
      }

      cache.Clear();

      var objectData = new SerializedGodotObject();

      var serializer = GodotBuiltInDataLibrary.serializers.Find( s => s.Handles( obj ) );

      if ( serializer != null )
      {
        var serialized = new SerializedGodotObjectMember();
        serialized.name = "data";
        serialized.value = serializer.Serialize( obj );

        objectData.members.Add( serialized );

        return objectData;
      }

      var nodeType = obj.GetType();
      var fields   = nodeType.GetFields();




      for ( int i = 0; i < fields.Length; i++ )
      {
        var isExportedVariable = System.Attribute.IsDefined( fields[ i ], typeof( Godot.ExportAttribute ) );

        if ( ! isExportedVariable )
        {
          continue;
        }

        var name = fields[ i ].Name;

        if ( name.StartsWith( "X_" ) || name.StartsWith( "x_" ) || name.StartsWith( "_" ) )
        {
          continue;
        }

        var value = fields[ i ].GetValue( obj );

        var resourceValue = value as Resource;

        if ( resourceValue != null )
        {
          value = cache.ContainsKey( resourceValue ) ? cache[ resourceValue ] : 
                  SerializedGodotObject.Create( resourceValue, cache );
        } 

        var member = new SerializedGodotObjectMember();

        member.name = name;
        member.value = value;

        objectData.members.Add( member );
      }

      return objectData;

    }
  }

}