using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace Elwig.Controls {
    public class IntegerUpDown : TextBox {

        public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(nameof(Minimum), typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
        public int? Minimum {
            get => (int?)GetValue(MinimumProperty);
            set => SetValue(MinimumProperty, value);
        }

        public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(nameof(Maximum), typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
        public int? Maximum {
            get => (int?)GetValue(MaximumProperty);
            set => SetValue(MaximumProperty, value);
        }

        public int? Value {
            get => int.TryParse(Text, out var res) ? res : null;
            set => Text = $"{value}";
        }

        static IntegerUpDown() {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(IntegerUpDown), new FrameworkPropertyMetadata(typeof(IntegerUpDown)));
        }

        public IntegerUpDown() {
            TextChanged += IntegerUpDown_TextChanged;
            LostFocus += IntegerUpDown_LostFocus;
            KeyUp += IntegerUpDown_KeyUp;
        }

        public override void OnApplyTemplate() {
            var incButton = GetTemplateChild("IncrementButton") as RepeatButton;
            var decButton = GetTemplateChild("DecrementButton") as RepeatButton;
            incButton!.Click += IncrementButton_Click;
            decButton!.Click += DecrementButton_Click;
            base.OnApplyTemplate();
        }

        private void IntegerUpDown_TextChanged(object sender, TextChangedEventArgs evt) {
            var idx = CaretIndex;
            Text = new string(Text.Where(char.IsAsciiDigit).Take(4).ToArray());
            CaretIndex = idx;
            evt.Handled = !(Value >= Minimum && Value <= Maximum);
            if (idx >= 4) {
                if (Value < Minimum) {
                    Value = Minimum;
                } else if (Value > Maximum) {
                    Value = Maximum;
                }
                CaretIndex = 4;
            }
        }

        private void IntegerUpDown_LostFocus(object sender, RoutedEventArgs evt) {
            if (Value < Minimum) {
                Value = Minimum;
            } else if (Value > Maximum) {
                Value = Maximum;
            }
        }

        private void IncrementButton_Click(object sender, RoutedEventArgs evt) {
            Value = Math.Min((Value ?? 0) + 1, Maximum ?? int.MaxValue);
        }

        private void DecrementButton_Click(object sender, RoutedEventArgs evt) {
            Value = Math.Max((Value ?? 0) - 1, Minimum ?? int.MinValue);
        }

        private void IntegerUpDown_KeyUp(object sender, KeyEventArgs evt) {
            switch (evt.Key) {
                case Key.Up:
                case Key.Add:
                case Key.OemPlus:
                    Value = Math.Min((Value ?? 0) + 1, Maximum ?? int.MaxValue);
                    evt.Handled = true;
                    CaretIndex = 4;
                    break;
                case Key.Down:
                case Key.Subtract:
                case Key.OemMinus:
                    Value = Math.Max((Value ?? 0) - 1, Minimum ?? int.MinValue);
                    evt.Handled = true;
                    CaretIndex = 4;
                    break;
                case Key.PageUp:
                    Value = Math.Min((Value ?? 0) + 10, Maximum ?? int.MaxValue);
                    evt.Handled = true;
                    CaretIndex = 4;
                    break;
                case Key.PageDown:
                    Value = Math.Max((Value ?? 0) - 10, Minimum ?? int.MinValue);
                    evt.Handled = true;
                    CaretIndex = 4;
                    break;
            }
        }
    }
}
