//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//
// WFX satellite library | Author: Sergei Y.Kitáev
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.CompilerServices;

using WF = System.Windows.Forms;

using CommonHelpers;

namespace WFX {

//**************************************************************************************************
/// <summary>
/// </summary>
public static partial class Dispatcher {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="ArgumentNullException" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Synchronize(Action operation)
{
   SynchronizeEx( null, operation, Assembly.GetCallingAssembly() );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Synchronize<T>(Func<T> operation)
{
   return SynchronizeEx( null, operation, Assembly.GetCallingAssembly() );
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="Exception" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Synchronize(bool interceptExceptions,Action operation)
{
   SynchronizeEx(
      interceptExceptions ? _TypeArrayWithMostBaseExceptionType_ : null,
      operation, Assembly.GetCallingAssembly() );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="Exception" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Synchronize<T>(bool interceptExceptions,Func<T> operation)
{
   return SynchronizeEx(
      interceptExceptions ? _TypeArrayWithMostBaseExceptionType_ : null,
      operation, Assembly.GetCallingAssembly() );
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="DispatcherException" />
/// <exception cref="Exception" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Synchronize( [HOptional] Type[] exceptionTypesForInterception,
                                [HOptional] Action operation )
{
   SynchronizeEx( exceptionTypesForInterception, operation, Assembly.GetCallingAssembly() );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="Exception" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Synchronize<T>( [HOptional] Type[] exceptionTypesForInterception,
                                Func<T> operation )
{
   return SynchronizeEx( exceptionTypesForInterception, operation, Assembly.GetCallingAssembly() );
}

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

/// <exception cref="ArgumentNullException" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static IAsyncResult BeginInvoke(Action operation)
{
   HArgChecking.VerifyNotNull( operation );
   return BeginInvokeEx( operation, Assembly.GetCallingAssembly() );
}

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

/// <exception cref="ArgumentNullException" />
[HOptionalResult][HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static IAsyncResult BeginOrInvoke(Action operation)
{
   HArgChecking.VerifyNotNull( operation );
   if ( HProgram.IsInMainThread )
      { operation(); return null; }
   return BeginInvokeEx( operation, Assembly.GetCallingAssembly() );
}

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

/// <exception cref="ArgumentNullException" />
[HMemoryBarrier]
public static object EndInvoke(IAsyncResult asyncResult)
{
   HArgChecking.VerifyNotNull( asyncResult );
   object rslt = Application.MainForm.EndInvoke( asyncResult );
   Thread.MemoryBarrier(); return rslt;
}

/// <exception cref="ArgumentNullException" />
[HMemoryBarrier]
public static object SetNullEndInvoke(ref IAsyncResult asyncResult)
{
   HArgChecking.VerifyNotNull( asyncResult );
   return EndInvoke( HOps.NullExchange( ref asyncResult ) );
}

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

[HMemoryBarrier]
public static object EndInvokeIfNotNull([HOptional] IAsyncResult asyncResult)
{
   return asyncResult != null ? EndInvoke( asyncResult ) : null;
}

[HMemoryBarrier]
public static object SetNullEndInvokeIfNotNull([HOptional] ref IAsyncResult asyncResult)
{
   return asyncResult != null ? SetNullEndInvoke( ref asyncResult ) : null;
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="DispatcherException" />
/// <exception cref="Exception" />
[MethodImpl(MethodImplOptions.NoInlining)]
static void SynchronizeEx( [HOptional] Type[] exceptionTypesForInterception,
                           [HOptional] Action operation,
                           Assembly callingAssembly,
                           int assertionSkipFrames = 0 )
{
   HOps.SubstituteIfNull(ref operation,HOps.Nop);
   if ( HProgram.IsInMainThread )
      { operation(); return; }
   if ( exceptionTypesForInterception != null )
   {
      HArgChecking.VerifyNotNulls( exceptionTypesForInterception );
      foreach (var type in exceptionTypesForInterception)
         if ( !HType.IsTypeOf<Exception>( type ) )
            throw new ArgumentException();
   }
   var callerStackTrace = HAssertion.PrepareStackTraceForDispatcherCall(
      callingAssembly, assertionSkipFrames + 2 );
   Exception exception = null;
   using (new HMemoryBarrier())
      Application.MainForm.Invoke(
         (Action)delegate {
            using (new HMemoryBarrier())
            using (new HAssertion(callerStackTrace))
               exception = HOps.Catch(delegate{ operation(); });
      });
   if ( exception == null )
      return;
   if ( exceptionTypesForInterception != null )
      foreach (Type type in exceptionTypesForInterception)
         if ( HType.IsTypeOf( exception.GetType(), type ) )
         {
            Exception newException;
            Type exceptionType = exception.GetType();
            try
            {
               newException = (Exception)HType.CreateInstance( exceptionType,
                  string.Format( RethrownExceptionMessageFmt, exceptionType ),
                  exception );
            }
            catch
            {
               throw exception;
            }
            throw newException;
         }
   throw new DispatcherException( UnexpectedExceptionAfterMarshallingErrorMessage, exception );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="Exception" />
[MethodImpl(MethodImplOptions.NoInlining)]
static T SynchronizeEx<T>( [HOptional] Type[] exceptionTypesForInterception,
                           Func<T> operation,
                           Assembly callingAssembly,
                           int assertionSkipFrames = 0 )
{
   HArgChecking.VerifyNotNulls( operation );
   if ( HProgram.IsInMainThread )
      return operation();
   var value = default(T);
   SynchronizeEx( exceptionTypesForInterception, delegate{ value = operation(); },
                  callingAssembly, assertionSkipFrames + 1 );
   return value;
}

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

/// <exception cref="ArgumentNullException" />
[MethodImpl(MethodImplOptions.NoInlining)]
static IAsyncResult BeginInvokeEx( Action operation,
                                   Assembly callingAssembly,
                                   int assertionSkipFrames = 0 )
{
   HArgChecking.VerifyNotNull( operation );
   var callerStackTrace = HAssertion.PrepareStackTraceForDispatcherCall(
      callingAssembly, assertionSkipFrames + 2 );
   Thread.MemoryBarrier();
   return Application.MainForm.BeginInvoke
   (
      (Action)delegate
      {
         using (new HMemoryBarrier())
         using (new HAssertion(callerStackTrace))
            operation();
      }
   );
}

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

static Dispatcher()
{
   WFX_Library.Access();
}

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

static readonly Type[]
   _TypeArrayWithMostBaseExceptionType_ = new Type[]{ typeof(Exception) };

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // Dispatcher
//**************************************************************************************************

} // WFX
