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

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Threading;
using System.Windows.Media;

using CommonHelpers;

namespace UserWin {

//**************************************************************************************************
partial class UXWindow {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[DefaultValue(_SynchronizeIsBusyWithApplicationDefault_)]
public static readonly DependencyProperty SynchronizeIsBusyWithApplicationProperty;
[DefaultValue(_CanCloseBusyDefault_)]
public static readonly DependencyProperty CanCloseBusyProperty;
[DefaultValue(_CanCloseDisabledDefault_)]
public static readonly DependencyProperty CanCloseDisabledProperty;
[DefaultValue(_BlockKeyboardInputWhenBusyDefault_)]
public static readonly DependencyProperty BlockKeyboardInputWhenBusyProperty;
[DefaultValue(_BlockMouseInputWhenBusyDefault_)]
public static readonly DependencyProperty BlockMouseInputWhenBusyProperty;
[DefaultValue(_FilterMainClientWMInputWhenBusyDefault_)]
public static readonly DependencyProperty FilterMainClientWMInputWhenBusyProperty;
[DefaultValue(_ApplyWaitCursorWhenBusyDefault_)]
public static readonly DependencyProperty ApplyWaitCursorWhenBusyProperty;
/*
[DefaultValue(_GlassEffectDefault_)]
public static readonly DependencyProperty GlassEffectProperty;
public static readonly DependencyProperty IsGlassEffectActiveProperty;
public static readonly DependencyProperty PreGlassEffectBackgroundProperty;
*/
[DefaultValue(_DragMoveBehaviourDefault_)]
public static readonly DependencyProperty DragMoveBehaviourProperty;
[DefaultValue(_SetsAnchorWindowOnActivationDefault_)]
public static readonly DependencyProperty SetsAnchorWindowOnActivationProperty;

public static readonly DependencyProperty IsBusyProperty;
public static readonly DependencyProperty CloseDemandProperty;

public static readonly RoutedEvent IsBusyChangedEvent;
//public static readonly RoutedEvent IsGlassEffectChangedEvent;

//==================================================================================================
/*
public static bool IsCompositionEnabled
{
   get
   {
      if ( !HOperatingSystem.IsWindowsVistaOrLater )
         return false;
      int enabled;
      if ( Win32.DwmIsCompositionEnabled( out enabled )<0 )
         throw HWin32Error.CreateOpFailedException( "DwmIsCompositionEnabled" );
      return enabled!=0;
   }
}
*/
//==================================================================================================

public static bool GetSynchronizeIsBusyWithApplication(Window window)
{
   return (bool)window.GetValue( SynchronizeIsBusyWithApplicationProperty );
}

public static void SetSynchronizeIsBusyWithApplication(Window window,bool value)
{
   window.SetValue( SynchronizeIsBusyWithApplicationProperty, value );
}

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

public static bool GetCanCloseBusy(Window window)
{
   return (bool)window.GetValue( CanCloseBusyProperty );
}

public static void SetCanCloseBusy(Window window,bool value)
{
   window.SetValue( CanCloseBusyProperty, value );
}

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

public static bool GetCanCloseDisabled(Window window)
{
   return (bool)window.GetValue( CanCloseDisabledProperty );
}

public static void SetCanCloseDisabled(Window window,bool value)
{
   window.SetValue( CanCloseDisabledProperty, value );
}

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

public static bool GetBlockKeyboardInputWhenBusy(Window window)
{
   return (bool)window.GetValue( BlockKeyboardInputWhenBusyProperty );
}

public static void SetBlockKeyboardInputWhenBusy(Window window,bool value)
{
   window.SetValue( BlockKeyboardInputWhenBusyProperty, value );
}

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

public static bool GetBlockMouseInputWhenBusy(Window window)
{
   return (bool)window.GetValue( BlockMouseInputWhenBusyProperty );
}

public static void SetBlockMouseInputWhenBusy(Window window,bool value)
{
   window.SetValue( BlockMouseInputWhenBusyProperty, value );
}

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

public static bool GetFilterMainClientWMInputWhenBusy(Window window)
{
   return (bool)window.GetValue( FilterMainClientWMInputWhenBusyProperty );
}

public static void SetFilterMainClientWMInputWhenBusy(Window window,bool value)
{
   window.SetValue( FilterMainClientWMInputWhenBusyProperty, value );
}

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

public static bool GetApplyWaitCursorWhenBusy(Window window)
{
   return (bool)window.GetValue( ApplyWaitCursorWhenBusyProperty );
}

public static void SetApplyWaitCursorWhenBusy(Window window,bool value)
{
   window.SetValue( ApplyWaitCursorWhenBusyProperty, value );
   if ( !value ) window.Cursor=null;
}

//--------------------------------------------------------------------------------------------------
/*
public static bool GetGlassEffect(Window window)
{
   return (bool)window.GetValue( GlassEffectProperty );
}

public static void SetGlassEffect(Window window,bool value)
{
   window.SetValue( GlassEffectProperty, value );
}
*/
//--------------------------------------------------------------------------------------------------
/*
public static bool GetIsGlassEffectActive(Window window)
{
   return (bool)window.GetValue( IsGlassEffectActiveProperty );
}

public static void SetIsGlassEffectActive(Window window,bool value)
{
   window.SetValue( IsGlassEffectActiveProperty, value );
}
*/
//--------------------------------------------------------------------------------------------------
/*
public static Brush GetPreGlassEffectBackground(Window window)
{
   return (Brush)window.GetValue( PreGlassEffectBackgroundProperty );
}

public static void SetPreGlassEffectBackground(Window window,Brush value)
{
   window.SetValue( PreGlassEffectBackgroundProperty, value );
}
*/
//--------------------------------------------------------------------------------------------------

public static bool GetDragMoveBehaviour(Window window)
{
   return (bool)window.GetValue( DragMoveBehaviourProperty );
}

public static void SetDragMoveBehaviour(Window window,bool value)
{
   window.SetValue( DragMoveBehaviourProperty, value );
}

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

public static bool GetSetsAnchorWindowOnActivation(Window window)
{
   return (bool)window.GetValue( SetsAnchorWindowOnActivationProperty );
}

public static void SetSetsAnchorWindowOnActivation(Window window,bool value)
{
   window.SetValue( SetsAnchorWindowOnActivationProperty, value );
}

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

public static bool GetIsBusy(Window window)
{
   return (bool)window.GetValue( IsBusyProperty );
}

public static void SetIsBusy(Window window,bool value)
{
   window.SetValue( IsBusyProperty, value );
}

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

public static bool GetCloseDemand(Window window)
{
   return (bool)window.GetValue( CloseDemandProperty );
}

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

public static bool DetectCanClose(Window window)
{
   return ( !GetIsBusy( window ) || GetCanCloseBusy( window ) ) &&
          ( window.IsEnabled || GetCanCloseDisabled( window ) );
}

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

public static IntPtr GetHandle(Window window)
{
   HArgChecking.VerifyNotNull( window );
   return new WindowInteropHelper( window ).Handle;
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="HOperationFailedException" />
public static Window FromHandle(IntPtr hWnd)
{
   if ( hWnd == IntPtr.Zero )
      throw new ArgumentNullException( "Window handle must not be null." );
   foreach (Window win in Application.Current.Windows)
      if ( GetHandle( win ) == hWnd )
         return win;
   throw new HOperationFailedException( "Can not find window by its handle." );
}

public static HwndSource GetHwndSource(Window window)
{
   HArgChecking.VerifyNotNull( window );
   return HwndSource.FromHwnd( GetHandle( window ) );
}

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

public static void Close(Window window)
{
   HProgram.VerifyInMainThread();
   window.SetValue( CloseDemandProperty, true );
   window.Close();
}

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

public static bool? GetDialogResult(Window window)
{
   HProgram.VerifyInMainThread();
   return window.DialogResult;
}

public static void SetDialogResult(Window window,bool? value)
{
   HProgram.VerifyInMainThread();
   if ( value.HasValue )
      window.SetValue( CloseDemandProperty, true );
   window.DialogResult = value;
}

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

public static bool? ShowDialog(Window window)
{
   return ShowDialog( window, false );
}
public static bool? ShowDialog(Window window,bool saveCursor)
{
   return ShowDialogWithOwner( window, UXApplication.AnchorWindow, saveCursor );
}

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

public static bool? ShowDialogWithOwner(Window window,[HOptional] Window owner)
{
   return ShowDialogWithOwner( window, owner, false );
}
public static bool? ShowDialogWithOwner(Window window,[HOptional] Window owner,bool saveCursor)
{
   Cursor savedCursor = null;
   if ( saveCursor )
      savedCursor = UXCursor.Current;
   window.Owner = owner;
   var result = window.ShowDialog();
   if ( saveCursor )
      UXCursor.Override( savedCursor );
   return result;
}

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

public bool? ShowDialogByMainForm(Window window)
{
   return ShowDialogByMainForm( window, false );
}
public bool? ShowDialogByMainForm(Window window,bool saveCursor)
{
   return ShowDialogWithOwner( UXApplication.MainWindow, saveCursor );
}

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

/// <exception cref="ArgumentNullException" />
public static bool EnableSystemMenuCloseItem(Window window)
{
   return EnableSystemMenuCloseItem( window, true );
}
/// <exception cref="ArgumentNullException" />
public static bool EnableSystemMenuCloseItem(Window window,bool enable)
{
   HArgChecking.VerifyNotNull( window );
   IntPtr hMenu = Win32.GetSystemMenu(
      GetHandle( window ), false );
   return
      hMenu != IntPtr.Zero &&
      Win32.EnableMenuItem( hMenu, Win32.SC_CLOSE,
         Win32.MF_BYCOMMAND | (enable ? Win32.MF_ENABLED : Win32.MF_GRAYED) )
      != unchecked((uint)-1);
}

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

/// <exception cref="ArgumentNullException" />
public static bool DisableSystemMenuCloseItem(Window window)
{
   return EnableSystemMenuCloseItem( window, false );
}

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

/// <exception cref="ArgumentNullException" />
public static void UpdateSystemMenuCloseItem(Window window)
{
   EnableSystemMenuCloseItem( window, DetectCanClose(window) );
}

/// <exception cref="ArgumentNullException" />
public static bool CancelClosing(Window window)
{
   return !DetectCanClose( window ) && !GetCloseDemand( window );
}

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

public static double GetRight(Window window)
{
   return window.Left+window.ActualWidth;
}

public static double GetBottom(Window window)
{
   return window.Top+window.ActualHeight;
}

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

public static Rect GetWindowRect(Window window)
{
   Win32_RECT rect;
   if ( !Win32.GetWindowRect( GetHandle( window ), out rect ) )
      throw HWin32Error.CreateExceptionFromLastError();
   return new Rect(
      UXVideo.PixelsToUnitsX( rect.left ),
      UXVideo.PixelsToUnitsY( rect.top  ),
      UXVideo.PixelsToUnitsX( rect.right-rect.left ),
      UXVideo.PixelsToUnitsY( rect.bottom-rect.top ));
}

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

public static void SetPosition(Window window,double left,double top)
{
   SetPositionAndSize( window, left, top, window.ActualWidth, window.ActualHeight );
}

public static void SetPositionAndSize( Window window,
                                       double left, double top,
                                       double width, double height )
{
   if ( !Win32.SetWindowPos( GetHandle(window), IntPtr.Zero,
         UXVideo.UnitsToPixelsX(left), UXVideo.UnitsToPixelsY(top),
         UXVideo.UnitsToPixelsX(width), UXVideo.UnitsToPixelsY(height),
         Win32.SWP_NOZORDER ) )
      throw new Win32Exception();
}

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

public static void Restore(Window window)
{
   Win32.ShowWindow( GetHandle( window ), Win32.SW_RESTORE );
}

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

public static bool TryFitIntoWorkArea(Window window)
{
   Rect workArea =
      SystemParameters.WorkArea;
   SetPosition
   (
      window,
      Math.Max( workArea.Left,
                Math.Min( window.Left,
                          workArea.Right - window.ActualWidth ) ),
      Math.Max( workArea.Top,
                Math.Min( window.Top,
                          workArea.Bottom - window.ActualHeight ) )
   );
   return
      window.Left >= workArea.Left && GetRight ( window ) <= workArea.Right &&
      window.Top  >= workArea.Top  && GetBottom( window ) <= workArea.Bottom;
}

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

public static bool BringToForeground(Window window)
{
   HOperationFailedException error;
   return BringToForeground( window, out error );
}

public static bool BringToForeground(Window window,out HOperationFailedException error)
{
   if ( !Win32.SetForegroundWindow( GetHandle( window ) ) )
   {
      error = HOpFailedExceptionCreator.Create( HWin32Error.CreateExceptionFromLastError() );
      return false;
   }
   error = null; return true;
}

//==================================================================================================
/*
public static void ExtendFrameIntoClientArea( Window window,
                                              double leftWidth, double topHeight,
                                              double rightWidth, double bottomHeight )
{
   ExtendFrameIntoClientArea
   (
      window,
      UXVideo.UnitsToPixelsX( leftWidth ),
      UXVideo.UnitsToPixelsY( topHeight ),
      UXVideo.UnitsToPixelsX( rightWidth ),
      UXVideo.UnitsToPixelsY( bottomHeight )
   );
}

/// <exception cref="PlatformNotSupportedException" />
/// <exception cref="HOperationFailedException" />
public static void ExtendFrameIntoClientArea( Window window,
                                              int leftWidth, int topHeight,
                                              int rightWidth, int bottomHeight )
{
   HOperatingSystem.VerifyWindowsVistaOrLater();

   var marInset = new Win32_MARGINS();

   marInset.cxLeftWidth    = leftWidth;
   marInset.cyTopHeight    = topHeight;
   marInset.cxRightWidth   = rightWidth;
   marInset.cyBottomHeight = bottomHeight;

   if ( Win32.DwmExtendFrameIntoClientArea( GetHandle(window), ref marInset ) < 0 )
      throw HWin32Error.CreateOpFailedException( "DwmExtendFrameIntoClientArea" );
}
*/
//--------------------------------------------------------------------------------------------------
/*
public static bool ApplyGlassEffect(Window window)
{
   if ( GetIsGlassEffectActive(window) )
      return true;
   if ( !IsCompositionEnabled )
      return false;

   SetPreGlassEffectBackground( window, window.Background );

   GetHwndSource( window ).CompositionTarget.BackgroundColor = Colors.Transparent;
   window.Background = Brushes.Transparent;

   ExtendFrameIntoClientArea(window,-1,-1,-1,-1);

   SetIsGlassEffectActive(window,true);

   return true;
}
*/
//==================================================================================================

static UXWindow()
{
   UserWin_Library.Access();

   SynchronizeIsBusyWithApplicationProperty = DependencyProperty.Register(
      "SynchronizeIsBusyWithApplication", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_SynchronizeIsBusyWithApplicationDefault_) );
   CanCloseBusyProperty = DependencyProperty.Register(
      "CanCloseBusy", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_CanCloseBusyDefault_) );
   CanCloseDisabledProperty = DependencyProperty.Register(
      "CanCloseDisabled", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_CanCloseDisabledDefault_) );
   BlockKeyboardInputWhenBusyProperty = DependencyProperty.Register(
      "BlockKeyboardInputWhenBusy", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_BlockKeyboardInputWhenBusyDefault_) );
   BlockMouseInputWhenBusyProperty = DependencyProperty.Register(
      "BlockMouseInputWhenBusy", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_BlockMouseInputWhenBusyDefault_) );
   FilterMainClientWMInputWhenBusyProperty = DependencyProperty.Register(
      "FilterMainClientWMInputWhenBusy", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_FilterMainClientWMInputWhenBusyDefault_) );
   ApplyWaitCursorWhenBusyProperty = DependencyProperty.Register(
      "ShowWaitCursorWhenBusy", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata( _ApplyWaitCursorWhenBusyDefault_,
         delegate (DependencyObject d,DependencyPropertyChangedEventArgs e)
         {
            ((Window)d).Cursor = (bool)e.NewValue && UXApplication.IsBusy ?
               Cursors.Wait : null;
         } ));
/*
   GlassEffectProperty = DependencyProperty.Register(
      "GlassEffect", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_GlassEffectDefault_) );
   IsGlassEffectActiveProperty = DependencyProperty.Register(
      "IsGlassEffectActive", typeof(bool), typeof(Window) );
   PreGlassEffectBackgroundProperty = DependencyProperty.Register(
      "PreGlassEffectBackground", typeof(Brush), typeof(Window) );
*/
   DragMoveBehaviourProperty = DependencyProperty.Register(
      "DragMoveBehaviour", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_DragMoveBehaviourDefault_) );
   SetsAnchorWindowOnActivationProperty = DependencyProperty.Register(
      "SetsAnchorWindowOnActivation", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata(_SetsAnchorWindowOnActivationDefault_) );

   IsBusyProperty = DependencyProperty.Register(
      "IsBusy", typeof(bool), typeof(Window),
      new FrameworkPropertyMetadata( false,
         FrameworkPropertyMetadataOptions.AffectsRender,
         Window_IsBusyChanged ) );
   CloseDemandProperty = DependencyProperty.Register(
      "CloseDemand", typeof(bool), typeof(Window) );

   IsBusyChangedEvent = EventManager.RegisterRoutedEvent(
      "IsBusyChanged", RoutingStrategy.Direct,
      typeof(RoutedPropertyChangedEventHandler<bool>),
      typeof(Window) );
/*
   IsGlassEffectChangedEvent = EventManager.RegisterRoutedEvent(
      "IsGlassEffectChanged", RoutingStrategy.Direct,
      typeof(RoutedPropertyChangedEventHandler<bool>),
      typeof(Window) );
*/
}

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

static void Window_IsBusyChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
   var window = (Window)d;
   var args = new RoutedPropertyChangedEventArgs<bool>(
      (bool)e.OldValue, (bool)e.NewValue, IsBusyChangedEvent );
   if ( window is UXWindow )
      ((UXWindow)window).OnIsBusyChanged( args );
   else
      window.RaiseEvent( args );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // UXWindow
//**************************************************************************************************

} // UserWin
