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

using CommonHelpers.WASM;

namespace AFXUI {

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

public const int PollInterval = 100; // milliseconds

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

public static event Action<ComponentBase?>?
   ExclusiveBegin, ExclusiveEnd;
public static readonly HListSet<Func<ComponentBase?,Task>>
   ExclusiveBeginA = new HListSet<Func<ComponentBase?,Task>>(),
   ExclusiveEndA = new HListSet<Func<ComponentBase?,Task>>();

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

public static bool HasExclusiveOperation =>
   Array.Exists( _ExclusiveLevels_, ld => ld.Context!=null );
public static bool HasExplicitExclusiveLock => _HasExplicitExclusiveLock_;

/// <exception cref="InvalidOperationException" />
public static bool WasXOpAbandonedOrCancelled => _WasXOpAbandonedOrCancelled_;
/// <exception cref="InvalidOperationException" />
public static bool WasExclusiveOpCancelled => _WasExclusiveOpCancelled_;

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

/// <exception cref="ArgumentOutOfRangeException" />
public static bool HasExclusiveOperationAt(int level) =>
   GetExclusiveContextAt( level ) != null;

/// <exception cref="ArgumentOutOfRangeException" />
public static int SyncEntryCountAt(int level) =>
   GetExclusiveLevelData( level ).SyncEntryCount;
/// <exception cref="ArgumentOutOfRangeException" />
public static int AsyncEntryCountAt(int level) =>
   GetExclusiveLevelData( level ).AsyncEntryCount;

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

public static bool CloseExclusiveOperation() =>
   CloseExclusiveOperationAt( 0 );
/// <exception cref="ArgumentOutOfRangeException" />
public static bool CloseExclusiveOperationAt(int level)
{
   var context = GetExclusiveContextAt( level );
   return context!=null ? CloseExclusiveOperation( context ) : false;
}

public static bool LeaveExclusiveOperationAt() =>
   LeaveExclusiveOperationAt( 0 );
/// <exception cref="ArgumentOutOfRangeException" />
public static bool LeaveExclusiveOperationAt(int level)
{
   var context = GetExclusiveContextAt( level );
   if ( context == null )
      return false;
   _ExclusiveLevels_[ level ].Context = context.Duplicate();
   context.Conceded = true;
   return true;
}

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

public static bool HasExclusiveCancellation() =>
   _ExclusiveCancellation_ != null;
/// <exception cref="InvalidOperationException" />
public static void SetExclusiveCancellation(CancellationTokenSource source) =>
   _ExclusiveCancellation_ = source;
/// <exception cref="InvalidOperationException" />
public static void ResetExclusiveCancellation() =>
   _ExclusiveCancellation_ = null;

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

public static bool CancelExclusiveOperation()
{
   if ( _ExclusiveCancellation_==null || _WasExclusiveOpCancelled_ )
      return false;
   _WasXOpAbandonedOrCancelled_ = true;
   _ExclusiveCancellation_.Cancel();
   return _WasExclusiveOpCancelled_ = true;
}

public static Task WaitExclusiveOperation_A() =>
   HAsyncAwait.WaitFor_A( PollInterval, () => !HasExclusiveOperation );

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

public static bool SetExplicitExclusiveLock() =>
   !HasExplicitExclusiveLock ? (_HasExplicitExclusiveLock_ = true) : false;

public static bool ResetExplicitExclusiveLock()
{
   if ( !HasExplicitExclusiveLock )
      return false;
   _HasExplicitExclusiveLock_ = false;
   return true;
}

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

public static Task WaitFor_A(Func<bool> condition) =>
   HAsyncAwait.WaitFor_A( PollInterval, condition );

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

/// <exception cref="InvalidOperationException" />
public static void VerifyExclusiveActive()
{
   if ( !HasExclusiveOperation )
      throw new InvalidOperationException(
         "Exclusive asynchronous operation is unexpectedly inactive (at all apartmet levels)." );
}

/// <exception cref="InvalidOperationException" />
public static void VerifyExclusiveNotActive()
{
   for (int l=0; l<ExclusiveLevelCount; l++)
      if ( HasExclusiveOperationAt( l ) )
         throw HInvOpExceptionCreator.Create(
            "Exclusive asynchronous operation is unexpectedly active (at apartmet-level {0}).",
            l );
}

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

/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="InvalidOperationException" />
public static void VerifyExclusiveActiveAt(int level) =>
   StrictExclusiveContextAt( level );

/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="InvalidOperationException" />
public static void VerifyExclusiveNotActiveAt(int level)
{
   if ( HasExclusiveOperation )
      throw HInvOpExceptionCreator.Create(
         "Exclusive asynchronous operation at apartmet-level {0} is unexpectedly active.",
         level );
}

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

static UCircuit()
{
   _ExclusiveLevels_ = new ExclusiveOpLevelData[ ExclusiveLevelCount ];
   for (int l=0; l<ExclusiveLevelCount; l++)
      _ExclusiveLevels_[ l ] = new ExclusiveOpLevelData();
}

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

/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="InvalidOperationException" />
[HStrictResult]
static ExclusiveOperationInfo StrictExclusiveContextAt(int level)
{
   var context = GetExclusiveContextAt( level );
   if ( context == null )
      throw HInvOpExceptionCreator.Create(
         "Exclusive asynchronous operation at apartmet-level {0} is not active.",
         level );
   return context;
}

/// <exception cref="ArgumentOutOfRangeException" />
static ExclusiveOperationInfo? GetExclusiveContextAt(int level) =>
   GetExclusiveLevelData( level ).Context;

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

/// <exception cref="ArgumentOutOfRangeException" />
static ExclusiveOpLevelData GetExclusiveLevelData(int level,string? argumentName=null)
{
   if ( !HOps.IsBetween( level, 0, ExclusiveLevelCount-1 ) )
      throw new ArgumentOutOfRangeException( argumentName ?? nameof(level), string.Format(
         "Incorrect exclusive operation apartmet-level: {0} (not from [0..{1}] diapason).",
         level, ExclusiveLevelCount-1 ));
   return _ExclusiveLevels_[ level ];
}

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

/// <exception cref="InvalidOperationException" />
static ExclusiveOperationInfo PrepareExclusiveInfo( int exclusive, int level,
                                                    Type type, ComponentBase? component,
                                                    string methodNameOrSignature )
{
   if ( exclusive != 2 ) // not interception
      VerifyExclusiveNotActiveAt( level );
   var ld = GetExclusiveLevelData( level );
   if ( ld.Context != null )
      ld.LastContext = ld.Context;
   return ld.Context = new ExclusiveOperationInfo(
      level, type, component, methodNameOrSignature );
}

static bool CloseExclusiveOperation(ExclusiveOperationInfo context)
{
   if ( context.Conceded || context.Closed )
      return false;
   context.Closed = true;
   var ld = GetExclusiveLevelData( context.Level );
   ld.LastContext = HOps.NullExchange(ref ld.Context);
   _ExclusiveCancellation_ = null;
   _WasXOpAbandonedOrCancelled_ =
      _WasExclusiveOpCancelled_ = false;
   return true;
}

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

static bool CheckBlocking( Type type, string? methodNameOrSignature ) =>
   UInputBlocking.CheckBlocking( type, methodNameOrSignature ?? "???" );
static bool CheckBlocking( ComponentBase component, string? methodNameOrSignature ) =>
   UInputBlocking.CheckBlocking( component.GetType(), methodNameOrSignature ?? "???" );
/// <exception cref="ArgumentNullException" />
static bool CheckBlocking( [HRequired] MethodBase? method ) =>
   UInputBlocking.CheckBlocking( method );

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

[JSInvokable]
public static void wasm_CancelPendingOperation() =>
   CancelExclusiveOperation();

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

const int ExclusiveLevelCount = 5;

static readonly ExclusiveOpLevelData[]
   _ExclusiveLevels_;
static bool
   _HasExplicitExclusiveLock_, _WasXOpAbandonedOrCancelled_, _WasExclusiveOpCancelled_;
static CancellationTokenSource?
   _ExclusiveCancellation_;

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

} // AFXUI
