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

using WF = System.Windows.Forms;

using CommonHelpers;

namespace WFX {

//**************************************************************************************************
partial class Application {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="InvalidOperationException" />
[HNotNull]
public static WF.Form MainForm
{
   get
   {
      var mainForm = _MainForm_;
      if ( mainForm == null )
         throw new InvalidOperationException(
            "Application's main form was not specified." );
      return mainForm;
   }
}

public static bool IsMainFormSpecified
{
   get { return GetMainForm() != null; }
}

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

[HMainThreadOnly]
public static WF.Form[] OpenFormsArray
{
   get
   {
      var list = new List<WF.Form>();
      ProcessOpenForms(delegate (WF.Form form){ list.Add( form ); });
      return list.ToArray();
   }
}

[HMainThreadOnly]
public static bool IsBlockedForInput
{
   get
   {
      return IgnoreInput || IsBusy && IgnoreInputWhenBusy;
   }
}

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

public static WF.IWin32Window AnchorWindow
{
   get { return _AnchorWindow_; }
   set { _AnchorWindow_ = value; }
}

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

public static bool IgnoreInput
{
   get { return _IgnoreInput_; }
   set { _IgnoreInput_ = value; }
}

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

public static bool IgnoreInputWhenBusy
{
   get { return _IgnoreInputWhenBusy_; }
   set { _IgnoreInputWhenBusy_ = value; }
}

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

public static bool DisableAllFormsClosabilityWhenBusy
{
   get { return _DisableAllFormsClosabilityWhenBusy_; }
   set { _DisableAllFormsClosabilityWhenBusy_ = value; }
}

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

public static event Action<WF.Form>
   FormActivated;

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

public static WF.Form GetMainForm()
{
   return _MainForm_;
}

public static WF.Form SpecifyMainForm([HOptional] WF.Form value)
{
   return _MainForm_ = value;
}

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

[HMainThreadOnly]
public static void UpdateOpenForms()
{
   ProcessOpenForms(delegate (WF.Form form){ form.Update(); });
}

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

[HMainThreadOnly]
public static void RaiseFormActivated(WF.Form form)
{
   HArgChecking.VerifyNotNull( form );
   HProgram.VerifyInMainThread();
   var formActivated = FormActivated;
   if ( formActivated != null )
      formActivated( form );
}

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

public static void EnableInputAndClosability()
{
   EnableInputAndClosability( true );
}

public static void EnableInputAndClosability(bool enable)
{
   IgnoreInput = !enable;
   ProcessOpenForms(delegate (WF.Form form){
      if ( form is Form )
         ((Form)form).IsBlockedForClose = !enable;
   });
}

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

public static void DisableInputAndClosability()
{
   EnableInputAndClosability( false );
}

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

[HMainThreadOnly]
public static void ProcessOpenForms(Action<WF.Form> callback)
{
   HArgChecking.VerifyNotNull( callback );
   HProgram.VerifyInMainThread();
   foreach (WF.Form form in WF.Application.OpenForms)
      callback( form );
}

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

[HMainThreadOnly]
public static void Run(WF.ApplicationContext context)
{
   PrepareToRun(); WF.Application.Run( context );
}
[HMainThreadOnly]
public static void Run(WF.Form mainForm)
{
   PrepareToRun(); WF.Application.Run( mainForm );
}

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

[HMainThreadOnly]
public static void Exit()
{
   IsClosing = true; WF.Application.Exit();
}
[HMainThreadOnly]
public static void Exit(CancelEventArgs e)
{
   IsClosing = true; WF.Application.Exit( e );
}

[HMainThreadOnly]
public static void ExitThread()
{
   IsClosing = true; WF.Application.ExitThread();
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
[HMainThreadOnly]
public static void ProcessMessageQueueWithoutUIAccessAndCloseWhile(HWhileCallback whileCallback)
{
   ProcessMessageQueueWithoutUIAccessAndCloseWhile( 100, whileCallback );
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
[HMainThreadOnly]
public static void ProcessMessageQueueWithoutUIAccessAndCloseWhile( int pollingInterval,
                                                                    HWhileCallback whileCallback )
{
   if ( pollingInterval < 0 )
      throw new ArgumentOutOfRangeException();
   HArgChecking.VerifyNotNull( whileCallback );
   while ( whileCallback() )
   {
      MessageHelpers.ProcessMessageQueueWithoutUIAccessAndClose();
      Thread.Sleep( pollingInterval );
   }
   MessageHelpers.ProcessMessageQueueWithoutUIAccessAndClose();
}

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

[HMainThreadOnly]
static void PrepareToRun()
{
   HProgram.VerifyInMainThread();
   WF.Application.RemoveMessageFilter( _AppMessageFilter_ );
   WF.Application.AddMessageFilter( _AppMessageFilter_ );
   IsClosing = false;
}

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

static readonly AppMessageFilter
   _AppMessageFilter_ = new AppMessageFilter();

static volatile WF.Form
   _MainForm_;
static volatile WF.IWin32Window
   _AnchorWindow_;
static volatile bool
   _IgnoreInput_,
   _IgnoreInputWhenBusy_ = true,
   _DisableAllFormsClosabilityWhenBusy_ = true;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // Application
//**************************************************************************************************

} // WFX
