using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace Vortex.Tools.FormControls {

    public partial class RangeControl : UserControl {
        //list of captures
        enum CapturedElement {
            None,
            ToValue,
            FromValue
        }

        public delegate void ValueChangedEventHandler(float fromValue, float toValue);
        public event ValueChangedEventHandler Changed;

        const int RIGHT_MARGIN = 30;
        const int BAR_LEFT_RIGHT_MARGIN = 4;
        const int SEGMENT_HEIGHT = 12;
        const int BUTTON_SIZE = SEGMENT_HEIGHT;

        bool m_Locked = true;
        bool m_AlwaysLock = false;
        bool m_ConfigEnabled = true;
        float m_MinValue = 0.0f;
        float m_MaxValue = 1.0f;
        float m_From = 0.5f;
        float m_To = 0.5f;

        CapturedElement m_CapturedMouse = CapturedElement.None;

        [Browsable(true)]
        public bool ConfigEnabled {
            get { return m_ConfigEnabled; }
            set { m_ConfigEnabled = value; ConfigPictureBox.Visible = value; }
        }

        [Browsable(true)]
        public string Label {
            get;
            set;
        }

        [Browsable(true)]
        public Color BarBackColor {
            get;
            set;
        }

        [Browsable(true)]
        public Color BarFillColor {
            get;
            set;
        }

        [Browsable(true)]
        public Color MarkerFillColor {
            get;
            set;
        }

        [Browsable(true)]
        public bool AlwaysLock {
            get { return m_AlwaysLock; }
            set { m_AlwaysLock = value; LockEnabled = true; LockPictureBox.Visible = !AlwaysLock; }
        }

        [Browsable(true)]
        public bool LockEnabled {
            get { return m_Locked; }
            set {
                m_Locked = value;
                if (m_Locked) {
                    float newFromTo = (m_From + m_To) * 0.5f;
                    m_From = m_To = newFromTo;
                }
                LockPictureBox.BackColor = m_Locked ? SystemColors.ControlDark : this.BackColor;
                RaiseValueChangedEvent();
                this.Refresh();
            }
        }

        [Browsable(true)]
        public float MinValue {
            get { return m_MinValue; }
            set {
                m_MinValue = Math.Min(value, m_MaxValue);
                if (m_From < m_MinValue) {
                    m_From = m_MinValue;
                }
                if (m_To < m_MinValue) {
                    m_To = m_MinValue;
                }
                RaiseValueChangedEvent();
                this.Refresh();
            }
        }

        [Browsable(true)]
        public float MaxValue {
            get { return m_MaxValue; }
            set {
                m_MaxValue = Math.Max(value, m_MinValue);
                if (m_From > m_MaxValue) {
                    m_From = m_MaxValue;
                }
                if (m_To > m_MaxValue) {
                    m_To = m_MaxValue;
                }
                RaiseValueChangedEvent();
                this.Refresh();
            }
        }

        [Browsable(true)]
        public float FromValue {
            get { return m_From; }
            set {
                m_From = Math.Min(Math.Max(value, m_MinValue), m_MaxValue);
                if (m_Locked) {
                    m_To = m_From;
                }
                RaiseValueChangedEvent();
                this.Refresh();
            }
        }

        [Browsable(true)]
        public float ToValue {
            get { return m_To; }
            set {
                m_To = Math.Min(Math.Max(value, m_MinValue), m_MaxValue);
                if (m_Locked) {
                    m_From = m_To;
                }
                RaiseValueChangedEvent();
                this.Refresh();
            }
        }

        private void RaiseValueChangedEvent() {
            if (Changed != null) {
                Changed(FromValue, ToValue);
            }
        }

        public RangeControl() {
            InitializeComponent();
            BarBackColor = Color.White;
            BarFillColor = Color.Orange;
            MarkerFillColor = Color.LightBlue;
        }

        public Color MainColor {
            get {
                return Enabled ? SystemColors.WindowText : SystemColors.GrayText;
            }
        }

        private void RangeControl_Paint(object sender, PaintEventArgs e) {
            e.Graphics.Clear(this.BackColor);
            //lets do some rendering preparations
            Rectangle bar = BarRect;

            //draw markers:
            float fromRatio = m_MaxValue != m_MinValue ? (m_From - m_MinValue) / (m_MaxValue - m_MinValue) : 0;
            float toRatio = m_MaxValue != m_MinValue ? (m_To - m_MinValue) / (m_MaxValue - m_MinValue) : 0;

            int fromPos = (int)(fromRatio * (float)bar.Width) + BAR_LEFT_RIGHT_MARGIN,
                toPos = (int)(toRatio * (float)bar.Width) + BAR_LEFT_RIGHT_MARGIN;

            //draw bar
            e.Graphics.FillRectangle(new SolidBrush(BarBackColor), bar);
            if (toPos == fromPos) {
                e.Graphics.DrawLine(new Pen(BarFillColor), toPos, bar.Top, toPos, bar.Bottom);
            }
            else {
                e.Graphics.FillRectangle(new SolidBrush(BarFillColor), new Rectangle(Math.Min(toPos, fromPos), bar.Y, Math.Max(toPos, fromPos) - Math.Min(toPos, fromPos), bar.Height));
            }
            e.Graphics.DrawRectangle(new Pen(MainColor), bar);

            //draw top marker
            int markerTop = SEGMENT_HEIGHT + 1;
            Point[] topMarkerPoints = new Point[] {
                new Point(toPos - 4, markerTop),
                new Point(toPos + 4, markerTop),
                new Point(toPos, markerTop + 10)
            };

            e.Graphics.FillPolygon(new SolidBrush(MarkerFillColor), topMarkerPoints);
            e.Graphics.DrawPolygon(new Pen(MainColor), topMarkerPoints);

            if (!AlwaysLock) {
                //draw bottom marker
                markerTop = SEGMENT_HEIGHT * 3;
                Point[] bottomMarkerPoints = new Point[] {
                    new Point(fromPos - 4, markerTop + 10),
                    new Point(fromPos + 4, markerTop + 10),
                    new Point(fromPos, markerTop)
                };

                e.Graphics.FillPolygon(new SolidBrush(MarkerFillColor), bottomMarkerPoints);
                e.Graphics.DrawPolygon(new Pen(MainColor), bottomMarkerPoints);

                //draw lock bar as well
                DrawLock(e.Graphics);
            }

            Brush textBrush = new SolidBrush(MainColor);
            //draw caption
            if (this.Label != null) {
                e.Graphics.DrawString(this.Label, this.Font, textBrush, new PointF(BAR_LEFT_RIGHT_MARGIN / 2, 0));
            }

            //draw values
            string toValue = string.Format("{0:F2}", ToValue), fromValue = string.Format("{0:F2}", FromValue);
            SizeF toValueSize = e.Graphics.MeasureString(toValue, this.Font);
            SizeF fromValueSize = e.Graphics.MeasureString(fromValue, this.Font);

            e.Graphics.DrawString(toValue, this.Font, textBrush, new PointF(this.Width - BAR_LEFT_RIGHT_MARGIN / 2 - toValueSize.Width, SEGMENT_HEIGHT));
            if (!AlwaysLock) {
                e.Graphics.DrawString(fromValue, this.Font, textBrush, new PointF(this.Width - BAR_LEFT_RIGHT_MARGIN / 2 - fromValueSize.Width, SEGMENT_HEIGHT * 3));
            }
        }

        private void DrawLock(Graphics graphics) {
            if (LockEnabled) {
                graphics.FillRectangle(new SolidBrush(SystemColors.ButtonShadow), LockRect);
                graphics.DrawRectangle(new Pen(SystemColors.WindowFrame), LockRect);
            }
            //draw lock
        }

        Rectangle BarRect {
            get { return new Rectangle(BAR_LEFT_RIGHT_MARGIN, SEGMENT_HEIGHT * 2, this.Width - BAR_LEFT_RIGHT_MARGIN * 2, SEGMENT_HEIGHT); }
        }

        Rectangle LockRect {
            get { return new Rectangle(this.Width - RIGHT_MARGIN, 0, BUTTON_SIZE, BUTTON_SIZE);  }
        }

        Rectangle ConfigRect {
            get { return new Rectangle(this.Width - RIGHT_MARGIN + LockPictureBox.Width + 2, 0, BUTTON_SIZE, BUTTON_SIZE); }
        }

        private void RangeControl_Load(object sender, EventArgs e) {
            UpdateInternalLayout();
        }

        private void UpdateInternalLayout() {
            LockPictureBox.Location = LockRect.Location;
            ConfigPictureBox.Location = ConfigRect.Location;
        }

        private void RangeControl_MouseDown(object sender, MouseEventArgs e) {
            if (e.Y > (SEGMENT_HEIGHT - 2) && e.Button == MouseButtons.Left) {
                m_CapturedMouse = e.Y < (BarRect.Y + BarRect.Height / 2) ? CapturedElement.ToValue : CapturedElement.FromValue;
                UpdatePointers(e.X, e.Y);
            }
        }

        private void UpdatePointers(int x, int y) {
            float xFactor = Math.Max(Math.Min((float)(x - BarRect.Left) / (float)(BarRect.Width), 1), 0);
            float value = (MaxValue - MinValue) * xFactor + MinValue;
            if (m_CapturedMouse == CapturedElement.ToValue) {
                ToValue = value;
            } else {
                FromValue = value;
            }
        }

        private void RangeControl_Resize(object sender, EventArgs e) {
            UpdateInternalLayout();
        }

        private void RangeControl_MouseMove(object sender, MouseEventArgs e) {
            if (m_CapturedMouse != CapturedElement.None) {
                UpdatePointers(e.X, e.Y);
            }
        }

        private void LockPictureBox_Click(object sender, EventArgs e) {
            LockEnabled = !LockEnabled;
        }

        private void RangeControl_MouseUp(object sender, MouseEventArgs e) {
            m_CapturedMouse = CapturedElement.None;
        }

        private void ConfigPictureBox_MouseDown(object sender, MouseEventArgs e) {
            RangeControlSetMinMaxForm dialog = new RangeControlSetMinMaxForm();
            dialog.MinValue = MinValue;
            dialog.MaxValue = MaxValue;
            if (dialog.ShowDialog() == DialogResult.OK) {
                MinValue = dialog.MinValue;
                MaxValue = dialog.MaxValue;
            }
        }

        private void RangeControl_EnabledChanged(object sender, EventArgs e) {
            LockPictureBox.Visible = this.Enabled && !this.AlwaysLock;
            ConfigPictureBox.Visible = this.Enabled && ConfigEnabled;
        }

        public float GetRangedValueFromRatio(float ratio) {
            return m_From + (m_To - m_From) * ratio;
        }

        public void Syncronize(RangeControl otherControl) {
            LockEnabled = otherControl.LockEnabled;
            MaxValue = otherControl.MaxValue;
            MinValue = otherControl.MinValue;
            FromValue = otherControl.FromValue;
            ToValue = otherControl.ToValue;
        }

        public void AutoSetValues(float from, float to) {
            m_MinValue = Math.Min(Math.Min(from, to), m_MinValue);
            m_MaxValue = Math.Max(Math.Max(from, to), m_MaxValue);
            //check are these values different - need to remove lock
            if (from != to) {
                m_Locked = false;
                m_AlwaysLock = false;
            }
            //
            FromValue = from;
            ToValue = to;
        }
    }
}
