using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;

// Ooganizer Namespaces
using Foo.Platform;
using Foo.Platform.Interacters;
using Foo.Platform.Win32;

namespace Foo.GUILib
{
    /// <summary>
    /// This is the generic GUICommon Class, that serves us a generic implementation to
    /// everyone as to isolate ourselves from OS Changes
    /// </summary>
    public class GUICommon
    {
        // Keep track of all the origianl window positions that go
        // thru here, ~we delete them when position back to org position
        // get's called.
        private static Dictionary<IntPtr,RECT> s_orgWindowPosMap;
        private static bool s_bOutOfProc_UseBH = false;

        // public properties
        public static bool OutOfProc { get { return s_bOutOfProc_UseBH; } }

        // keep track of Window Positions
        static GUICommon()
        {
            s_orgWindowPosMap = new Dictionary<IntPtr, RECT>();
        }

        /// <summary>
        /// Performs a screen snapshot of the passed in window handle.
        /// </summary>
        /// <param name="hWnd">Window to Snap</param>
        /// <returns>a window image</returns>
        public static Image PerformSnapshot(IntPtr hWnd)
        {        
            if (hWnd != IntPtr.Zero)
            {
                ScreenCapture screencap = new ScreenCapture();

                if (Env.Theme == Env.Themes.AeroTheme)
                    return screencap.SaveWindowUsingFullScreenShotCrop(hWnd);
                else
                    return screencap.SaveWindowUsingDCNBitBlt(hWnd, false);
            }
            return null;
        }

        /// <summary>
        /// Positions a window in the center at Golden Ratio and takes a snapshot.
        /// ~corner stone of our add workspace strategy
        /// </summary>
        /// <param name="hWnd">handle to the window to manipulate</param>
        /// <param name="bOutOfProc">Set to true to use ButtonHook to Perform the action</param>
        public static Image PositionWindowToGoldenPosAndTakeSnapshot(IntPtr hWnd, bool bOutOfProc)
        {
            // use ButtonHook, Yes / No?
            s_bOutOfProc_UseBH = bOutOfProc;

            // First thing to do is to save the org position
            RECT rect;
            Win32Functions.GetWindowRect(hWnd, out rect);
            s_orgWindowPosMap[hWnd] = rect;

            // First we fade the window totally away
            FadeWindow(hWnd, true);

            // now we want to fade it out of the monitor center
            // However using the golden ratio as the size
            FadeWindowOutOfMonitorCenterInGoldenRatio(hWnd, rect);

            // Make sure we are in position and drawing at this point,
            // perform the snapshot and return
            System.Threading.Thread.Sleep(10);

            //PositionWindowBackToWhereItWas(hWnd);

            Image image = PerformSnapshot(hWnd);
            //image.Save(@"D:\Users\HPInvent\Desktop\Snapshot1.png", System.Drawing.Imaging.ImageFormat.Png);
            //return null;

            return image;
        }

        /// <summary>
        /// Positions a window back to where it was prior to calling PositionWindowToGoldenPosAndTakeSnapshot().
        /// ~Hence, you should call this after calling that function
        /// </summary>
        /// <param name="hWnd">handle to the window to manipulate</param>
        /// <param name="bOutOfProc">Set to true to use ButtonHook to Perform the action</param>
        public static void PositionWindowBackToWhereItWas(IntPtr hWnd, bool bOutOfProc)
        {
            // use ButtonHook, Yes / No?
            s_bOutOfProc_UseBH = bOutOfProc;

            // try to retrieve the org value from the map
            if (!s_orgWindowPosMap.ContainsKey(hWnd))
                return;

            RECT orgRect = s_orgWindowPosMap[hWnd];

            // Set the Window totally invisible
            SetAlphaTransparency(hWnd, 0);

            // As part of the effect, wait a little
            System.Threading.Thread.Sleep(60);

            // Now just flat out position it back to where it was
            Win32Functions.SetWindowPos(hWnd, IntPtr.Zero, orgRect.left, orgRect.top, orgRect.AsRectangle.Width, orgRect.AsRectangle.Height, (uint)SetWindowPosFlags.SWP_SHOWWINDOW);

            // Now Fade the Window out
            FadeWindow(hWnd, false);
            
            //SetAlphaTransparency(hWnd, 0); // invisible            
            //SetAlphaTransparency(hWnd, 255); // visible

            // since we positioned the window back to where it was,
            // we no longer need to keep track of it's org position
            s_orgWindowPosMap.Remove(hWnd);
        }

        /// <summary>
        /// Use this generic function to set the transparency
        /// </summary>        
        private static void SetAlphaTransparency(IntPtr hWnd, byte alpha)
        {
            if (s_bOutOfProc_UseBH)
                BHInteracter.SetWindowTransparency(hWnd, alpha);
            else
                Win32_WrapperFunc.SetAlphaChannel(hWnd, alpha);
        }

        /// <summary>
        /// The Golden Ratio (~1.618) is used for the Snapshot to give it the best look
        /// </summary>
        /// <param name="screen">pass in th screen for which to calculate the ratio for</param>
        /// <param name="rect">the width and height to achieve the best golden ratio for that screen</param>
        private static void CalculateGoldenRatio(Screen screen, out RECT rect)
        {
            const int _MAXHEIGHT = 1100;      // max pleasing height
            const double _fGoldenRatio = 1.618;
            const double _fsafetybuffer = 10; // 10% just in case 
                                              // safety buffer for height/width

            int maxHeight = screen.WorkingArea.Height;
            int maxWidth = screen.WorkingArea.Width;

            // is this a potrait screen?
            bool bIsPortrait = maxHeight > maxWidth;

            // calculated height and width
            int height = 0;
            int width = 0;

            // Normal Screen
            if (!bIsPortrait)
            {
                // calculated height and width;
                height = maxHeight - (int)(((double)maxHeight / 100) * _fsafetybuffer);
                width = (int)((double)height / _fGoldenRatio);
            }
            else // Portrait
            {
                width = maxWidth - (int)(((double)maxWidth / 100) * _fsafetybuffer);
                height = (int)((double)width * _fGoldenRatio);                
            }

            // Enforce that the height is not bigger than _MAXHEIGHT, anything bigger than 
            // that just look unpleasant.
            if (height >= _MAXHEIGHT)
            {
                height = _MAXHEIGHT;
                width = (int)((double)height / _fGoldenRatio);
            }

            // pass out the calculated height and width
            rect.left = 0;
            rect.top = 0;
            rect.bottom = height;
            rect.right = width;
        }

        /// <summary>
        /// Use this to quickle fade a window
        /// </summary>
        /// <param name="bFadeIn">Set to true to fade in, false to Fade out</param>
        private static void FadeWindow(IntPtr hWnd, bool bFadeIn)
        {

            // Stole Increment Fading from Buttonhook
            const int LOWER_INCREMENT = 7;
            const int MIDDLE_INCREMENT = 15;
            const int UPPER_INCREMENT = 25;

            // Increment Points *inverted*            
            const int LOWER_POINT = 60;
            const int MIDDLE_POINT = 160;
            const int UPPER_POINT = 230;

            // Fade in or Fade out?
            byte alpha = (!bFadeIn)? (byte)0 : (byte)255;
            bool bDone = false;

            if (!bFadeIn) // FadeOut
            {
                while (!bDone)
                {
                    if (alpha >= UPPER_POINT)
                    {
                        alpha = 255; // just show the thing already
                        bDone = true;
                    }
                    else if (alpha <= LOWER_POINT)
                        alpha += LOWER_INCREMENT;
                    else if (alpha <= MIDDLE_POINT)
                        alpha += MIDDLE_INCREMENT;
                    else
                        alpha += UPPER_INCREMENT;

                    SetAlphaTransparency(hWnd, alpha);
                    System.Threading.Thread.Sleep(20);
                }
            }
            else  // FadeIn
            {
                while (!bDone)
                {
                    if (alpha <= (255 - UPPER_POINT)) //25
                    {
                        alpha = 0; // just hide the thing already
                        bDone = true;
                    }
                    else if (alpha >= (255 - LOWER_POINT)) //195
                        alpha -= LOWER_INCREMENT;
                    else if (alpha >= (255 - MIDDLE_POINT)) // 95
                        alpha -= MIDDLE_INCREMENT;
                    else
                        alpha -= UPPER_INCREMENT;

                    SetAlphaTransparency(hWnd, alpha);
                    System.Threading.Thread.Sleep(20);
                }
            }
        }

        /// <summary>
        /// Helper function to determine the monitor and it's center and
        /// fade out the window there in GoldenRatio Siz
        /// </summary>
        private static void FadeWindowOutOfMonitorCenterInGoldenRatio(IntPtr hWnd, RECT orgRect)
        {
            // get the screen the original rect was mostly located
            Screen screen = Screen.FromRectangle(orgRect.AsRectangle);

            // now figure out the best rect to use for this screen 
            RECT goldenRect;
            CalculateGoldenRatio(screen, out goldenRect);
                                    
            // calculate where we must place the golden window 
            // (centering it on the screen)
            Point pt = new Point();
            pt.Y = (screen.Bounds.Height - goldenRect.AsRectangle.Height) / 2;
            pt.X = (screen.Bounds.Width - goldenRect.AsRectangle.Width) / 2;

            // Calculate the window center according to the screen
            pt.Y = screen.Bounds.Top + pt.Y;
            pt.X = screen.Bounds.Left + pt.X;            

            // Make the window totally transparent and show it in the golden ratio position            
            SetAlphaTransparency(hWnd, 0);
            Win32Functions.SetWindowPos(hWnd, IntPtr.Zero, pt.X, pt.Y, goldenRect.AsRectangle.Width, goldenRect.AsRectangle.Height, (uint) SetWindowPosFlags.SWP_SHOWWINDOW);

            // Now Fade the Window out
            FadeWindow(hWnd, false);                  
        }

    }
}

