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

using WF = System.Windows.Forms;

using CommonHelpers;

namespace WFX {

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

public static event DisplayAppWaitingStatusCallback
   DisplayAppWaitingStatusHandler;

public static event Action
   ReportErrorPosteriorTrigger;

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

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

public static ReportAppWaitingErrorCallback 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
   {
      return Dispatcher.Synchronize(delegate{
         return _CurrentStatus_ != null;
      });
   }
}

public static bool IsSuspended
{
   get
   {
      return Dispatcher.Synchronize(delegate{
         return IsActive && _CurrentStatus_.IsSuspended;
      });
   }
}

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

public static string Title
{
   get
   {
      return Dispatcher.Synchronize(delegate{
         VerifyActive();
         return _Title_;
      });
   }
}

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

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

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

public static AppWaitingStatus CurrentStatus
{
   get
   {
      return Dispatcher.Synchronize(delegate{
         VerifyActive();
         return _CurrentStatus_;
      });
   }
}

public static AppWaitingStatus RootStatus
{
   get
   {
      return Dispatcher.Synchronize(delegate{
         VerifyActive();
         return _RootStatus_;
      });
   }
}

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

public static AppWaiting CurrentInstance
{
   get
   {
      return Dispatcher.Synchronize(delegate{
         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 AppWaiting CreateInstance([HOptional] string caption,[HOptional] object data)
{
   return new AppWaiting( new AppWaitingStatus(caption,data) );
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[HReturnsPrevious][HOptionalResult]
public static AppWaitingStatus Deactivate()
{
   return Dispatcher.Synchronize(delegate{
      if (IsActive)
      {
         SuppressBackOff();
         var rslt = SetStatus( null );
         SwitchToInactiveAppearance();
         return rslt;
      }
      else
         return null;
   });
}

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

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

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

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

[HReturnsPrevious]
public static AppWaitingStatus DisplayStatus(AppWaitingStatus status)
{
   HArgChecking.VerifyNotNull(status);
   return Dispatcher.Synchronize(delegate{
      VerifyActive();
      var rslt = SetStatus(status);
      ShowStatus();
      return rslt;
   });
}

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

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

public static void RefreshStatus()
{
   Dispatcher.Synchronize(delegate{ ShowStatus(); });
}

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

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

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

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

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

public static void SuppressCurrentForBackOff()
{
   Dispatcher.Synchronize(delegate{
      CurrentInstance._SuppressBackOff_ = true;
   });
}

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

public static WF.DialogResult ReportErrorEx( [HOptional] ShowMessageExtraInfo extraInfo,
                                             [HOptional] WF.IWin32Window owner,
                                             string errorMessage,
                                             WF.MessageBoxButtons buttons,
                                             WF.DialogResult defaultResult,
                                             bool skipSuppressBackOff )
{
   HArgChecking.VerifyNotNull( errorMessage );
   return Dispatcher.Synchronize(delegate{
      return DoReportErrorEx( extraInfo,
         owner, errorMessage, buttons, defaultResult, skipSuppressBackOff ); });
}

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

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

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

static WF.DialogResult DoReportErrorEx( [HOptional] ShowMessageExtraInfo extraInfo,
                                        [HOptional] WF.IWin32Window owner,
                                        string errorMessage,
                                        WF.MessageBoxButtons buttons,
                                        WF.DialogResult defaultResult,
                                        bool skipSuppressBackOff )
{
   HProgram.VerifyInMainThread();
   VerifyActive();
   var reportErrorCallback = ReportErrorCallback;
   if ( reportErrorCallback == null )
      reportErrorCallback =
         delegate ( object d, HSound snd, WF.MessageBoxOptions o,
                    WF.IWin32Window own, string msg, Exception x, string tit,
                    WF.MessageBoxButtons btn, WF.DialogResult dr )
            { return MessageBox.ShowError( extraInfo, own, msg, tit, btn, dr ); };
   var sound = HSounds.Error;
   object data = null;
   WF.MessageBoxOptions options = 0;
   Exception e = null;
   if ( extraInfo != null )
   {
      sound = extraInfo.Sound;
      data = extraInfo.Data;
      e = extraInfo.Exception;
   }
   WF.DialogResult rslt;
   using (new AppWaiting( true ))
   {
      rslt = reportErrorCallback(
         data, sound, options, owner, errorMessage, e, Title,
         buttons, defaultResult );
      if ( !skipSuppressBackOff )
         SuppressBackOff();
   }
   var posteriorTrigger = ReportErrorPosteriorTrigger;
   if ( posteriorTrigger != null )
      posteriorTrigger();
   return rslt;
}

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

static void SwitchToActiveAppearance()
{
   Dispatcher.Synchronize
   (
      delegate
      {
         if ( ReleaseMouseCaptureOnActivation )
            Mouse.ReleaseCapture();
         if ( AffectsCursor )
            Cursor.SetWait();
         if ( KeepApplicationBusyWhenActive )
            Application.IsBusy = true;
      }
   );
   ShowStatus();
}

static void SwitchToInactiveAppearance()
{
   if ( ProcessMessageQueueWithoutInputOnDeactivation )
      Input.ProcessMessageQueueWithoutInput();
   if ( KeepApplicationBusyWhenActive )
      Dispatcher.Synchronize(delegate{ Application.IsBusy = false; });
   ShowStatus();
   if ( AffectsCursor )
      Dispatcher.Synchronize(delegate{ Cursor.Reset(); });
}

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

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

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

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

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

static volatile bool
   _KeepApplicationBusyWhenActive_ = true;
static volatile ReportAppWaitingErrorCallback
   _ReportErrorCallback_;

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

static volatile bool
   _ReleaseMouseCaptureOnActivation_ = true,
   _ProcessMessageQueueWithoutInputOnDeactivation_ = true,
   _AffectsCursor_ = true;

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

static AppWaitingStatus
   _RootStatus_,
   _CurrentStatus_;
static AppWaiting
   _CurrentInstance_;
static string
   _Title_;
static bool
   _IsInDispose_;
static volatile bool
   _PreviouslyShownActivityValue_,
   _PreviouslyShownSuspensionValue_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // AppWaiting
//**************************************************************************************************

} // WFX
