//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define TRACE
#define DEBUG

using System;
using System.Diagnostics;

#if !WASM
namespace user_rtx { using u = user_rtx;
#else
namespace user_rtx.wasm { using u = user_rtx.wasm;
#endif

/// <summary>
/// Trace implementation (internal instance class)
/// </summary>
public class Tracer {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static readonly object
   SyncRoot = new object();

//==================================================================================================

internal Tracer( bool debug ) =>
   _debug = debug;

//==================================================================================================

internal void Print() =>
   Print( "" );

internal void Print( string message ) =>
   Print( u.TraceMessageCategory.Neutral, message );

internal void Print( string messageFmt, params object?[] args ) =>
   Print( u.TraceMessageCategory.Neutral, messageFmt.FMT( args ) );

internal void Print( object obj ) =>
   Print( u.TraceMessageCategory.Neutral, obj.STR( false ) );

//--------------------------------------------------------------------------------------------------

internal void Print( u.TraceMessageCategory messageCategory, string message )
{
   bool isActiveCategory = messageCategory != u.TraceMessageCategory.None;
   if ( RELEASE && !isActiveCategory )
      return;
   bool isFictitiousTest = DEBUG && !isActiveCategory;
   lock ( SyncRoot ) {
      bool w32Trace = u.ops.False;//u.platform.IsWin32;
      bool dotNetTrace = !w32Trace;
      int indentLevel = dotNetTrace ? Trace.IndentLevel : 0;
      if ( dotNetTrace && !isFictitiousTest )
         if ( _debug )
            Debug.IndentLevel = 0;
         else
            Trace.IndentLevel = 0;
      try {
         string outputString = u.str.FMT(
            "{0}{1}{2} {3}",
            u.ops.Exchange( ref _wasPrinted, true ) ? "" : Environment.NewLine,
            !urtx.IsWASM ? (u.program.SimpleName+" ") : "",
            GetMessageCategotyToken( messageCategory ),
            message );
         if ( isFictitiousTest )
            return;
         if ( u.platform.IsWin32 )
            outputString = u.str.AsAnsi( outputString );
         if ( dotNetTrace )
            if ( _debug ) {
               Debug.WriteLine( outputString );
               Debug.Flush();
            }
            else {
               Trace.WriteLine( outputString );
               Trace.Flush();
            }
         if ( w32Trace )
            w32_core.OutputDebugString( outputString );
      }
      finally {
         if ( dotNetTrace && !isFictitiousTest )
            if ( _debug )
               Debug.IndentLevel = indentLevel;
            else
               Trace.IndentLevel = indentLevel;
      }
   }
}

internal void Print( u.TraceMessageCategory messageCategory,
                     string messageFmt, params object?[] args ) =>
   Print( messageCategory, messageFmt.FMT( args ) );

//--------------------------------------------------------------------------------------------------

internal void PrintInfo( string message ) =>
   Print( u.TraceMessageCategory.Information, message );

internal void PrintInfo( string messageFmt, params object?[] args ) =>
   Print( u.TraceMessageCategory.Information, messageFmt.FMT( args ) );

//--------------------------------------------------------------------------------------------------

internal void PrintComment( string message ) =>
   PrintInfo( "## {0}", message );

internal void PrintComment( string messageFmt, params object?[] args ) =>
   PrintComment( messageFmt.FMT( args ) );

//--------------------------------------------------------------------------------------------------

internal void PrintWarning( string message ) =>
   Print( u.TraceMessageCategory.Warning, message );

internal void PrintWarning( string messageFmt, params object?[] args ) =>
   Print( u.TraceMessageCategory.Warning, messageFmt.FMT( args ) );

//--------------------------------------------------------------------------------------------------

internal void PrintError( string message ) =>
   Print( u.TraceMessageCategory.Error, message );

internal void PrintError( string messageFmt, params object?[] args ) =>
   Print( u.TraceMessageCategory.Error, messageFmt.FMT( args ) );

internal void PrintError( Exception e ) =>
   PrintError( u.except.ChainedMessage( e ) );

//==================================================================================================

internal void PrintOperation( string message ) =>
   PrintOperation( false, message );

internal void PrintOperation( string messageFmt, params object?[] args ) =>
   PrintOperation( false, messageFmt, args );

//--------------------------------------------------------------------------------------------------

internal void PrintOperation( bool informationCategory, string message )
{
   Print( informationCategory ?
      u.TraceMessageCategory.Information :
      u.TraceMessageCategory.Neutral,
      "* {0} ...", u.str.AsSingleLine( message ) );
}

internal void PrintOperation( bool informationCategory,
                              string messageFmt,
                              params object?[] args ) =>
   PrintOperation( informationCategory, messageFmt.FMT( args ) );

//==================================================================================================

internal void DetailedPrint( string caption, string details ) =>
   DetailedPrint( u.TraceMessageCategory.Neutral, caption, details );

internal void DetailedPrint( string caption, string detailsFmt, params object?[] args ) =>
   DetailedPrint( u.TraceMessageCategory.Neutral, caption, detailsFmt, args );

//--------------------------------------------------------------------------------------------------

internal void DetailedPrint( u.TraceMessageCategory messageCategory,
                             string caption, string details ) =>
   Print( messageCategory,
      "=== {0}: ===\n{1}",
      u.str.AsSingleLine( caption ),
      details );

internal void DetailedPrint( u.TraceMessageCategory messageCategory, string caption,
                             string detailsFmt, params object?[] args ) =>
   DetailedPrint( messageCategory, caption, detailsFmt.FMT( args ) );

//--------------------------------------------------------------------------------------------------

internal void DetailedPrintInfo( string caption, string details ) =>
   DetailedPrint( u.TraceMessageCategory.Information, caption, details );

internal void DetailedPrintInfo( string caption, string detailsFmt, params object?[] args ) =>
   DetailedPrint( u.TraceMessageCategory.Information, caption, detailsFmt, args );

//--------------------------------------------------------------------------------------------------

internal void DetailedPrintWarning( string caption, string details ) =>
   DetailedPrint( u.TraceMessageCategory.Warning, caption, details );

internal void DetailedPrintWarning( string caption, string detailsFmt, params object?[] args ) =>
   DetailedPrint( u.TraceMessageCategory.Warning, caption, detailsFmt, args );

//--------------------------------------------------------------------------------------------------

internal void DetailedPrintError( string caption, string details ) =>
   DetailedPrint( u.TraceMessageCategory.Error, caption, details );

internal void DetailedPrintError( string caption, string detailsFmt, params object?[] args ) =>
   DetailedPrint( u.TraceMessageCategory.Error, caption, detailsFmt, args );

internal void DetailedPrintError( u.TraceMessageCategory messageCategory,
                                  string caption, Exception e ) =>
   DetailedPrintError( caption, u.except.ChainedMessage( e ) );

//==================================================================================================

/// <exception cref="ArgumentOutOfRangeException" />
static string GetMessageCategotyToken( u.TraceMessageCategory messageCategory )
{
   switch ( messageCategory )
   {
   case u.TraceMessageCategory.None       : return "";
   case u.TraceMessageCategory.Neutral    : return ">>";
   case u.TraceMessageCategory.Information: return "I:";
   case u.TraceMessageCategory.Warning    : return "W:";
   case u.TraceMessageCategory.Error      : return "E:";
   } // switch
   throw new ArgumentOutOfRangeException(
      "Incorrect trace message category enum-value.",
      nameof(messageCategory) );
}

//--------------------------------------------------------------------------------------------------

public static bool DEBUG =>
#if DEBUG
   true;
#else // !DEBUG:
   false;
#endif // !DEBUG

public static bool RELEASE =>
#if RELEASE
   true;
#else // !RELEASE:
   false;
#endif // !RELEASE

//==================================================================================================

static bool _wasPrinted;

bool _debug;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // Tracer

} // user_rtx (.wasm)
