using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Collections;
using System.Drawing.Drawing2D;

namespace XWolf.Aero
{
    #region XGlassForm
    public class XGlassForm : Form
    {
        private bool composite;
        private GlassMargins glassMargins = new GlassMargins(50, 0, 0, 0);
        private Image glassImage = null;
        private Point glassImageLocation = new Point();
        private bool glassDrag = true;

        public XGlassForm()
        {
            DoubleBuffered = true;
            composite = Glass.Composite;
            this.Shown += new EventHandler(XGlassForm_Shown);
            this.Paint += new PaintEventHandler(XGlassForm_Paint);
            this.Resize += new EventHandler(XGlassForm_Resize);
            this.MouseDown += new MouseEventHandler(XGlassForm_MouseDown);
        }

        void XGlassForm_Resize(object sender, EventArgs e)
        {
            Invalidate();
        }

        //protected override void WndProc(ref Message msg)
        //{
        //    int[] except={3,6,8,13,14,20,28,32,36,70,71,132,134,160,161,174,274,512,533,534,561,562,641,642,674,675};
        //    //mensaje 20

        //    //switch (msg.Msg)
        //    //{
        //    //    case WM_DWMCOLORIZATIONCOLORCHANGED:
        //    //        // The color format of currColor is 0xAARRGGBB.
        //    //        uint currColor = (uint)msg.WParam.ToInt32();
        //    //        bool opacityblend = (msg.LParam.ToInt32() != 0);
        //    //        ...
        //    //        break;
        //    //}
        //    base.WndProc(ref msg);
        //    foreach (int msge in except)
        //        if (msg.Msg == msge)
        //            return;
        //    Console.WriteLine("MSG: " + msg.Msg);

        //}

        void XGlassForm_Shown(object sender, EventArgs e)
        {
            UpdateGlass();
        }
        void XGlassForm_Paint(object sender, PaintEventArgs e)
        {
            Size client = ClientSize;
            if (DesignMode)
            {
                Pen p = new Pen(Color.Gray);
                Rectangle glassclient = new Rectangle(glassMargins.Left - 1, glassMargins.Top - 1, client.Width - (glassMargins.Left + glassMargins.Right) + 1, client.Height - (glassMargins.Top + glassMargins.Bottom) + 1);
                e.Graphics.DrawRectangle(p, glassclient);
            }
            else
                if (glassMargins.IsFull)
                    e.Graphics.Clear(Color.Transparent);
                else
                {

                    Rectangle glassclient = new Rectangle(glassMargins.Left, glassMargins.Top, client.Width - (glassMargins.Left + glassMargins.Right), client.Height - (glassMargins.Top + glassMargins.Bottom));
                    e.Graphics.Clear(composite ? Color.Transparent : SystemColors.ButtonShadow);
                    e.Graphics.FillRectangle(new SolidBrush(BackColor), glassclient);
                }
            if (glassImage != null)
            {
                Rectangle r = new Rectangle(glassImageLocation, glassImage.Size);
                e.Graphics.DrawImage(glassImage, r);
            }
            //if (glassMargins.IsFull)
            //    e.Graphics.Clear(Color.Transparent);
            //else
            //{
            //    Brush b = new SolidBrush(Color.Transparent);
            //    Size client=ClientSize;
            //    e.Graphics.FillRectangle(b, 0, 0, glassMargins.Left, client.Width);
            //}
        }

        void XGlassForm_MouseDown(object sender, MouseEventArgs e)
        {
            if (!glassDrag)
                return;
            Size c = ClientSize;
            if (e.X > glassMargins.Left && e.X < c.Width - glassMargins.Right &&
                e.Y > glassMargins.Top && e.Y < c.Height - glassMargins.Bottom)
                return;
            // Releasing the mouse capture to allow the form to receive the order
            Aero.ReleaseCapture();
            // Ordering the form
            // Simulating left mouse button clicking on the title bar
            Aero.SendMessage(this.Handle, // Form handle
            Aero.WM_NCLBUTTONDOWN, // Simulating the click
            Aero.HTCAPTION, // Attaching it to the title bar
            0); // No further options required
        }


        private void UpdateGlass()
        {
            if (composite)
            {
                Glass.GlassForm(this, glassMargins);
                Invalidate();
            }
        }

        protected void DrawText(Graphics g, string text, Font font, Brush brush, PointF p)
        {
            GraphicsPath gp = new GraphicsPath();
            gp.AddString(text, font.FontFamily, (int)font.Style, font.Size, p, StringFormat.GenericDefault);
            g.FillPath(brush, gp);
            gp.Dispose();
        }

        protected void DrawText(Graphics g, string text, FontFamily fontfam, int fontstyle, float fontsize, Brush brush, PointF p)
        {
            GraphicsPath gp = new GraphicsPath();
            gp.AddString(text, fontfam, fontstyle, fontsize, p, StringFormat.GenericDefault);
            g.FillPath(brush, gp);
            gp.Dispose();
        }

        [Description("Margenes del efecto cristal"), Category("Aero Glass"), RefreshProperties(RefreshProperties.All)]
        public GlassMargins GlassMargins
        {
            get { return glassMargins; }
            set { glassMargins = value; UpdateGlass(); }
        }

        [Description("Cristal completo"), Category("Aero Glass"), RefreshProperties(RefreshProperties.All)]
        public bool FullGlass
        {
            get { return glassMargins.IsFull; }
            set { if (value) glassMargins = GlassMargins.GetFullGlassMargins(); UpdateGlass(); }
        }

        [Description("Imagen en el cristal"), Category("Aero Glass")]
        public Image GlassImage
        {
            get { return glassImage; }
            set { glassImage = value; Invalidate(); }
        }

        [Description("Posicion de Imagen en el cristal"), Category("Aero Glass")]
        public Point GlassImageLocation
        {
            get { return glassImageLocation; }
            set { glassImageLocation = value; Invalidate(); }
        }

        [Description("Usar la extensión del glass como un tirador"), Category("Aero Glass")]
        public bool GlassDrag
        {
            get { return glassDrag; }
            set { glassDrag = value; }
        }
    }
    #endregion

    public class Aero
    {
        internal static bool hasAero;

        #region API
        private class DWNAPI
        {
            [DllImport("dwmapi.dll")]
            public extern static int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref GlassMargins margin);
            [DllImport("dwmapi.dll")]
            public extern static int DwmIsCompositionEnabled(ref int en);
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.I4)]
        public static extern int SendMessage(IntPtr hWnd, [param: MarshalAs(UnmanagedType.U4)]uint Msg, [param: MarshalAs(UnmanagedType.U4)]uint wParam, [param: MarshalAs(UnmanagedType.I4)]int lParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ReleaseCapture();

        public const uint WM_NCLBUTTONDOWN = 0xA1; // 161
        public const uint HTCAPTION = 2;
        #endregion


        #region APIex
        internal static void DwmExtendFrameIntoClientArea(IntPtr hwnd, GlassMargins margin)
        {
            try
            {
                DWNAPI.DwmExtendFrameIntoClientArea(hwnd, ref margin);
            }
            catch { };
        }
        internal static bool DwmIsCompositionEnabled()
        {
            try
            {
                int result = 0;
                DWNAPI.DwmIsCompositionEnabled(ref result);
                return result > 0;
            }
            catch { return false; };
        }
        #endregion

        static Aero()
        {
            hasAero = false;
            try
            {
                DwmIsCompositionEnabled();
                hasAero = true;
            }
            catch { };
        }

        public static bool HasAero { get { return hasAero; } }
    }

    #region GalssMarginsConverter
    public class GalssMarginsConverter : TypeConverter
    {
        private static CultureInfo FixCulture(CultureInfo c)
        {
            if (c == null)
                return CultureInfo.CurrentCulture;
            return c;
        }
        private static char GetListSeparator(CultureInfo c)
        {
            c = FixCulture(c);
            return c.TextInfo.ListSeparator[0];
        }

        #region ConvertFrom

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            string str = value as string;
            if (str == null)
                return base.ConvertFrom(context, culture, value);
            str = str.Trim();
            if (str.Length == 0)
                return null;
            culture = FixCulture(culture);
            char ch = culture.TextInfo.ListSeparator[0];
            string[] strArray = str.Split(new char[] { ch });
            int[] numArray = new int[strArray.Length];
            if (numArray.Length != 4)
                throw new ArgumentException("Solo se admiten 4 valores");
            TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
            for (int i = 0; i < numArray.Length; i++)
                numArray[i] = (int)converter.ConvertFromString(context, culture, strArray[i]);
            return new GlassMargins(numArray[0], numArray[1], numArray[2], numArray[3]);
        }

        #endregion

        #region ConvertTo

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
                throw new ArgumentNullException("destinationType");
            if (value is GlassMargins)
            {
                if (destinationType == typeof(string))
                {
                    GlassMargins gmargins = (GlassMargins)value;
                    if (culture == null)
                        culture = CultureInfo.CurrentCulture;
                    string separator = culture.TextInfo.ListSeparator + " ";
                    TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
                    string[] strArray ={
                        converter.ConvertToString(context, culture, gmargins.Left),
                        converter.ConvertToString(context, culture, gmargins.Top),
                        converter.ConvertToString(context, culture, gmargins.Right),
                        converter.ConvertToString(context, culture, gmargins.Bottom)
                    };
                    return string.Join(separator, strArray);
                }
                //if (destinationType == typeof(InstanceDescriptor))
                //{
                //    GlassMargins gmargins = (GlassMargins)value;

                //    return new InstanceDescriptor(typeof(GlassMargins).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) }), new object[] { gmargins.Left, gmargins.Top, gmargins.Right, gmargins.Bottom });
                //}
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }

        #endregion

        #region Instancia
        public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (propertyValues == null)
            {
                throw new ArgumentNullException("propertyValues");
            }
            GlassMargins gmargins = (GlassMargins)context.PropertyDescriptor.GetValue(context.Instance);
            return new GlassMargins((int)propertyValues["Left"], (int)propertyValues["Top"], (int)propertyValues["Right"], (int)propertyValues["Bottom"]);
        }

        public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        #endregion

        #region Properties
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            return TypeDescriptor.GetProperties(value, attributes).Sort(new string[] { "Left", "Top", "Right", "Bottom" });
        }
        #endregion
    }
    #endregion

    //#region GalssMarginsConverter
    //public class GalssMarginsConverter : TypeConverter
    //{
    //    #region ConvertFrom

    //    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    //    {
    //        return ((sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType));
    //    }

    //    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    //    {
    //        string str = value as string;
    //        if (str == null)
    //            return base.ConvertFrom(context, culture, value);
    //        str = str.Trim();
    //        if (str.Length == 0)
    //            return null;
    //        if (culture == null)
    //            culture = CultureInfo.CurrentCulture;
    //        char ch = culture.TextInfo.ListSeparator[0];
    //        string[] strArray = str.Split(new char[] { ch });
    //        int[] numArray = new int[strArray.Length];
    //        TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
    //        for (int i = 0; i < numArray.Length; i++)
    //            numArray[i] = (int)converter.ConvertFromString(context, culture, strArray[i]);
    //        if (numArray.Length != 4)
    //            throw new ArgumentException();
    //        return new GlassMargins(numArray[0], numArray[1], numArray[2], numArray[3]);
    //    }

    //    #endregion

    //    #region ConvertTo

    //    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    //    {
    //        //return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
    //        return ((destinationType == typeof(String)) || base.CanConvertTo(context, destinationType));
    //    }

    //    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    //    {
    //        if (destinationType == null)
    //            throw new ArgumentNullException("destinationType");
    //        if (value is GlassMargins)
    //        {
    //            if (destinationType == typeof(string))
    //            {
    //                GlassMargins gmargins = (GlassMargins)value;
    //                if (culture == null)
    //                    culture = CultureInfo.CurrentCulture;
    //                string separator = culture.TextInfo.ListSeparator + " ";
    //                TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
    //                string[] strArray ={
    //                    converter.ConvertToString(context, culture, gmargins.Left),
    //                    converter.ConvertToString(context, culture, gmargins.Top),
    //                    converter.ConvertToString(context, culture, gmargins.Right),
    //                    converter.ConvertToString(context, culture, gmargins.Bottom)
    //                };
    //                return string.Join(separator, strArray);
    //            }
    //            //if (destinationType == typeof(InstanceDescriptor))
    //            //{
    //            //    GlassMargins gmargins = (GlassMargins)value;

    //            //    return new InstanceDescriptor(typeof(GlassMargins).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) }), new object[] { gmargins.Left, gmargins.Top, gmargins.Right, gmargins.Bottom });
    //            //}
    //        }
    //        return base.ConvertTo(context, culture, value, destinationType);
    //    }

    //    #endregion

    //    //public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
    //    //{
    //    //    if (context == null)
    //    //    {
    //    //        throw new ArgumentNullException("context");
    //    //    }
    //    //    if (propertyValues == null)
    //    //    {
    //    //        throw new ArgumentNullException("propertyValues");
    //    //    }
    //    //    GlassMargins gmargins = (GlassMargins)context.PropertyDescriptor.GetValue(context.Instance);
    //    //    return new GlassMargins((int)propertyValues["Left"], (int)propertyValues["Top"], (int)propertyValues["Right"], (int)propertyValues["Bottom"]);
    //    //}

    //    //public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
    //    //{
    //    //    return true;
    //    //}

    //    #region Properties
    //    public override bool GetPropertiesSupported(ITypeDescriptorContext context)
    //    {
    //        return true;
    //    }

    //    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    //    {
    //        return TypeDescriptor.GetProperties(value, attributes).Sort(new string[] { "Left", "Top", "Right", "Bottom" });
    //    }
    //    #endregion
    //}
    //#endregion

    #region GlassMargins
    [Serializable, StructLayout(LayoutKind.Sequential), TypeConverter(typeof(GalssMarginsConverter))]
    public struct GlassMargins
    {
        private int left;
        private int right;
        private int top;
        private int bottom;

        public static GlassMargins GetFullGlassMargins()
        {
            return new GlassMargins(-1, -1, -1, -1);
        }

        public GlassMargins(int left, int top, int right, int bottom)
        {
            this.left = left;
            this.right = right;
            this.top = top;
            this.bottom = bottom;
        }

        public int Top { get { return top; } set { top = value; } }
        public int Left { get { return left; } set { left = value; } }
        public int Right { get { return right; } set { right = value; } }
        public int Bottom { get { return bottom; } set { bottom = value; } }

        [Browsable(false)]
        public bool IsFull { get { return top == -1 && left == -1 && right == -1 & bottom == -1; } }

        [Browsable(false)]
        public bool IsNone { get { return top == 0 && left == 0 && right == 0 & bottom == 0; } }

        public override string ToString()
        {
            if (IsFull)
                return "Full Glass";
            if (IsNone)
                return "No Glass";
            return left + "; " + top + "; " + right + "; " + bottom;
        }
    };
    #endregion

    internal class Glass
    {
        private static bool CheckComposite()
        {
            if (!Aero.hasAero)
                return false;
            return Aero.DwmIsCompositionEnabled();
        }

        public static void GlassForm(Form f)
        {
            GlassForm(f, GlassMargins.GetFullGlassMargins());
        }

        public static void GlassForm(Form f, int leftmargin, int bottommargin, int rightmargin, int topmargin)
        {
            GlassForm(f, new GlassMargins(leftmargin, bottommargin, rightmargin, topmargin));
        }

        public static void GlassForm(Form f, GlassMargins margins)
        {
            if (!Composite)
                return;
            Aero.DwmExtendFrameIntoClientArea(f.Handle, margins);
        }

        public static bool Composite { get { return CheckComposite(); } }
    }

}
