using Choinka.Properties;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Choinka
{
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;

        public POINT(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { }

        public static implicit operator System.Drawing.Point(POINT p)
        {
            return new System.Drawing.Point(p.X, p.Y);
        }

        public static implicit operator POINT(System.Drawing.Point p)
        {
            return new POINT(p.X, p.Y);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left, Top, Right, Bottom;

        public RECT(int left, int top, int right, int bottom)
        {
            Left = left;
            Top = top;
            Right = right;
            Bottom = bottom;
        }

        public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }

        public int X
        {
            get { return Left; }
            set { Right -= (Left - value); Left = value; }
        }

        public int Y
        {
            get { return Top; }
            set { Bottom -= (Top - value); Top = value; }
        }

        public int Height
        {
            get { return Bottom - Top; }
            set { Bottom = value + Top; }
        }

        public int Width
        {
            get { return Right - Left; }
            set { Right = value + Left; }
        }

        public System.Drawing.Point Location
        {
            get { return new System.Drawing.Point(Left, Top); }
            set { X = value.X; Y = value.Y; }
        }

        public System.Drawing.Size Size
        {
            get { return new System.Drawing.Size(Width, Height); }
            set { Width = value.Width; Height = value.Height; }
        }

        public static implicit operator System.Drawing.Rectangle(RECT r)
        {
            return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
        }

        public static implicit operator RECT(System.Drawing.Rectangle r)
        {
            return new RECT(r);
        }

        public static bool operator ==(RECT r1, RECT r2)
        {
            return r1.Equals(r2);
        }

        public static bool operator !=(RECT r1, RECT r2)
        {
            return !r1.Equals(r2);
        }

        public bool Equals(RECT r)
        {
            return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
        }

        public override bool Equals(object obj)
        {
            if (obj is RECT)
                return Equals((RECT)obj);
            else if (obj is System.Drawing.Rectangle)
                return Equals(new RECT((System.Drawing.Rectangle)obj));
            return false;
        }

        public override int GetHashCode()
        {
            return ((System.Drawing.Rectangle)this).GetHashCode();
        }

        public override string ToString()
        {
            return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
        }
    }

    enum ShowWindowCommands
    {
        /// <summary>
        /// Hides the window and activates another window.
        /// </summary>
        Hide = 0,
        /// <summary>
        /// Activates and displays a window. If the window is minimized or
        /// maximized, the system restores it to its original size and position.
        /// An application should specify this flag when displaying the window
        /// for the first time.
        /// </summary>
        Normal = 1,
        /// <summary>
        /// Activates the window and displays it as a minimized window.
        /// </summary>
        ShowMinimized = 2,
        /// <summary>
        /// Maximizes the specified window.
        /// </summary>
        Maximize = 3, // is this the right value?
        /// <summary>
        /// Activates the window and displays it as a maximized window.
        /// </summary>      
        ShowMaximized = 3,
        /// <summary>
        /// Displays a window in its most recent size and position. This value
        /// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
        /// the window is not activated.
        /// </summary>
        ShowNoActivate = 4,
        /// <summary>
        /// Activates the window and displays it in its current size and position.
        /// </summary>
        Show = 5,
        /// <summary>
        /// Minimizes the specified window and activates the next top-level
        /// window in the Z order.
        /// </summary>
        Minimize = 6,
        /// <summary>
        /// Displays the window as a minimized window. This value is similar to
        /// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
        /// window is not activated.
        /// </summary>
        ShowMinNoActive = 7,
        /// <summary>
        /// Displays the window in its current size and position. This value is
        /// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
        /// window is not activated.
        /// </summary>
        ShowNA = 8,
        /// <summary>
        /// Activates and displays the window. If the window is minimized or
        /// maximized, the system restores it to its original size and position.
        /// An application should specify this flag when restoring a minimized window.
        /// </summary>
        Restore = 9,
        /// <summary>
        /// Sets the show state based on the SW_* value specified in the
        /// STARTUPINFO structure passed to the CreateProcess function by the
        /// program that started the application.
        /// </summary>
        ShowDefault = 10,
        /// <summary>
        ///  <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
        /// that owns the window is not responding. This flag should only be
        /// used when minimizing windows from a different thread.
        /// </summary>
        ForceMinimize = 11
    }

    /// <summary>
    /// Contains information about the placement of a window on the screen.
    /// </summary>
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    internal struct WINDOWPLACEMENT
    {
        /// <summary>
        /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
        /// <para>
        /// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly.
        /// </para>
        /// </summary>
        public int Length;

        /// <summary>
        /// Specifies flags that control the position of the minimized window and the method by which the window is restored.
        /// </summary>
        public int Flags;

        /// <summary>
        /// The current show state of the window.
        /// </summary>
        public ShowWindowCommands ShowCmd;

        /// <summary>
        /// The coordinates of the window's upper-left corner when the window is minimized.
        /// </summary>
        public POINT MinPosition;

        /// <summary>
        /// The coordinates of the window's upper-left corner when the window is maximized.
        /// </summary>
        public POINT MaxPosition;

        /// <summary>
        /// The window's coordinates when the window is in the restored position.
        /// </summary>
        public RECT NormalPosition;

        /// <summary>
        /// Gets the default (empty) value.
        /// </summary>
        public static WINDOWPLACEMENT Default
        {
            get
            {
                WINDOWPLACEMENT result = new WINDOWPLACEMENT();
                result.Length = Marshal.SizeOf(result);
                return result;
            }
        }
    }

    partial class Form1
    {
        public delegate bool WindowEnumCallback(int hwnd, int lparam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool EnumWindows(WindowEnumCallback lpEnumFunc, int lParam);

        [DllImport("user32.dll")]
        public static extern void GetWindowText(int h, StringBuilder s, int nMaxCount);

        [DllImport("user32.dll")]
        public static extern bool IsWindowVisible(int h);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool GetWindowPlacement(int hWnd, ref WINDOWPLACEMENT lpwndpl);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetWindowRect(int hwnd, out Rectangle lpRect);

        [DllImport("user32.dll", SetLastError = true)]
        static extern int GetWindow(int hWnd, GetWindow_Cmd uCmd);

        enum GetWindow_Cmd : uint
        {
            GW_HWNDFIRST = 0,
            GW_HWNDLAST = 1,
            GW_HWNDNEXT = 2,
            GW_HWNDPREV = 3,
            GW_OWNER = 4,
            GW_CHILD = 5,
            GW_ENABLEDPOPUP = 6
        }

        private Dictionary<int, List<object>> dict = new Dictionary<int, List<object>>();
        private bool AddWnd(int hwnd, int lparam)
        {
            if (IsWindowVisible(hwnd) && hwnd != ((int)(this.Handle)))
            {
                List<object> list = new List<object>();
                WINDOWPLACEMENT pl = new WINDOWPLACEMENT();
                GetWindowPlacement(hwnd, ref pl);
                Rectangle r;
                GetWindowRect(hwnd, out r);
                StringBuilder sb = new StringBuilder(255);
                GetWindowText(hwnd, sb, sb.Capacity);
                if (sb.Length > 0 && ((pl.ShowCmd == ShowWindowCommands.Normal && r.Top > this.Height / 2) || pl.ShowCmd == ShowWindowCommands.Maximize) && sb.ToString() != this.Text && r.Width > this.Width)
                {
                    int z = 0;
                    for (int h = hwnd; h != 0; h = GetWindow(h, GetWindow_Cmd.GW_HWNDPREV)) z++;
                    list.Add(hwnd);
                    list.Add(sb.ToString());
                    list.Add(r);
                    list.Add(pl);
                    dict.Add(z, list);
                }
            }
            return true;
        }

        Boolean kierunek = new Random().Next(2) == 0;
        int hwndpodloga;
        int szer = Screen.PrimaryScreen.WorkingArea.Width;
        int wys = Screen.PrimaryScreen.WorkingArea.Height;

        private void biegnij()
        {
            dict.Clear();
            EnumWindows(new WindowEnumCallback(AddWnd), 0);

            foreach (var item in dict.OrderBy(i => i.Key))
            {
                if (((WINDOWPLACEMENT)(item.Value[3])).ShowCmd == ShowWindowCommands.Maximize)
                {
                    this.Top = wys - this.Height + 15; //ustaw dziada na podloge
                    hwndpodloga = 0;
                    break;
                }

                if ((int)(item.Value[0]) != hwndpodloga)
                    teleport();
                break;
            }

            WINDOWPLACEMENT z = new WINDOWPLACEMENT();
            GetWindowPlacement(hwndpodloga, ref z);

            if (hwndpodloga != 0)
            {
                if (!IsWindowVisible(hwndpodloga) ||
                    z.NormalPosition.Top + 15 != this.Bottom ||
                    z.ShowCmd != ShowWindowCommands.Normal ||
                    this.Right > z.NormalPosition.Right + 30 ||
                    this.Left < z.NormalPosition.Left - 35)
                {
                    teleport();
                }

                if (this.Right > z.NormalPosition.Right || this.Left + 20 <= z.NormalPosition.Left) kier();
            }

            if (this.Left <= 0 || this.Right >= szer) kier();

            idzSynu(15);
        }

        private void idzSynu(int krok)
        {
            if (kierunek)
                this.Left -= krok;
            else
                this.Left += krok;
        }

        private void teleport()
        {
            zanik();
            this.Top = wys - this.Height + 15; //ustaw dziada na podloge
            hwndpodloga = 0;
            dict.Clear();
            EnumWindows(new WindowEnumCallback(AddWnd), 0);

            foreach (var item in dict.OrderBy(i => i.Key))
            {
                if (((WINDOWPLACEMENT)(item.Value[3])).ShowCmd == ShowWindowCommands.Maximize)
                    break;

                Rectangle r = (Rectangle)(item.Value[2]);
                this.Top = r.Top - this.Height + 15;

                this.Left = new Random().Next(r.Left - 20, ((WINDOWPLACEMENT)(item.Value[3])).NormalPosition.Right - this.Width);
                kierunek = new Random().Next(2) == 0;
                kier();
                hwndpodloga = (int)(item.Value[0]);
                break;
            }
            powrot();
        }

        private void kier()
        {
            if (kierunek)
            {
                kierunek = false;
                pictureBox1.Image = Resources.christmas_animated_Santa_sneaking2;
            }
            else
            {
                kierunek = true;
                pictureBox1.Image = Resources.christmas_animated_Santa_sneaking;
            }
        }
    }
}
