using BPA.UIControl.Commons.KnownBoxes;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Threading;

namespace BPA.UIControl
{
    /// <summary>
    /// 时间选择器
    /// </summary>
    [TemplatePart(Name = TextBoxPartName, Type = typeof(TextBox))]
    [TemplatePart(Name = PopupPartName, Type = typeof(Popup))]
    [TemplatePart(Name = ButtonPartName, Type = typeof(Button))]
    [TemplatePart(Name = ClockPartName, Type = typeof(Clock))]
    public class TimePicker : Control
    {
        /// <summary>
        /// 文本框名称
        /// </summary>
        public const string TextBoxPartName = "PART_TextBox";

        /// <summary>
        /// 弹窗名称
        /// </summary>
        public const string PopupPartName = "PART_Popup";

        /// <summary>
        /// 选择按钮名称
        /// </summary>
        public const string ButtonPartName = "PART_Button";

        /// <summary>
        /// 时钟名称
        /// </summary>
        public const string ClockPartName = "PART_Clock";

        private TextBox _textBox;
        private Popup _popup;
        private Clock _clock;

        /// <summary>
        /// Initializes a new instance of the <see cref="TimePicker"/> class.
        /// </summary>
        static TimePicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(typeof(TimePicker)));
        }

        /// <inheritdoc/>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            Clock clock = GetTemplateChild(ClockPartName) as Clock;
            clock.SelectedTimeChanged += Clock_SelectedTimeChanged;
            _clock = clock;

            TextBox textBox = GetTemplateChild(TextBoxPartName) as TextBox;
            var binding1 = new Binding("Text");
            binding1.Source = this;
            binding1.Mode = BindingMode.TwoWay;
            textBox.SetBinding(TextBox.TextProperty, binding1);
            _textBox = textBox;

            Popup popup = GetTemplateChild(PopupPartName) as Popup;
            popup.Opened += Popup_Opened;
            popup.Closed += Popup_Closed;
            if (IsDropDownOpen)
            {
                _popup.IsOpen = true;
            }
            _popup = popup;

            Button button = GetTemplateChild(ButtonPartName) as Button;
            button.Click += Button_Click;
        }

        #region 路由事件

        /// <summary>
        /// 选择时间改变事件
        /// </summary>
        public static readonly RoutedEvent SelectedTimeChangedEvent =
            EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime?>), typeof(TimePicker));

        /// <summary>
        /// 选择时间改变事件
        /// </summary>
        public event RoutedPropertyChangedEventHandler<DateTime?> SelectedTimeChanged
        {
            add { AddHandler(SelectedTimeChangedEvent, value); }
            remove { RemoveHandler(SelectedTimeChangedEvent, value); }
        }

        #endregion 路由事件

        #region 依赖属性

        /// <summary>
        /// 选中时间
        /// </summary>
        public static readonly DependencyProperty SeletedTimeProperty = DependencyProperty.Register(
            "SelectedTime", typeof(DateTime?), typeof(TimePicker), new FrameworkPropertyMetadata(default(DateTime?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeChanged));

        /// <summary>
        /// 选中时间
        /// </summary>
        public DateTime? SelectedTime
        {
            get { return (DateTime?)GetValue(SeletedTimeProperty); }
            set { SetValue(SeletedTimeProperty, value); }
        }

        /// <summary>
        /// 弹窗打开
        /// </summary>
        public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
            "IsDropDownOpen", typeof(bool), typeof(TimePicker), new PropertyMetadata(BooleanBoxes.FalseBox, OnIsDropDownOpenChanged));

        /// <summary>
        /// 弹窗打开
        /// </summary>
        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, BooleanBoxes.Box(value)); }
        }

        private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimePicker tp = d as TimePicker;
            bool newValue = (bool)e.NewValue;
            if (tp._popup != null && tp._popup.IsOpen != newValue)
            {
                tp._popup.IsOpen = newValue;
                if (newValue)
                {
                    tp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate ()
                    {
                        tp._clock.Focus();
                    });
                }
            }
        }

        /// <summary>
        /// 显示文本
        /// </summary>
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text", typeof(string), typeof(TimePicker), new PropertyMetadata(null, OnTextChanged));

        /// <summary>
        /// 显示文本
        /// </summary>
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimePicker timePicker = (TimePicker)d;
            var oldDateTime = timePicker.SelectedTime?.ToString(timePicker.SelectedTimeFormat);
            if (oldDateTime != timePicker.Text)
            {
                if (DateTime.TryParse(timePicker.Text, out DateTime result))
                {
                    timePicker.SelectedTime = result;
                }
                else if (string.IsNullOrEmpty(timePicker.Text))
                {
                    timePicker.SelectedTime = null;
                }
                else
                {
                    timePicker.Text = oldDateTime;
                }
            }
        }

        /// <summary>
        /// 选择时间格式化
        /// </summary>
        public static readonly DependencyProperty SelectedTimeFormatProperty = DependencyProperty.Register(
            "SelectedTimeFormat", typeof(string), typeof(TimePicker), new PropertyMetadata(default(string), OnSelectedTimeFormatChanged));

        private static void OnSelectedTimeFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is TimePicker timePicker)
            {
                timePicker.Text = timePicker.SelectedTime?.ToString(timePicker.SelectedTimeFormat);
            }
        }

        /// <summary>
        /// 选择时间格式化
        /// </summary>
        public string SelectedTimeFormat
        {
            get { return (string)GetValue(SelectedTimeFormatProperty); }
            set { SetValue(SelectedTimeFormatProperty, value); }
        }

        #endregion 依赖属性

        #region 方法

        private void TextBox_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            TextBox textBox = sender as TextBox;
            textBox.Focus();
        }

        /// <summary>
        /// 选择时间改变
        /// </summary>
        private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimePicker timePicker = (TimePicker)d;

            timePicker.Text = timePicker.SelectedTime?.ToString(timePicker.SelectedTimeFormat);

            RoutedPropertyChangedEventArgs<DateTime?> args = new RoutedPropertyChangedEventArgs<DateTime?>((DateTime?)e.OldValue, (DateTime?)e.NewValue);
            args.RoutedEvent = TimePicker.SelectedTimeChangedEvent;
            timePicker.RaiseEvent(args);
        }

        /// <summary>
        /// 时钟的时间改变
        /// </summary>
        private void Clock_SelectedTimeChanged(object sender, RoutedPropertyChangedEventArgs<DateTime?> e)
        {
            if (SelectedTime != e.NewValue)
            {
                SelectedTime = e.NewValue;
            }

            IsDropDownOpen = false;

            _textBox.Focus();
            _textBox.SelectAll();
        }

        /// <summary>
        /// 点击时间按钮
        /// </summary>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Text = _textBox.Text;
            IsDropDownOpen = !IsDropDownOpen;
        }

        private void Popup_Opened(object sender, EventArgs e)
        {
            _clock.SelectedTime = SelectedTime;
        }

        /// <summary>
        /// popup 关闭
        /// </summary>
        private void Popup_Closed(object sender, EventArgs e)
        {
            if (this._clock.IsKeyboardFocusWithin)
            {
                this._textBox.Focus();
            }
            else
            {
                this.IsDropDownOpen = false;
            }
        }

        #endregion 方法
    }
}