using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows;
using System.Windows.Interop;

namespace Foo.GUILib
{
    /// <summary>
    /// Class used to keep WPF Windows Hidden permanently.
    /// Should be called by Performance WPF Window 'Hidden' loads 
    /// when constructed, and when loaded.
    /// 
    /// Note: All WPFWindow's that use this class don't set certain properties on the
    /// window until they are loaded and it's NOT a performance Cache window.
    /// </summary>
    public static class WPFHiddenWindow
    {
        private static Dictionary<IntPtr, Window> s_hWndToWindowMap = new Dictionary<IntPtr, Window>();

        /// <summary>
        /// * Core * 'Hiding' Function. Makes sure that the Window is and
        /// stays hidden.
        /// </summary>
        /// <param name="wpfWindow">a WPF window object</param>
        private static void HideWPFWindow(Window wpfWindow)
        {
            // Force Opacity
            wpfWindow.WindowStyle = WindowStyle.None;
            wpfWindow.Opacity = 0.0;                        
            wpfWindow.ShowInTaskbar = false;
            wpfWindow.ShowActivated = false;

            // Force a tiny Window in top-left corner
            wpfWindow.Width = 5;
            wpfWindow.Height = 5;
            wpfWindow.Top = 0;
            wpfWindow.Left = 0;
            wpfWindow.WindowStartupLocation = WindowStartupLocation.Manual;           
        }

        /// <summary>
        /// Call this to execute a Show() call on a wpfWindow Performance cache window. Returns true
        /// if bPerformanceCache is true. This means that this Function did something to the window.
        /// </summary>
        /// <param name="wpfWindow">a WPF window object</param>
        /// <param name="bPerformanceCache">Pass in true, if this is a performance Cache window</param>
        /// <returns>true if bPerformanceCache is true, false otherwise</returns>
        public static bool ExecuteShowOnAHiddenWindow(Window wpfWindow, bool bPerformanceCache)
        {
            if (bPerformanceCache)
            {
                HideWPFWindow(wpfWindow);
                wpfWindow.Show();
                wpfWindow.Hide();
                return true;
            }
            return false;
        }

        /// <summary>
        /// Call this in your WPFWindow's constructor. Returns true if bPerformanceCache is true.
        /// This means that this Function did something to the window.
        /// </summary>
        /// <param name="wpfWindow">a WPF window object</param>
        /// <param name="bPerformanceCache">Pass in true, if this is a performance Cache window</param>
        /// <returns>true if bPerformanceCache is true, false otherwise</returns>
        public static bool HiddenWindowConstructor(Window wpfWindow, bool bPerformanceCache)
        {
            if (bPerformanceCache)
            {
                HideWPFWindow(wpfWindow);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Call this from your WPFWindow's OnLoad Event. Returns true if bPerformanceCache is true.
        /// This means that this Function did something to the window.
        /// </summary>
        /// <param name="wpfWindow">a WPF window object</param>
        /// <param name="bPerformanceCache">Pass in true, if this is a performance Cache window</param>
        /// <returns>true if bPerformanceCache is true, false otherwise</returns>
        public static bool HiddenWindowOnLoad(Window wpfWindow, bool bPerformanceCache)
        {
            if (bPerformanceCache)
            {
                HideWPFWindow(wpfWindow);

                // Add Message Hook for performanceCache WPForm * To make sure it is always invisible *
                WindowInteropHelper interop = new WindowInteropHelper(wpfWindow);
                HwndSource source = HwndSource.FromHwnd(interop.Handle);
                source.AddHook(new HwndSourceHook(MessageHook));
                s_hWndToWindowMap[interop.Handle] = wpfWindow;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Call this from your WPFWindow's Closing Event. Returns true if bPerformanceCache is true.
        /// This means that this Function did something to the window.
        /// </summary>
        /// <param name="wpfWindow">a WPF window object</param>
        /// <param name="bPerformanceCache">Pass in true, if this is a performance Cache window</param>
        /// <returns>true if bPerformanceCache is true, false otherwise</returns>
        public static bool HiddenWindowClosing(Window wpfWindow, bool bPerformanceCache)
        {
            if (s_hWndToWindowMap.ContainsValue(wpfWindow))
            {
                foreach (IntPtr hWnd in s_hWndToWindowMap.Keys)
                {
                    if (s_hWndToWindowMap[hWnd] == wpfWindow)
                    {                        
                        s_hWndToWindowMap.Remove(hWnd);
                        break;
                    }                    
                }
                return true;
            }            
            return false;
        }

        /// <summary>
        /// This Message Hook is for the Hidden wpfForm that we use for better performance,
        /// We want to make sure that it is never displayed *It can occur that explorer.exe can show it, without this hook*
        /// ~i.e. when explorer.exe crashes, and get's restarted
        /// </summary>       
        private static IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Get the Proper WPF Object and make sure it is Hidden
            if (s_hWndToWindowMap.ContainsKey(hwnd))
            {
                Window wpfWindow = s_hWndToWindowMap[hwnd];
                if (wpfWindow != null)
                    HideWPFWindow(wpfWindow);
            }
            
            handled = false;
            return System.IntPtr.Zero;
        }
    }
}
