using DarkUI.Config;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace DarkUI.Controls
{
    public class DarkDropdownList : Control
    {
        #region Event Region

        public event EventHandler SelectedItemChanged;

        #endregion

        #region Field Region

        private DarkControlState _controlState = DarkControlState.Normal;

        private ObservableCollection<DarkDropdownItem> _items = new ObservableCollection<DarkDropdownItem>();
        private DarkDropdownItem _selectedItem;

        private DarkContextMenu _menu = new DarkContextMenu();
        private bool _menuOpen = false;

        private bool _showBorder = true;

        private int _itemHeight = 22;
        private int _maxHeight = 130;

        private readonly int _iconSize = 16;

        private ToolStripDropDownDirection _dropdownDirection = ToolStripDropDownDirection.Default;

        #endregion

        #region Property Region

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ObservableCollection<DarkDropdownItem> Items
        {
            get { return _items; }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public DarkDropdownItem SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                _selectedItem = value;
                SelectedItemChanged?.Invoke(this, new EventArgs());
            }
        }

        [Category("Appearance")]
        [Description("Determines whether a border is drawn around the control.")]
        [DefaultValue(true)]
        public bool ShowBorder
        {
            get { return _showBorder; }
            set
            {
                _showBorder = value;
                Invalidate();
            }
        }

        protected override Size DefaultSize
        {
            get { return new Size(100, 26); }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public DarkControlState ControlState
        {
            get { return _controlState; }
        }

        [Category("Appearance")]
        [Description("Determines the height of the individual list view items.")]
        [DefaultValue(22)]
        public int ItemHeight
        {
            get { return _itemHeight; }
            set
            {
                _itemHeight = value;
                ResizeMenu();
            }
        }

        [Category("Appearance")]
        [Description("Determines the maximum height of the dropdown panel.")]
        [DefaultValue(130)]
        public int MaxHeight
        {
            get { return _maxHeight; }
            set
            {
                _maxHeight = value;
                ResizeMenu();
            }
        }

        [Category("Behavior")]
        [Description("Determines what location the dropdown list appears.")]
        [DefaultValue(ToolStripDropDownDirection.Default)]
        public ToolStripDropDownDirection DropdownDirection
        {
            get { return _dropdownDirection; }
            set { _dropdownDirection = value; }
        }

        #endregion

        #region Constructor Region

        public DarkDropdownList()
        {
            SetStyle(ControlStyles.OptimizedDoubleBuffer |
                     ControlStyles.ResizeRedraw |
                     ControlStyles.UserPaint |
                     ControlStyles.Selectable |
                     ControlStyles.UserMouse, true);

            _menu.AutoSize = false;
            _menu.Closed += Menu_Closed;

            Items.CollectionChanged += Items_CollectionChanged;
            SelectedItemChanged += DarkDropdownList_SelectedItemChanged;

            SetControlState(DarkControlState.Normal);
        }

        #endregion

        #region Method Region

        private ToolStripMenuItem GetMenuItem(DarkDropdownItem item)
        {
            foreach (ToolStripMenuItem menuItem in _menu.Items)
            {
                if ((DarkDropdownItem)menuItem.Tag == item)
                    return menuItem;
            }

            return null;
        }

        private void SetControlState(DarkControlState controlState)
        {
            if (_menuOpen)
                return;

            if (_controlState != controlState)
            {
                _controlState = controlState;
                Invalidate();
            }
        }

        private void ShowMenu()
        {
            if (_menu.Visible)
                return;

            SetControlState(DarkControlState.Pressed);

            _menuOpen = true;

            var pos = new Point(0, ClientRectangle.Bottom);

            if (_dropdownDirection == ToolStripDropDownDirection.AboveLeft || _dropdownDirection == ToolStripDropDownDirection.AboveRight)
                pos.Y = 0;

            _menu.Show(this, pos, _dropdownDirection);

            if (SelectedItem != null)
            {
                var selectedItem = GetMenuItem(SelectedItem);
                selectedItem.Select();
            }
        }

        private void ResizeMenu()
        {
            var width = ClientRectangle.Width;
            var height = (_menu.Items.Count * _itemHeight) + 4;

            if (height > _maxHeight)
                height = _maxHeight;

            // Dirty: Check what the autosized items are
            foreach (ToolStripMenuItem item in _menu.Items)
            {
                item.AutoSize = true;

                if (item.Size.Width > width)
                    width = item.Size.Width;

                item.AutoSize = false;
            }

            // Force the size
            foreach (ToolStripMenuItem item in _menu.Items)
                item.Size = new Size(width - 1, _itemHeight);

            _menu.Size = new Size(width, height);
        }

        #endregion

        #region Event Handler Region

        private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (DarkDropdownItem item in e.NewItems)
                {
                    var menuItem = new ToolStripMenuItem(item.Text)
                    {
                        Image = item.Icon,
                        AutoSize = false,
                        Height = _itemHeight,
                        Font = Font,
                        Tag = item,
                        TextAlign = ContentAlignment.MiddleLeft
                    };

                    _menu.Items.Add(menuItem);
                    menuItem.Click += Item_Select;

                    if (SelectedItem == null)
                        SelectedItem = item;
                }
            }

            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (DarkDropdownItem item in e.OldItems)
                {
                    foreach (ToolStripMenuItem menuItem in _menu.Items)
                    {
                        if ((DarkDropdownItem)menuItem.Tag == item)
                            _menu.Items.Remove(menuItem);
                    }
                }
            }

            ResizeMenu();
        }

        private void Item_Select(object sender, EventArgs e)
        {
            var menuItem = sender as ToolStripMenuItem;
            if (menuItem == null)
                return;

            var dropdownItem = (DarkDropdownItem)menuItem.Tag;
            if (_selectedItem != dropdownItem)
                SelectedItem = dropdownItem;
        }

        private void DarkDropdownList_SelectedItemChanged(object sender, EventArgs e)
        {
            foreach (ToolStripMenuItem item in _menu.Items)
            {
                if ((DarkDropdownItem)item.Tag == SelectedItem)
                {
                    item.BackColor = Colors.DarkBlueBackground;
                    item.Font = new Font(Font, FontStyle.Bold);
                }
                else
                {
                    item.BackColor = Colors.GreyBackground;
                    item.Font = new Font(Font, FontStyle.Regular);
                }
            }

            Invalidate();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            ResizeMenu();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (e.Button == MouseButtons.Left)
            {
                if (ClientRectangle.Contains(e.Location))
                    SetControlState(DarkControlState.Pressed);
                else
                    SetControlState(DarkControlState.Hover);
            }
            else
            {
                SetControlState(DarkControlState.Hover);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            ShowMenu();
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            SetControlState(DarkControlState.Normal);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);

            SetControlState(DarkControlState.Normal);
        }

        protected override void OnMouseCaptureChanged(EventArgs e)
        {
            base.OnMouseCaptureChanged(e);

            var location = Cursor.Position;

            if (!ClientRectangle.Contains(location))
                SetControlState(DarkControlState.Normal);
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);

            Invalidate();
        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);

            var location = Cursor.Position;

            if (!ClientRectangle.Contains(location))
                SetControlState(DarkControlState.Normal);
            else
                SetControlState(DarkControlState.Hover);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if (e.KeyCode == Keys.Space)
                ShowMenu();
        }

        private void Menu_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            _menuOpen = false;

            if (!ClientRectangle.Contains(MousePosition))
                SetControlState(DarkControlState.Normal);
            else
                SetControlState(DarkControlState.Hover);
        }

        #endregion

        #region Render Region

        protected override void OnPaint(PaintEventArgs e)
        {
            var g = e.Graphics;

            // Draw background
            using (var b = new SolidBrush(Colors.MediumBackground))
            {
                g.FillRectangle(b, ClientRectangle);
            }

            // Draw normal state
            if (ControlState == DarkControlState.Normal)
            {
                if (ShowBorder)
                {
                    using (var p = new Pen(Colors.LightBorder, 1))
                    {
                        var modRect = new Rectangle(ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Width - 1, ClientRectangle.Height - 1);
                        g.DrawRectangle(p, modRect);
                    }
                }
            }

            // Draw hover state
            if (ControlState == DarkControlState.Hover)
            {
                using (var b = new SolidBrush(Colors.DarkBorder))
                {
                    g.FillRectangle(b, ClientRectangle);
                }

                using (var b = new SolidBrush(Colors.DarkBackground))
                {
                    var arrowRect = new Rectangle(ClientRectangle.Right - DropdownIcons.small_arrow.Width - 8, ClientRectangle.Top, DropdownIcons.small_arrow.Width + 8, ClientRectangle.Height);
                    g.FillRectangle(b, arrowRect);
                }

                using (var p = new Pen(Colors.BlueSelection, 1))
                {
                    var modRect = new Rectangle(ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Width - 1 - DropdownIcons.small_arrow.Width - 8, ClientRectangle.Height - 1);
                    g.DrawRectangle(p, modRect);
                }
            }

            // Draw pressed state
            if (ControlState == DarkControlState.Pressed)
            {
                using (var b = new SolidBrush(Colors.DarkBorder))
                {
                    g.FillRectangle(b, ClientRectangle);
                }

                using (var b = new SolidBrush(Colors.BlueSelection))
                {
                    var arrowRect = new Rectangle(ClientRectangle.Right - DropdownIcons.small_arrow.Width - 8, ClientRectangle.Top, DropdownIcons.small_arrow.Width + 8, ClientRectangle.Height);
                    g.FillRectangle(b, arrowRect);
                }
            }

            // Draw dropdown arrow
            using (var img = DropdownIcons.small_arrow)
            {
                g.DrawImageUnscaled(img, ClientRectangle.Right - img.Width - 4, ClientRectangle.Top + (ClientRectangle.Height / 2) - (img.Height / 2));
            }

            // Draw selected item
            if (SelectedItem != null)
            {
                // Draw Icon
                var hasIcon = SelectedItem.Icon != null;

                if (hasIcon)
                {
                    g.DrawImageUnscaled(SelectedItem.Icon, new Point(ClientRectangle.Left + 5, ClientRectangle.Top + (ClientRectangle.Height / 2) - (_iconSize / 2)));
                }

                // Draw Text
                using (var b = new SolidBrush(Colors.LightText))
                {
                    var stringFormat = new StringFormat
                    {
                        Alignment = StringAlignment.Near,
                        LineAlignment = StringAlignment.Center
                    };

                    var rect = new Rectangle(ClientRectangle.Left + 2, ClientRectangle.Top, ClientRectangle.Width - 16, ClientRectangle.Height);

                    if (hasIcon)
                    {
                        rect.X += _iconSize + 7;
                        rect.Width -= _iconSize + 7;
                    }

                    g.DrawString(SelectedItem.Text, Font, b, rect, stringFormat);
                }
            }
        }

        #endregion
    }
}
