using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using VIZ.Framework.Core;

namespace VIZ.Framework.Common
{
    /// <summary>
    /// 数字输入框
    /// </summary>
    [TemplatePart(Name = nameof(PART_Up), Type = typeof(RepeatButton))]
    [TemplatePart(Name = nameof(PART_Down), Type = typeof(RepeatButton))]
    public class NumberBox : TextBox
    {
        static NumberBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NumberBox), new FrameworkPropertyMetadata(typeof(NumberBox)));
        }

        public NumberBox()
        {
            this.SetCurrentValue(NumberBox.TextProperty, this.MinValue.ToString());
        }

        #region MinValue -- 最小值

        /// <summary>
        /// 最小值
        /// </summary>
        public double MinValue
        {
            get { return (double)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for MinValue.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty MinValueProperty =
            DependencyProperty.Register("MinValue", typeof(double), typeof(NumberBox), new PropertyMetadata(0d));

        #endregion

        #region MaxValue -- 最大值

        /// <summary>
        /// 最大值
        /// </summary>
        public double MaxValue
        {
            get { return (double)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for MaxValue.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty MaxValueProperty =
            DependencyProperty.Register("MaxValue", typeof(double), typeof(NumberBox), new PropertyMetadata(100d));

        #endregion

        #region Value -- 当前值

        /// <summary>
        /// 当前值
        /// </summary>
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(NumberBox), new PropertyMetadata(0d, new PropertyChangedCallback((s, e) =>
            {
                (s as NumberBox).Text = ((double)e.NewValue).ToString();
            })));

        #endregion

        #region Interval -- 间隔值

        /// <summary>
        /// 间隔值
        /// </summary>
        public double Interval
        {
            get { return (double)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for Interval.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IntervalProperty =
            DependencyProperty.Register("Interval", typeof(double), typeof(NumberBox), new PropertyMetadata(1d, new PropertyChangedCallback((s, e) =>
            {
                // 重置正则表达式
                (s as NumberBox).ResetRegex();
            })));

        #endregion

        #region IsShowUpAndDownButton -- 是否显示上下按钮

        /// <summary>
        /// 是否显示上下按钮
        /// </summary>
        public bool IsShowUpAndDownButton
        {
            get { return (bool)GetValue(IsShowUpAndDownButtonProperty); }
            set { SetValue(IsShowUpAndDownButtonProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for IsShowUpAndDownButton.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IsShowUpAndDownButtonProperty =
            DependencyProperty.Register("IsShowUpAndDownButton", typeof(bool), typeof(NumberBox), new PropertyMetadata(true));

        #endregion

        /// <summary>
        /// 向上按钮
        /// </summary>
        private RepeatButton PART_Up;

        /// <summary>
        /// 向下按钮
        /// </summary>
        private RepeatButton PART_Down;

        /// <summary>
        /// 之前的文本值
        /// </summary>
        private string old_text;

        /// <summary>
        /// 数字正则表达式
        /// </summary>
        private Regex regex;

        /// <summary>
        /// 应用模板
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.PART_Up = this.Template.FindName(nameof(PART_Up), this) as RepeatButton;
            this.PART_Down = this.Template.FindName(nameof(PART_Down), this) as RepeatButton;

            if (this.PART_Up != null)
            {
                this.PART_Up.Click -= PART_Up_Click;
                this.PART_Up.Click += PART_Up_Click;
            }

            if (this.PART_Down != null)
            {
                this.PART_Down.Click -= PART_Down_Click;
                this.PART_Down.Click += PART_Down_Click;
            }
        }

        /// <summary>
        /// 失去焦点
        /// </summary>
        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);

            if (!double.TryParse(this.Text, out double value))
            {
                value = this.MinValue;
            }

            value = MathHelper.Clip(this.MinValue, this.MaxValue, value);
            value = Math.Round(value, MathHelper.GetDigitsPrecision(this.Interval));

            this.Text = value.ToString();
            this.Value = value;
        }

        /// <summary>
        /// 文本改变之后
        /// </summary>
        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            base.OnTextChanged(e);

            if (this.regex == null)
            {
                this.ResetRegex();
            }

            if (!this.regex.IsMatch(this.Text))
            {
                this.Text = this.old_text;
            }
            else
            {
                this.old_text = this.Text;
            }
        }

        /// <summary>
        /// 向下
        /// </summary>
        private void PART_Down_Click(object sender, RoutedEventArgs e)
        {
            double value = this.Value - this.Interval;
            value = Math.Round(value, MathHelper.GetDigitsPrecision(this.Interval));

            if (value < this.MinValue || value > this.MaxValue)
                return;

            this.Value = value;
        }

        /// <summary>
        /// 向上
        /// </summary>
        private void PART_Up_Click(object sender, RoutedEventArgs e)
        {
            double value = this.Value + this.Interval;
            value = Math.Round(value, MathHelper.GetDigitsPrecision(this.Interval));

            if (value < this.MinValue || value > this.MaxValue)
                return;

            this.Value = value;
        }

        /// <summary>
        /// 重置正则表达式
        /// </summary>
        private void ResetRegex()
        {
            double precision = MathHelper.GetDigitsPrecision(this.Interval);
            StringBuilder sb = new StringBuilder();
            sb.Append("^");
            if (this.MinValue < 0)
            {
                if (precision > 0)
                {
                    sb.Append("[-.]*");
                }
                else
                {
                    sb.Append("[-]*");
                }
            }
            else
            {
                if (precision > 0)
                {
                    sb.Append("[.]*");
                }
            }
            sb.Append("[0-9]*");
            if (precision > 0)
            {
                sb.Append("[.]*[0-9]{0," + precision + "}");
            }

            sb.Append("$");

            regex = new Regex(sb.ToString());
        }
    }
}
