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

using System.Windows;
using System.Windows.Threading;

using CommonHelpers;

namespace UserWin {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// <exception cref="ArgumentNullException" />
[HMemoryBarrier]
public static void BeginOrInvoke(Action operation)
{
   BeginOrInvokeEx( null, operation, Assembly.GetCallingAssembly() );
}

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

/// <exception cref="ArgumentNullException" />
[HMemoryBarrier]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void BeginOrInvoke(DispatcherPriority? priority,Action operation)
{
   BeginOrInvokeEx( priority, operation, Assembly.GetCallingAssembly() );
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="Exception" />
[MethodImpl(MethodImplOptions.NoInlining)]
static void SynchronizeEx( DispatcherPriority? priority,
                           [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())
      UXApplication.Dispatcher.Invoke
      (
         (Action)delegate {
            using (new HMemoryBarrier())
            using (new HAssertion(callerStackTrace))
               try
               {
                  operation();
               }
               catch (Exception e)
               {
                  if ( exceptionTypesForInterception != null )
                     foreach (var type in exceptionTypesForInterception)
                        if ( HType.IsTypeOf( e.GetType(), type ) )
                           { exception = e; return; }
                  throw;
               }
         },
         priority ?? DispatcherPriority.Normal
      );
   if ( exception == null )
      return;
   Exception newException;
   Type exceptionType = exception.GetType();
   try
   {
      newException = (Exception)HType.CreateInstance( exceptionType,
         string.Format( RethrownExceptionMessageFmt, exceptionType ),
         exception );
   }
   catch
   {
      throw exception;
   }
   throw newException;
}

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

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

/// <exception cref="ArgumentNullException" />
[MethodImpl(MethodImplOptions.NoInlining)]
static void BeginOrInvokeEx( DispatcherPriority? priority, Action operation,
                             Assembly callingAssembly, int assertionSkipFrames = 0 )
{
   HArgChecking.VerifyNotNull( operation );
   if ( HProgram.IsInMainThread )
      { operation(); return; }
   var callerStackTrace = HAssertion.PrepareStackTraceForDispatcherCall(
      callingAssembly, assertionSkipFrames + 2 );
   Thread.MemoryBarrier();
   UXApplication.Dispatcher.BeginInvoke
   (
      (Action)delegate
      {
         using (new HMemoryBarrier())
         using (new HAssertion(callerStackTrace))
            operation();
      },
      priority ?? DispatcherPriority.Normal
   );
}

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

static UXDispatcher()
{
   UserWin_Library.Access();
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // UXDispatcher
//**************************************************************************************************

} // UserWin
