//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// 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 {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="ArgumentNullException" />
static Task<int> BaseDo_A( int exclusive,
                           Type type, ComponentBase? component, [HRequired] MethodBase? method,
                           UCircuitExtraParams extraParams, Func<Task> bodyA ) =>
   BaseDo_A( exclusive, type, component, HArgChecking.NN(method).Name, extraParams, null, bodyA );

/// <exception cref="ArgumentNullException" />
static Task<int> BaseDo_A( int exclusive,
                           Type type, ComponentBase? component, [HRequired] MethodBase? method,
                           UCircuitExtraParams extraParams,
                           Func<bool>? preliminary, Func<Task> bodyA ) =>
   BaseDo_A( exclusive, type, component, HArgChecking.NN(method).Name, extraParams,
             preliminary, bodyA );

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

static Task<int> BaseDo_A( int exclusive,
                           Type type, ComponentBase? component, string? methodNameOrSignature,
                           UCircuitExtraParams extraParams, Func<Task> bodyA ) =>
   BaseDo_A( exclusive, type, component, methodNameOrSignature, extraParams, null, bodyA );

static async Task<int> BaseDo_A( int exclusive, Type type, ComponentBase? component,
                                 string? methodNameOrSignature, UCircuitExtraParams extraParams,
                                 Func<bool>? preliminary, Func<Task> bodyA )
{
   if ( IsInFatalError )
      return Int32.MinValue; // SPA is blocked
   try
   {
      return await DoBaseDo_A( exclusive, type, component,
                               methodNameOrSignature, extraParams,
                               preliminary, bodyA );
   }
   catch (Exception e)
   {
      throw UFatalBreakdown.ProcessUnhandledException( e );
   }
}

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

static async Task<int> DoBaseDo_A( int exclusive, Type type, ComponentBase? component,
                                   string? methodNameOrSignature, UCircuitExtraParams extraParams,
                                   Func<bool>? preliminary, Func<Task> bodyA )
{
   try
   {
      HArgChecking.VerifyNotNulls( type, bodyA );
      methodNameOrSignature = HArgChecking.NN( methodNameOrSignature );
      if ( exclusive!=0 && preliminary!=null )
         throw new ArgumentException(
            "Preliminary action can not be specified for non-exclusive operation (\"UCircuit\")." );
   }
   catch (ArgumentException e)
   {
      UBreakdown.ProcessUnhandledException( e );
      goto EXCEPTION_CAUGHT;
   }

   int level = extraParams.ExclusiveApartmentLevel;
   var ld = GetExclusiveLevelData( level, "extraParams.ExclusiveApartmentLevel" );
   var traceOptions = extraParams.TraceOptions;
   var tp = new TraceParameters( traceOptions, level, type, methodNameOrSignature );
   var erp = new ExclusiveRejectionParams(
      traceOptions, exclusive, level, type, methodNameOrSignature );
   ExclusiveOperationInfo? context;
   bool isVisible, asyncShallowPrint;
   try
   {
      if ( !CheckExclusiveRejection( erp ) )
         return 0; // ignored
      isVisible = (traceOptions & UFuncTraceOptions.NoTrace)==0;
      asyncShallowPrint = isVisible && ld.SyncEntryCount_Visible > 0;
      if ( asyncShallowPrint )
      {
         PrintAsyncShallowIn( tp );
         ld.TraceSyncExit = true;
      }
      else
         PrintMethodTitle( tp );
      context = exclusive != 0 ?
         PrepareExclusiveInfo( exclusive, level, type, component, methodNameOrSignature ) : null;
      if ( exclusive==-1 && ResetExplicitExclusiveLock() ) // master
         Trace( traceOptions,
            "Explicit exclusive lock was reseted by: [{0}] {1}.",
            type.Name, methodNameOrSignature );
      if ( exclusive == 2 ) //exclusive interception
      {
         var previousCtx = ld.LastContext;
         if ( previousCtx == null )
            throw HInvOpExceptionCreator.Create(
               "Exclusive interception has failed in: [{0}] {1}.\n"+
               "Previous exclusiveness context is not found.",
               type.Name, methodNameOrSignature );
         if ( !previousCtx.AwaitInterception )
            throw HInvOpExceptionCreator.Create(
               "Exclusive interception has failed in: [{0}] {1}.\n"+
               "\"AwaitInterception\"-flag has not been set by previous context.\n"+
               "Previous exclusiveness context: [{2}] {3}.",
               type.Name, methodNameOrSignature,
               previousCtx.Type.Name, previousCtx.MethodNameOrSignature );
         previousCtx.Closed = previousCtx.Intercepted = true;
         Trace( traceOptions,
            "Exclusive operation of \"{0}.{1}\" was intercepted by: [{2}] {3}.",
            previousCtx.Type, previousCtx.MethodNameOrSignature,
            type.Name, methodNameOrSignature );
      }
   }
   catch (Exception e) when ( e is ArgumentException ||
                              e is InvalidOperationException )
   {
      UBreakdown.ProcessUnhandledException( e );
      goto EXCEPTION_CAUGHT;
   }

   bool escape=false, exceptionCaught=false;

   if ( context!=null && preliminary!=null )
   {
      bool selfRefusal;
      try
      {
         selfRefusal = !preliminary.Invoke();
      }
      catch (Exception e)
      {
         PROCESS_EXCEPTION( e );
         goto QUIT;
      }
      if ( selfRefusal )
      {
         Trace( traceOptions, "REFUSAL" );
         CloseExclusiveOperation( context );
         return -2; // self-refusion in preliminary block (by caller)
      }
   }

   ld.AsyncEntryCount++;
   if ( isVisible )
      ld.AsyncEntryCount_Visible++;
   bool fireEvents = exclusive!=0 && component!=null;
   try
   {
      if ( fireEvents )
      {
         ExclusiveBegin?.Invoke( component );
         foreach (var callbackA in ExclusiveBeginA)
#pragma warning disable 4014
            callbackA.Invoke( component );
#pragma warning restore 4014
      }
      await bodyA();
   }
   catch (Exception e)
   {
      PROCESS_EXCEPTION( e );
   }
   if ( context != null )
      CloseExclusiveOperation( context );
   ld.AsyncEntryCount--;
   if ( isVisible )
      ld.AsyncEntryCount_Visible--;
   if ( asyncShallowPrint || TraceFuncExit( traceOptions ) )
      PrintAsyncShallowOut( tp );
   if ( fireEvents )
   {
      try
      {
         ExclusiveEnd?.Invoke( component );
         foreach (var callbackA in ExclusiveEndA)
#pragma warning disable 4014
            callbackA.Invoke( component );
#pragma warning restore 4014
      }
      catch (Exception e)
      {
         PROCESS_EXCEPTION( e );
      }
   }
   if ( escape || exceptionCaught )
      goto QUIT;
   return 1; // done

ESCAPE:
   return -1; // escaped
EXCEPTION_CAUGHT:
   return Int32.MinValue+1; // unhandled exception was caught and reported
QUIT:
   if ( exceptionCaught ) goto EXCEPTION_CAUGHT;
   if ( escape ) goto ESCAPE;
   throw HAssertionFailure.DEAD_BRANCH();

   void PROCESS_EXCEPTION(Exception e)
   {
      if (e is UEscapeException)
      {
         escape = true;
         UDebugConsole.Print( HException.ComposeChainedMessage( e ) );
         SetEscapeRequested();
      }
      else
      {
         exceptionCaught = true;
         fireEvents = false;
         UBreakdown.ProcessUnhandledException( e );
      }
   }
}

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

} // AFXUI
