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

using CommonHelpers.WASM;

namespace AFXUI {

//**************************************************************************************************
public abstract partial class UComponentBase: OwningComponentBase,IDisposable {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public string UniqueId { get; } = Guid.NewGuid().ToString();

public bool CancelExclusiveOperationOnDispose { get; set; }

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

public bool WasLoaded { get; private set; }
public bool WereParametersSet { get; private set; }
public bool WasDisposed { get; private set; }

public void Dispose()
{
   try { OnDispose(); }
   finally { WasDisposed = true; }
}

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

public void InvokeStateHasChanged() => StateHasChanged();

[HStrictResult]
public T GetService<T>() where T: class
{
   var service = ScopedServices.GetService( typeof(T) ) as T;
   return service ?? throw HAssertion.FAIL();
}

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

/// <exception cref="InvalidOperationException" />
/// <exception cref="HUnsuccessException" />
[HStrictResult]
public static UComponentBase ByUniqueId(string uniqueId) =>
   GetByUniqueId( uniqueId ) ?? throw HUnsuccessExceptionCreator.Create(
      "Component not found by its unique identifier: \"{0}\" (\"UComponentBase\").",
      uniqueId );
/// <exception cref="InvalidOperationException" />
/// <exception cref="HUnsuccessException" />
[HStrictResult]
public static R ByUniqueId<R>(string uniqueId)
where R: UComponentBase =>
   CastComponent<R>( ByUniqueId(uniqueId), uniqueId );

public static UComponentBase? GetByUniqueId(string uniqueId)
{
   UComponentBase? instance;
   return _Instances_.TryGetValue(uniqueId,out instance) ? instance : null;
}
/// <exception cref="HUnsuccessException" />
public static R? GetByUniqueId<R>(string uniqueId)
where R: UComponentBase
{
   var component = GetByUniqueId( uniqueId );
   return component != null ?
      CastComponent<R>( component, uniqueId ) :
      null;
}

/// <exception cref="HUnsuccessException" />
static R CastComponent<R>(UComponentBase component,string uniqueId)
where R: UComponentBase =>
   (HArgChecking.VerifyNotNull(component) as R) ??
      throw HUnsuccessExceptionCreator.Create(
         "Component with unique identifier \"{0}\" is not of \"{1}\"-type (\"UComponentBase\").",
         uniqueId, typeof(R) );

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

public static bool HasExclusiveOperation => UCircuit.HasExclusiveOperation;
/// <exception cref="InvalidOperationException" />
public static bool WasXOpAbandonedOrCancelled => UCircuit.WasXOpAbandonedOrCancelled;
/// <exception cref="InvalidOperationException" />
public static bool WasExclusiveOpCancelled => UCircuit.WasExclusiveOpCancelled;

/// <exception cref="ArgumentOutOfRangeException" />
public static bool HasExclusiveOperationAt(int level) =>
   UCircuit.HasExclusiveOperationAt( level );

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

/// <exception cref="Exception" />
public static void ProcessRenderingError( Type componentType, Exception e,
                                          bool navigateToErrorPage = true ) =>
   UBreakdown.ProcessRenderingError( componentType, e, navigateToErrorPage );


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

protected bool IsPageComponent { get; }

protected bool AvoidThickRendering { get; set; }

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

public UComponentBase(): base()
{
   _Instances_.Add( UniqueId, this );
}
protected UComponentBase(bool isPageComponent)
{
   if ( IsPageComponent = isPageComponent )
      CancelExclusiveOperationOnDispose = true;
}

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

protected override void OnParametersSet() =>
go_nt(()=>{
   _ShouldRender_ = true;
   base.OnParametersSet();
   if ( !WereParametersSet )
   {
      On_FirstParametersSet();
      WereParametersSet = true;
   }
});

protected virtual void On_FirstParametersSet()
{}

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

protected override void OnAfterRender(bool firstRender) =>
go_nt(()=>{
   if ( firstRender ) On_Load();
   base.OnAfterRender( firstRender );
   if ( firstRender )
   {
      WasLoaded = true;
      On_AfterLoad();
   }
   _ShouldRender_ = false;
});
protected virtual void On_Load()
{}
protected virtual void On_AfterLoad()
{}

protected override Task OnAfterRenderAsync(bool firstRender) =>
go_nt(async()=>{
   if ( firstRender ) await On_Load_A();
   await base.OnAfterRenderAsync( firstRender );
   if ( firstRender ) await On_AfterLoad_A();
});
protected virtual async Task On_Load_A() =>
   await Task.Delay(0);
protected virtual async Task On_AfterLoad_A() =>
   await Task.Delay(0);

protected void OnAfterRender_Original(bool firstRender) =>
   base.OnAfterRender( firstRender );
protected Task OnAfterRenderAsync_Original(bool firstRender) =>
   base.OnAfterRenderAsync( firstRender );

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

protected virtual bool GetIsCurrentPage() => false;

protected override bool ShouldRender() =>
   !AvoidThickRendering || _ShouldRender_;

protected virtual void UpdateForRendering()
{
   _ShouldRender_ = true;
   StateHasChanged();
}

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

protected virtual void OnDispose()
{
   UCircuit.ExclusiveBegin -= ExclusiveBegin_Handler;
   UCircuit.ExclusiveEnd -= ExclusiveEnd_Handler;
   if ( CancelExclusiveOperationOnDispose ) 
      UCircuit.CancelExclusiveOperation();
   _Instances_.Remove( UniqueId );
}

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

protected virtual void OnExclusiveBegin()
{}
protected virtual void OnExclusiveEnd()
{}

protected virtual Task OnExclusiveBegin_A() => Task.Delay(0);
protected virtual Task OnExclusiveEnd_A() => Task.Delay(0);

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

protected override void OnInitialized()
{
   if ( IsPageComponent )
      UInputBlocking.NeutralizeResetToInitial();
   base.OnInitialized();
   UCircuit.ExclusiveBegin += ExclusiveBegin_Handler;
   UCircuit.ExclusiveEnd += ExclusiveEnd_Handler;
}

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

void ExclusiveBegin_Handler(ComponentBase? component) =>
   OnExclusiveBegin();
void ExclusiveEnd_Handler(ComponentBase? component) =>
   OnExclusiveEnd();

Task ExclusiveBegin_Handler_A(ComponentBase? component) =>
   OnExclusiveBegin_A();
Task ExclusiveEnd_Handler_A(ComponentBase? component) =>
   OnExclusiveEnd_A();

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

bool _ShouldRender_ = true;

static readonly Dictionary<string,UComponentBase>
   _Instances_ = new Dictionary<string,UComponentBase>();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // UComponentBase
//**************************************************************************************************

} // AFXUI
