//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// AFXUI auxiliary module for Blazor-application (WASM) | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;

using CommonHelpers.WASM;

namespace AFXUI {

//**************************************************************************************************
static partial class UCircuit {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static bool IsInFatalError => UFatalBreakdown.IsInFatalError;

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

/// <exception cref="ArgumentNullException" />
static int BaseDo( int exclusive,
                   Type type, ComponentBase? component, [HRequired] MethodBase? method,
                   UCircuitExtraParams extraParams, Action body ) =>
   BaseDo( exclusive, type, component, HArgChecking.NN(method).Name, extraParams, body );

static int BaseDo( int exclusive, Type type, ComponentBase? component,
                   string? methodNameOrSignature, UCircuitExtraParams extraParams, Action body )
{
   if ( IsInFatalError )
      return Int32.MinValue; // SPA is blocked
   try
   {
      return DoBaseDo( exclusive, type, component,
                       methodNameOrSignature, extraParams, body );
   }
   catch (Exception e)
   {
      throw UFatalBreakdown.ProcessUnhandledException( e );
   }
}

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

static int DoBaseDo( int exclusive, Type type, ComponentBase? component,
                     string? methodNameOrSignature, UCircuitExtraParams extraParams, Action body )
{
   try
   {
      if ( HOps.IsOneOf( exclusive, 2 ) )
         throw new ArgumentException(HString.FormatAsText(
            "Wrong value was specified for synchronous exclusive incocation mode: {0}.\n"+
            "Permitted synchronous values are: 0 (non-exclusive), 1 (gen.exclusive), "+
            "-1 (master).", exclusive ),
            nameof( exclusive ) );
      HArgChecking.VerifyNotNulls( type, body );
      methodNameOrSignature = HArgChecking.NN( methodNameOrSignature );
   }
   catch (ArgumentException e)
   {
      UBreakdown.ProcessUnhandledException( e );
      return Int32.MinValue+1; // unhandled exception was caught and reported
   }

   int level = extraParams.ExclusiveApartmentLevel;
   var ld = GetExclusiveLevelData( level, "extraParams.ExclusiveApartmentLevel" );
   var traceOptions = extraParams.TraceOptions;
   var tp = new TraceParameters( traceOptions, level, type, methodNameOrSignature );
   try
   {
      if ( !CheckExclusiveRejection(new ExclusiveRejectionParams(
           traceOptions, exclusive, level, type, methodNameOrSignature )) )
         return 0; // ignored
   }
   catch (Exception e) when ( e is ArgumentException ||
                              e is InvalidOperationException )
   {
      UBreakdown.ProcessUnhandledException( e );
      return Int32.MinValue+1; // unhandled exception was caught and reported
   }

   var context = exclusive != 0 ?
      PrepareExclusiveInfo( exclusive, level, type, component, methodNameOrSignature ) : null;
   if ( exclusive==-1 && ResetExplicitExclusiveLock() ) // master
      Trace( traceOptions,
         "Explicit exclusive lock was reset by: [{0}] {1} (synchronous).",
         type.Name, methodNameOrSignature );
   bool isVisible = (traceOptions & UFuncTraceOptions.NoTrace)==0,
        syncShallowPrint = false;
   ld.SyncEntryCount++;
   if ( isVisible )
      if ( ++ld.SyncEntryCount_Visible > 1 )
         PrintInnerMethodIn( tp );
      else
         if ( ld.AsyncEntryCount_Visible > 0 )
         {
            syncShallowPrint = true;
            PrintShallowTitle( tp );
         }
         else
            PrintMethodTitle( tp );

   bool escape=false, exceptionCaught=false;
   try
   {
      body();
   }
   catch (UEscapeException e)
   {
      escape = true;
      UDebugConsole.Print( HException.ComposeChainedMessage( e ) );
      SetEscapeRequested();
   }
   catch (Exception e)
   {
      exceptionCaught = true;
      UBreakdown.ProcessUnhandledException( e );
   }
   if ( context != null )
      CloseExclusiveOperation( context );
   if ( ld.SyncEntryCount_Visible > 1 )
      PrintInnerMethodOut( tp );
   ld.SyncEntryCount--;
   if ( isVisible )
      ld.SyncEntryCount_Visible--;
   if ( syncShallowPrint )
      PrintShallowOut( tp );
   else
      if ( ld.SyncEntryCount_Visible==0 && ld.TraceSyncExit || TraceFuncExit( traceOptions ) )
         PrintOuterMethodOut( tp );
   if ( isVisible && ld.SyncEntryCount_Visible==0 )
      ld.TraceSyncExit = false;
   if ( exceptionCaught )
      return Int32.MinValue+1; // unhandled exception was caught and reported
   if ( escape )
      return -1; // escaped
   return 1; // done
}

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

/// <exception cref="ArgumentException" />
/// <exception cref="InvalidOperationException" />
static bool CheckExclusiveRejection(ExclusiveRejectionParams erp)
{
   var traceOptions = erp.TraceOptions;
   int exclusive = erp.Exclusive;
   int level = erp.Level;
   Type type = erp.Type;
   string methodNameOrSignature = erp.MethodNameOrSignature;
   if ( exclusive == 0 )
   {
      if ( level > 0 )
         throw new ArgumentException(
            "Exclusive apartment level must not be specified for nonexclusive operation." );
      return true;
   }
   if ( !HOps.IsOneOf( exclusive, 1,2,-1 ) )
      throw new ArgumentException(HString.FormatAsText(
         "Wrong value was specified for exclusive invocation mode: {0}.\n"+
         "Supported values are: 0 (non-exclusive), 1 (gen.exclusive), 2 (interception), "+
         "-1 (master).", exclusive ),
         nameof( exclusive ) );
   if ( IsEscapeRequested )
      return RejectExclusive_Escape( erp );
   var context = GetExclusiveContextAt( erp.Level );
   if ( context != null )
   {
      if ( exclusive == -1 ) // exclusive master
         throw HInvOpExceptionCreator.Create(
            "Attempt of invocation in exclusive mode as master (-1) when another exclusive "+
            "operation is active.\n{0}",
            ExclusiveRejectedBy_Msg( context, true ) );
      if ( exclusive == 2 ) //exclusive interception
         return true;
      return RejectExclusive( erp, "allready active", context );
   }
   if ( HasExplicitExclusiveLock && exclusive != -1 ) // not exclusive master
      return RejectExclusive( erp, "explicit lock" );
   return true;
}

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

static bool RejectExclusive( ExclusiveRejectionParams erp,
                             string causeText, ExclusiveOperationInfo? info=null )
{
   Trace( erp.TraceOptions,
      "Exclusive rejection ({0}) has been rased in: [{1}] {2}.{3}{4}",
      causeText, erp.Type.Name, erp.MethodNameOrSignature,
      info!=null ? "\n"+ExclusiveRejectedBy_Msg( info ) : "",
      erp.Level>0 ? $"\nThis collision has occured at apartment-level: {erp.Level}." : "" );
   return false;
}
static bool RejectExclusive_Escape( ExclusiveRejectionParams erp ) =>
   RejectExclusive( erp, "escape requested" );

static string ExclusiveRejectedBy_Msg( ExclusiveOperationInfo info, bool forThrowing=false ) =>
   string.Format( "({0}: \"{1}.{2}\".)",
      forThrowing ? "Blocked by" : "Rejected by",
      info.Type, info.MethodNameOrSignature );

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // UCircuit
//**************************************************************************************************

} // AFXUI
