// Copyright (c) DotSpatial Team. All rights reserved.
// Licensed under the MIT license. See License.txt file in the project root for full license information.

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

namespace DotSpatial.Symbology.Forms
{
    /// <summary>
    /// A color drop down.
    /// </summary>
    internal class ColorDropDown : ComboBox
    {
        #region Fields

        private readonly Pen _boxPen;
        private Brush _backBrush;
        private Array _colors;
        private Brush _foreBrush;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ColorDropDown"/> class with known colors populated in it.
        /// </summary>
        public ColorDropDown()
        {
            // Fill the color array with known colors
            _colors = Enum.GetValues(typeof(KnownColor));
            _boxPen = Pens.Black;

            DrawMode = DrawMode.OwnerDrawFixed;
            DropDownStyle = ComboBoxStyle.DropDownList;

            if (DesignMode == false)
            {
                foreach (KnownColor kc in _colors)
                {
                    Items.Add(kc);
                }
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the draw mode.
        /// </summary>
        [Browsable(false)]
        public new DrawMode DrawMode
        {
            get
            {
                return base.DrawMode;
            }

            set
            {
                base.DrawMode = value;
            }
        }

        /// <summary>
        /// Gets or sets the drop down style.
        /// </summary>
        [Browsable(false)]
        public new ComboBoxStyle DropDownStyle
        {
            get
            {
                return base.DropDownStyle;
            }

            set
            {
                base.DropDownStyle = value;
            }
        }

        /// <summary>
        /// Gets or sets the currently selected color from this dropdown control.
        /// </summary>
        public Color Value
        {
            get
            {
                Color col = Color.Empty;

                if (SelectedItem is KnownColor)
                {
                    // the color to draw
                    col = Color.FromKnownColor((KnownColor)SelectedItem);
                }

                if (SelectedItem is Color)
                {
                    col = (Color)SelectedItem;
                }

                return col;
            }

            set
            {
                foreach (object item in Items)
                {
                    Color col;
                    if (item is KnownColor)
                    {
                        col = Color.FromKnownColor((KnownColor)item);
                        if (col == value)
                        {
                            SelectedItem = item;
                            return;
                        }
                    }

                    if (item is Color)
                    {
                        col = (Color)item;
                        if (col == value)
                        {
                            SelectedItem = item;
                            return;
                        }
                    }
                }

                Items.Add(value);
                SelectedIndex = Items.Count - 1;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Disposes stuff
        /// </summary>
        /// <param name="disposing">Indicats whether managed resources should be disposed.</param>
        protected override void Dispose(bool disposing)
        {
            _colors = null;
            _backBrush?.Dispose();
            _foreBrush?.Dispose();

            base.Dispose(disposing);
        }

        /// <summary>
        /// Actually handles the drawing of a single item
        /// </summary>
        /// <param name="e">DrawItemEventArgs</param>
        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if (IsDisposed) return;
            if (Visible == false) return;
            if (e.Index == -1) return; // no items to draw

            Color col = Color.Empty;
            string name = "Empty";
            if (Items[e.Index] is KnownColor)
            {
                // the color to draw
                col = Color.FromKnownColor((KnownColor)Items[e.Index]);
                name = col.Name;
            }

            if (Items[e.Index] is Color)
            {
                col = (Color)Items[e.Index];
                name = "R: " + col.R + " G: " + col.G + " B: " + col.B;
            }

            // by erasing and drawing off-camera, we avoid flicker
            Bitmap bmp = new Bitmap(e.Bounds.Width, e.Bounds.Height);
            Graphics backBuffer = Graphics.FromImage(bmp);

            // erase by drawing the background
            if (_backBrush == null) _backBrush = new SolidBrush(e.BackColor);
            if (_foreBrush == null) _foreBrush = new SolidBrush(e.ForeColor);
            if (((SolidBrush)_backBrush).Color != e.BackColor)
            {
                _backBrush = new SolidBrush(e.BackColor);
            }

            if (((SolidBrush)_foreBrush).Color != e.ForeColor)
            {
                _foreBrush = new SolidBrush(e.ForeColor);
            }

            backBuffer.FillRectangle(_backBrush, new Rectangle(0, 0, e.Bounds.Width, e.Bounds.Height));

            // draw the color box
            Rectangle colorBox = new Rectangle(1, 1, 25, e.Bounds.Height - 2);
            Brush colBrush = new SolidBrush(col);
            backBuffer.FillRectangle(colBrush, colorBox);
            colBrush.Dispose();
            backBuffer.DrawRectangle(_boxPen, colorBox);

            // label the color
            backBuffer.DrawString(name, Font, _foreBrush, 32F, 1);

            // now that we have drawn the item, add it to the front
            e.Graphics.DrawImage(bmp, e.Bounds.X, e.Bounds.Y);
        }

        /// <summary>
        /// Prevents flicker .. or possibly does nothing.. I'm not sure.
        /// </summary>
        /// <param name="pevent">PaintEventArgs</param>
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            // base.OnPaintBackground(pevent);
        }

        #endregion
    }
}