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

namespace Rokojori
{ 
  public class RJLog
  {

    public static string GetInfo( object obj, params object[] values )
    {

      if ( obj == null )
      {
        return "<null>"; 
      }

      var sb = new StringBuilder();
      sb.Append( obj.GetType().Name );
      sb.Append( "{ " );

      for ( int i = 0; i < values.Length; i++ )
      {
        if ( i != 0 )
        {
          sb.Append( ", " );
        }
        
        Stringify( values[ i ], sb );
      }

      sb.Append( " }" );

      return sb.ToString();
    }

    public static string Stringify( object obj )
    {
      var sb = new StringBuilder(); 

      Stringify( obj, sb );
      
      return sb.ToString();
    }

    public static void Stringify( object obj, StringBuilder output )
    {
      if ( obj == null )
      {
        output.Append( "<null>" );
        return;
      }

      if ( obj is float )
      {
        var floatValue = (float) obj;
        var doubleValue = (double) floatValue; 
        output.Append( RegexUtility.NumberToString( doubleValue ) );
        return;
      }

      if ( obj is double )
      {
        output.Append( RegexUtility.NumberToString( (double)obj ) );
        return; 
      }

      if ( obj.GetType().IsArray )
      {
        var array = (Array)obj;

        output.Append( "［" );

        var first = true;

        foreach ( var it in array )
        {
          if ( first )
          {
            first = false;
          }
          else
          {
            output.Append( ", " );
          }

          Stringify( it, output );
        }

        output.Append( "］" );

        return;
      }

      if ( obj is IList && obj.GetType().IsGenericType )
      {
        var list = (IList)obj;

        output.Append( "［" );

        var first = true;

        foreach ( var it in list )
        {
          if ( first )
          {
            first = false;
          }
          else
          {
            output.Append( ", " );
          }


          Stringify( it, output );
        }

        output.Append( "］" );

        return;
      }
      
      output.Append( obj.ToString() );
    }

     public static string Stringify( params object[] objects )
    {
      return GetLogString( objects );
    }

    static void LogMessage( string message, int frameIndex = 3 )
    {
      var trace = GetTrace( frameIndex );
      GD.PrintRich("\n[b]" +  message );
      GD.PrintRich( trace );
    }

    static void GDLogMessage( string message, string trace )
    {
      trace = "[color=#888888]  " + trace + "[/color]" ;
      GD.PrintRich("\n[b]" +  message );
      GD.PrintRich( trace );
    }

    static void LogMessageWithFullTrace( string message )
    {
      var trace = GetFullTrace();
      GD.PrintRich("\n[b]" +  message );
      GD.PrintRich( trace );
    }

    static void LogErrorWithFullTrace( string message )
    {
      var trace = GetFullTrace();
      GD.PrintErr("\n" +  message );
      GD.PrintRich( trace );
    }

    static void LogErrorMessage( string message, int frameIndex = 3 )
    {
      var trace = GetTrace( frameIndex );
      GD.PrintErr( "\n"+ message );
      GD.PrintRich( trace );
    }

    static string GetFullTrace()
    {
      return ( new System.Diagnostics.StackTrace( true ) ).ToString();
    }

    static string GetTrace( int frameIndex = 3 )
    {
      var stackTrace = new System.Diagnostics.StackTrace( true );

      var frame = stackTrace.GetFrame( frameIndex );

      var className = frame.GetFileName();

      if ( className != null )
      {
        var slashIndex = -1;
        for ( int i = className.Length - 1; i >= 0 && slashIndex == -1; i-- )
        {
          if ( className[ i ] == '\\' || className[ i ] == '/' )
          {
            slashIndex = i;
          }
        }

        if ( slashIndex != -1 )
        {
          var end = className.EndsWith( ".cs" ) ? className.Length - 3 : className.Length; 
          className = className.Substring( slashIndex + 1, end - ( slashIndex + 1 ) );
        }
      }
      
      if ( className == null )
      {
        className ="<Unknown>";
      } 
      

      var trace = className + "." + frame.GetMethod().Name + "() ln."+ frame.GetFileLineNumber();
      return "[color=#888888]  " + trace + "[/color]" ;
    }

    public static void LogWithFullTrace( params object[] objects)
    {
      LogMessageWithFullTrace( GetLogString( objects ) );
    }

    public static void Log( params object[] objects )
    {
      LogMessage( GetLogString( objects ) );
    }

    public static void LogWithTraceOffset( int offset, params object[] objects )
    {
      LogMessage( GetLogString( objects ), 3 + offset );
    }

    public static void Log( Node node, params object[] objects)
    {
      LogMessage( "[color=#55aaff]" + HierarchyName.Of( node ) + "[/color]\n" + GetLogString( objects ), 4 );
    }

    public static void Log( Resource resource, params object[] objects)
    {
      LogMessage( "[color=#55ffaa]" + HierarchyName.Of( resource ) + "[/color]\n" + GetLogString( objects ), 4 );
    }

    public static void GDLog( string trace, Node node, params object[] objects)
    {
      GDLogMessage( "[color=#55aaff]" + HierarchyName.Of( node ) + "[/color]\n" + GetLogString( objects ), trace );
    }

    public static void GDLog( string trace, Resource resource, params object[] objects)
    {
      GDLogMessage( "[color=#55ffaa]" + HierarchyName.Of( resource ) + "[/color]\n" + GetLogString( objects ), trace );
    }

    public static void Error( params object[] objects)
    {
      LogErrorMessage( GetLogString( objects ) );
    }

    public static void ErrorWithFullTrace( params object[] objects)
    {
      LogErrorWithFullTrace( GetLogString( objects ) );
    }

    public static void Error( Node node, params object[] objects)
    {
      LogErrorMessage( "" +  HierarchyName.Of( node ) + "\n" + GetLogString( objects ), 4 );
    }

    public static void Error( Resource resource, params object[] objects)
    {
      LogErrorMessage( "" +  HierarchyName.Of( resource ) + "\n" + GetLogString( objects ), 4 );
    }

    public static void ErrorGD( GodotObject obj, params object[] objects)
    {
      if ( obj is Node n )
      {
        Error( n, objects );
      }
      else if ( obj is Resource r )
      {
        Error( r, objects );
      }
      else
      {
        LogErrorMessage( "" +  obj + "\n" + GetLogString( objects ), 4 );
      }
    }

   

    public static string GetLogString( object[] objects )
    {
      var sb = new StringBuilder();

      for ( int i = 0; i < objects.Length; i++ )
      {
        if ( i != 0 )
        {
          sb.Append( " " ); 
        }

        Stringify( objects[ i ], sb );
      }

      return sb.ToString();
    }
  }

}
