//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//
// UserWin satellite library | Author: Sergei Y.Kitáev
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.ComponentModel;
using System.Threading.Tasks;

using System.Windows;
using System.Windows.Input;

using CommonHelpers;

namespace UserWin {

//**************************************************************************************************
/// <summary>
/// </summary>
[HSyncOnlyDisposable]
public partial class UXAppWaiting: HDisposable {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static event UXDisplayAppWaitingStatusCallback
   DisplayAppWaitingStatusHandler;

public static event Action
   ReportErrorPosteriorTrigger;

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

[DefaultValue(true)]
public static bool KeepApplicationBusyWhenActive
{
   get { return _KeepApplicationBusyWhenActive_; }
   set { _KeepApplicationBusyWhenActive_ = value; }
}

public static UXReportAppWaitingErrorCallback ReportErrorCallback
{
   get { return _ReportErrorCallback_; }
   set { _ReportErrorCallback_ = value; }
}

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

[DefaultValue(true)]
public static bool ReleaseMouseCaptureOnActivation
{
   get { return _ReleaseMouseCaptureOnActivation_; }
   set { _ReleaseMouseCaptureOnActivation_ = value; }
}

[DefaultValue(false)]
public static bool ProcessMessageQueueWithoutInputOnDeactivation
{
   get { return _ProcessMessageQueueWithoutInputOnDeactivation_; }
   set { _ProcessMessageQueueWithoutInputOnDeactivation_ = value; }
}

[DefaultValue(false)]
public static bool AffectsCursor
{
   get { return _AffectsCursor_; }
   set { _AffectsCursor_ = value; }
}

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

public static bool IsActive
{
   get
   {
      HProgram.VerifyInMainThread();
      return _CurrentStatus_ != null;
   }
}

public static bool IsSuspended
{
   get
   {
      HProgram.VerifyInMainThread();
      return IsActive && _CurrentStatus_.IsSuspended;
   }
}

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

public static string Title
{
   get { VerifyActive(); return _Title_; }
}

public static string Caption
{
   get { VerifyActive(); return _CurrentStatus_.Caption; }
}

public static object Data
{
   get { VerifyActive(); return _CurrentStatus_.Data; }
}

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

public static UXAppWaitingStatus CurrentStatus
{
   get { VerifyActive(); return _CurrentStatus_; }
}

public static UXAppWaitingStatus RootStatus
{
   get { VerifyActive(); return _RootStatus_; }
}

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

public static UXAppWaiting CurrentInstance
{
   get
   {
      VerifyActive();
      if (_CurrentInstance_==null)
         throw new InvalidOperationException(
            "No instance of application waiting was created." );
      if (_IsInDispose_)
         throw new InvalidOperationException(
            "Current instance of application waiting can not be accessed during disposing." );
      return _CurrentInstance_;
   }
}

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

public static UXAppWaiting CreateInstance([HOptional] string caption,[HOptional] object data)
{
   return new UXAppWaiting( new UXAppWaitingStatus( caption, data ) );
}

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

public static void VerifyActive()
{
   HProgram.VerifyInMainThread();
   if ( !IsActive )
      throw new InvalidOperationException(
         "Application waiting is not active." );
}

public static void VerifyNotActive()
{
   HProgram.VerifyInMainThread();
   if ( IsActive )
      throw new InvalidOperationException(
         "Application waiting must not be active." );
}

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

public static void VerifySuspended()
{
   HProgram.VerifyInMainThread();
   if ( !IsSuspended )
      throw new InvalidOperationException(
         "Application waiting is not suspended." );
}

public static void VerifyNotSuspended()
{
   HProgram.VerifyInMainThread();
   if ( IsSuspended )
      throw new InvalidOperationException(
         "Application waiting must not be suspended." );
}

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

[HReturnsPrevious]
public static UXAppWaitingStatus Activate([HOptional] string title)
{
   return Activate( title, title );
}

[HReturnsPrevious]
public static UXAppWaitingStatus Activate([HOptional] string title,[HOptional] string caption)
{
   return ActivateWithData( title, caption, null );
}

[HReturnsPrevious]
public static UXAppWaitingStatus Activate( [HOptional] string title,
                                           string captionFmt,
                                           params object[] parameters )
{
   return Activate( title, string.Format( captionFmt, parameters ) );
}

[HReturnsPrevious]
public static UXAppWaitingStatus Activate([HOptional] string title,UXAppWaitingStatus status)
{
   if ( IsActive )
      SuppressBackOff();
   _Title_ = title;
   var rslt = SetStatus( status );
   _RootStatus_ = _CurrentStatus_;
   SwitchToActiveAppearance();
   return rslt;
}

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

[HReturnsPrevious]
public static UXAppWaitingStatus ActivateWithData( [HOptional] string title,
                                                   [HOptional] object data )
{
   return ActivateWithData( title, title, data );
}

[HReturnsPrevious]
public static UXAppWaitingStatus ActivateWithData( [HOptional] string title,
                                                   [HOptional] string caption,
                                                   [HOptional] object data )
{
   return Activate( title, new UXAppWaitingStatus( caption, data ) );
}

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

[HReturnsPrevious][HOptionalResult]
public static UXAppWaitingStatus Deactivate()
{
   if ( IsActive )
   {
      SuppressBackOff();
      var rslt = SetStatus( null );
      SwitchToInactiveAppearance();
      return rslt;
   }
   else
      return null;
}

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

[HReturnsPrevious]
public static UXAppWaitingStatus DisplayStatus()
{
   return DisplayStatus( (string)null );
}

[HReturnsPrevious]
public static UXAppWaitingStatus DisplayStatus([HOptional] string caption)
{
   return DisplayStatusWithData( caption, null );
}

[HReturnsPrevious]
public static UXAppWaitingStatus DisplayStatus(string captionFmt,params object[] parameters)
{
   return DisplayStatus( string.Format( captionFmt, parameters ) );
}

[HReturnsPrevious]
public static UXAppWaitingStatus DisplayStatus(UXAppWaitingStatus status)
{
   VerifyActive();
   var rslt = SetStatus( status );
   ShowStatus();
   return rslt;
}

[HReturnsPrevious]
public static UXAppWaitingStatus DisplayStatusWithData( [HOptional] string caption,
                                                        [HOptional] object data )
{
   return DisplayStatus( new UXAppWaitingStatus( caption, data ) );
}

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

public static void RefreshStatus()
{
   ShowStatus();
}

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

public static void RevertStatus()
{
   RevertStatus( false );
}

public static void RevertStatus(bool revertToRoot)
{
   VerifyActive();
   if ( revertToRoot || _CurrentInstance_ == null )
      DisplayStatus( _RootStatus_ );
   else
      DisplayStatus( _CurrentInstance_._PreviousStatus_ );
}

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

public static void SuppressBackOff()
{
   if ( IsActive )
   {
      for ( var instance = _CurrentInstance_;
            instance != null;
            instance = instance._PreviousInstance_ )
         instance._SuppressBackOff_ = true;
      _CurrentInstance_ = null;
   }
}

public static void SuppressCurrentForBackOff()
{
   HProgram.VerifyInMainThread();
   CurrentInstance._SuppressBackOff_ = true;
}

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

public static async Task<MessageBoxResult> ReportErrorEx(
   [HOptional] UXShowMessageExtraInfo extraInfo,
   [HOptional] Window owner,
   string errorMessage,
   MessageBoxButton button,
   MessageBoxResult defaultResult,
   bool skipSuppressBackOff )
{
   HArgChecking.VerifyNotNull( errorMessage );
   VerifyActive();

   var sound = HSounds.Error;
   object data = null;
   var options = MessageBoxOptions.None;
   Exception e = null;
   if ( extraInfo != null )
   {
      sound = extraInfo.Sound;
      data = extraInfo.Data;
      e = extraInfo.Exception;
   }
   var reportErrorCallback = ReportErrorCallback;
   var reportErrorTask = reportErrorCallback != null ?
      new Task<MessageBoxResult>(delegate{
         return reportErrorCallback(
            data, sound, options, owner, errorMessage, e, Title,
            button, defaultResult ); }) :
      UXMessageBox.ShowError( extraInfo, owner, errorMessage, Title, button, defaultResult );

   MessageBoxResult rslt;
   using (new UXAppWaiting( true ))
   {
      rslt = await reportErrorTask;
      if ( !skipSuppressBackOff )
         SuppressBackOff();
   }
   var posteriorTrigger = ReportErrorPosteriorTrigger;
   if ( posteriorTrigger != null )
      posteriorTrigger();
   return rslt;
}

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

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

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

static void SwitchToActiveAppearance()
{
   HProgram.VerifyInMainThread();
   if ( ReleaseMouseCaptureOnActivation )
      UXMouse.ReleaseCapture();
   if ( AffectsCursor )
      UXCursor.SetWait();
   if ( KeepApplicationBusyWhenActive )
      UXApplication.IsBusy = true;
   ShowStatus();
}

static void SwitchToInactiveAppearance()
{
   HProgram.VerifyInMainThread();
   if ( ProcessMessageQueueWithoutInputOnDeactivation )
      UXWMInputHelpers.ProcessMessageQueueWithoutInput();
   if ( KeepApplicationBusyWhenActive )
      UXApplication.IsBusy = false;
   ShowStatus();
   if ( AffectsCursor )
      UXCursor.Reset();
}

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

[HReturnsPrevious]
static UXAppWaitingStatus SetStatus([HOptional] UXAppWaitingStatus status)
{
   HProgram.VerifyInMainThread();
   return HOps.Exchange( ref _CurrentStatus_,
      status!=null ? status.Duplicate() : null );
}

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

static void ShowStatus()
{
   HProgram.VerifyInMainThread();
   var displayStatusHandler =
      DisplayAppWaitingStatusHandler;
   bool
      isActive = IsActive,
      isSuspended = IsSuspended;
   if ( KeepApplicationBusyWhenActive &&
        isActive && _PreviouslyShownActivityValue_ &&
        isSuspended!=_PreviouslyShownSuspensionValue_ )
      UXApplication.BusyStateChanged_Handler( true );
   if ( displayStatusHandler != null )
   {
      string
         caption = isActive ? Caption : null;
      object
         data = isActive ? Data : null;
      displayStatusHandler( isActive, isSuspended, caption, data );
   }
   _PreviouslyShownActivityValue_ = isActive;
   _PreviouslyShownSuspensionValue_ = isSuspended;
}

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

static volatile bool
   _KeepApplicationBusyWhenActive_ = true,
   _ReleaseMouseCaptureOnActivation_ = true,
   _ProcessMessageQueueWithoutInputOnDeactivation_,
   _AffectsCursor_;
static volatile UXReportAppWaitingErrorCallback
   _ReportErrorCallback_;

static UXAppWaitingStatus
   _RootStatus_,
   _CurrentStatus_;
static UXAppWaiting
   _CurrentInstance_;
static string
   _Title_;
static bool
   _IsInDispose_;
static volatile bool
   _PreviouslyShownActivityValue_,
   _PreviouslyShownSuspensionValue_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // UXAppWaiting
//**************************************************************************************************

} // UserWin
