using System;
using System.Drawing;
using System.Windows.Forms;
using NotepadEnhanced.Helpers;

namespace NotepadEnhanced.GUI.Controls
{
    public partial class ScrollingLabel : UserControl
    {
        private Point _lastPos;
        private int _lineHeight;

        public ScrollingLabel()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
            SetFontHeight();
        }

        private void pnlInner_SizeChanged(object sender, EventArgs e)
        {
            label.MaximumSize = new Size(pnlInner.ClientSize.Width, 0);
            CheckBoundarys();
        }

        private void label_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                label.Top += e.Y - _lastPos.Y;
            }
        }

        private void label_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _lastPos = e.Location;
            }
        }

        private void label_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                CheckBoundarys();
            }
        }

        /// <summary>
        /// Checks to see if the label has been scrolled to the point where there is empty space in
        /// its parent. If it is, then use a loop to quickly animate it back.
        /// </summary>
        private void CheckBoundarys()
        {
            if (label.Height < this.ClientSize.Height)
            {
                label.Top = 0;
            }
            else if (label.Location.Y > 0)
            {
                label.Top = 0;
            }
            else if (label.Location.Y < (label.Height * -1) + pnlInner.ClientSize.Height)
            {
                label.Top = (label.Height * -1) + pnlInner.ClientSize.Height;
            }
        }

        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            base.OnPreviewKeyDown(e);

            switch (e.KeyCode)
            {
                // Page down to scroll to end of text
                case Keys.PageDown: label.Top = (label.Height * -1) + pnlInner.ClientSize.Height; break;
                // Page up to scroll to start of text
                case Keys.PageUp: label.Top = 0; break;
                // Down to scroll down one line
                case Keys.Down: label.Top -= _lineHeight; break;
                // Up to scroll up one line
                case Keys.Up: label.Top += _lineHeight; break;
                // Left to scroll back a "page" of lines
                case Keys.Left: label.Top += pnlInner.ClientSize.Height - _lineHeight; break;
                // Right to scroll forward a "page" of lines
                case Keys.Right: label.Top -= pnlInner.ClientSize.Height - _lineHeight; break;
                // Control + Plus to increase font size
                case (Keys.Oemplus | Keys.ControlKey): label.Font = label.Font.SetSize(1); break;
                // Control + Minus to decrease font size
                case (Keys.OemMinus | Keys.ControlKey): label.Font = label.Font.SetSize(-1); break;
                // Numpad 4 to decrease side padding
                case (Keys.NumPad4): LeftRightPadding -= 2; break;
                // Numpad 6 to increase side padding
                case (Keys.NumPad6): LeftRightPadding += 2; break;
                // Numpad 2 to decrease vertical padding
                case (Keys.NumPad2): TopBottomPadding -= 2; break;
                // Numpad 8 to increase vertical padding
                case (Keys.NumPad8): TopBottomPadding += 2; break;
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            label.Top += e.Delta;
            CheckBoundarys();
        }

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

            switch (e.KeyCode)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                    CheckBoundarys();
                    break;
            }
        }

        private void label_FontChanged(object sender, EventArgs e)
        {
            SetFontHeight();
        }

        private void SetFontHeight()
        {
            using (Graphics graphics = this.CreateGraphics())
            {
                _lineHeight = (int)(graphics.MeasureString("ABC123", label.Font).Height + 0.5);
            }
        }

        public void SetLabelText(string text)
        {
            label.Text = text;
        }

        #region Properties
        public override Font Font
        {
            get { return base.Font; }
            set { label.Font = value; }
        }

        public override Color BackColor
        {
            get
            {
                return base.BackColor;
            }
            set
            {
                base.BackColor = value;
                pnlInner.BackColor = value;
                label.BackColor = value;
            }
        }

        public override Color ForeColor
        {
            get { return base.ForeColor; }
            set { label.ForeColor = value; }
        }

        public int LeftRightPadding
        {
            get { return this.Padding.Right; }
            set
            {
                this.Padding = new Padding(value, this.Padding.Top, value, this.Padding.Bottom);
            }
        }

        public int TopBottomPadding
        {
            get { return this.Padding.Top; }
            set
            {
                this.Padding = new Padding(this.Padding.Left, value, this.Padding.Right, value);
            }
        }
        #endregion
    }
}
