using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace ElmarBoye.Samples.Forms
{
    /// <summary>
    /// Basis Implementation eine flachen ComboBox, ähnlich der ComboBox mit ComboBoxStyle.Flat bzw. Popup
    /// </summary>
    [ToolboxItem(true)]
    public class FlatComboBox : System.Windows.Forms.ComboBox
    {
        private bool _mouseEntered;

        public FlatComboBox()
            : base()
        {
            _mouseEntered = false;
        }

        #region Internal/Protected Properties
        private bool MouseEntered
        {
            get { return _mouseEntered; }
            set
            {
                if (_mouseEntered != value)
                {
                    _mouseEntered = value;
                    if (this.FlatStyle == FlatStyle.Popup)
                    {
                        this.Invalidate();
                    }
                }
            }
        }
        #endregion Internal/Protected Properties

        #region Overrides
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            this.MouseEntered = true;
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseEnter(e);
            this.MouseEntered = false;
        }

        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
        }
        #endregion Overrides

        #region Draw
        protected override void WndProc(ref Message m)
        {
            // TODO: In NativeMethods verschieben
            const int WM_SETFOCUS = 7;
            const int WM_KILLFOCUS = 8;
            const int WM_PAINT = 15;

            switch (m.Msg)
            {
                case WM_SETFOCUS:
                case WM_KILLFOCUS:
                    if (base.FlatStyle == FlatStyle.Popup)
                        this.Invalidate();
                    break;

                case WM_PAINT:
                    // Überschreibt das Standard-Verhalten 
                    if (this.DropDownStyle != ComboBoxStyle.Simple
                        && (base.FlatStyle == FlatStyle.Flat || base.FlatStyle == FlatStyle.Popup))
                    {
                        base.DefWndProc(ref m);
                        using (Graphics graphics = Graphics.FromHwndInternal(base.Handle))
                        {
                            this.DrawCombo(graphics);
                        }
                    }
                    else
                    {
                        base.WndProc(ref m);
                    }
                    return;

            }
            base.WndProc(ref m);
        }

        private void DrawCombo(Graphics graphics)
        {
            if (this.DropDownStyle == ComboBoxStyle.Simple)
                return;

            // TODO: RightToLeft Support
            int arrowWidth = SystemInformation.HorizontalScrollBarArrowWidth;
            Rectangle innerBounds = new Rectangle(
                base.ClientRectangle.X, base.ClientRectangle.Y,
                base.ClientRectangle.Width - 1, base.ClientRectangle.Height - 1);
            Rectangle borderRectangle = new Rectangle(
                innerBounds.X + 1, innerBounds.Y + 1,
                innerBounds.Width - arrowWidth - 2, innerBounds.Height - 2);
            Rectangle dropDownRectangle = new Rectangle(
                borderRectangle.Right + 1, borderRectangle.Y,
                arrowWidth, borderRectangle.Height + 1);
            Rectangle focusRectangle = new Rectangle(
                dropDownRectangle.X, innerBounds.Y,
                innerBounds.Right - dropDownRectangle.X, innerBounds.Height);

            // Rahmen Zeichnen
            using (Pen pen = new Pen(base.Enabled ? this.BackColor : SystemColors.Control))
            {
                graphics.DrawRectangle(pen, borderRectangle);
            }

            // Focus Rechteck zeichnen
            if (!base.Enabled
                || (base.FlatStyle == FlatStyle.Popup
                && (base.ContainsFocus || this.MouseEntered)))
            {
                graphics.DrawRectangle(SystemPens.ControlDark, innerBounds);
                graphics.DrawRectangle(SystemPens.ControlDark, focusRectangle);
            }
            else
            {
                using (Pen pen = new Pen((base.Enabled)
                           ? SystemColors.Window
                           : SystemColors.ControlDark))
                {
                    graphics.DrawRectangle(pen, innerBounds);
                    graphics.DrawRectangle(pen, focusRectangle);
                }
            }

            if (!base.Enabled)
            {
                // Rahmen .Inflate(-1, -1)
                Rectangle disabledRectangle = new Rectangle(
                    borderRectangle.X + 1, borderRectangle.Y + 1,
                    borderRectangle.Width - 2, borderRectangle.Height - 2);
                graphics.DrawRectangle(SystemPens.Control, disabledRectangle);
            }

            // DropDown zeichnen
            this.DrawDropDown(graphics, dropDownRectangle);
        }

        private void DrawDropDown(Graphics graphics, Rectangle bounds)
        {
            int arrowX = bounds.Left + (bounds.Width / 2) + (bounds.Width % 2);
            int arrowY = bounds.Top + (bounds.Height / 2);
            Point[] arrowPoints = new Point[]
			{
				new Point(arrowX - 4, arrowY - 1),
				new Point(arrowX, arrowY + 3),
				new Point(arrowX + 4, arrowY - 1)
			};

            graphics.FillRectangle(SystemBrushes.Control, bounds);
            graphics.FillPolygon(
                (base.Enabled) ? SystemBrushes.ControlText : SystemBrushes.ControlDark,
                arrowPoints);
        }
        #endregion Draw
    }
}
