using BPA.UIControl.Commons.KnownBoxes;
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace BPA.UIControl
{
    /// <summary>
    /// 汉堡包菜单
    /// </summary>
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(HamburgerMenuItem))]
    [StyleTypedProperty(Property = "OptionsItemContainerStyle", StyleTargetType = typeof(HamburgerMenuOptionsItem))]
    [TemplatePart(Name = HamburgerButtonPartName, Type = typeof(Button))]
    [TemplatePart(Name = OptionsItemsControlPartName, Type = typeof(MenuBase))]
    [TemplatePart(Name = ContentTransitionPartName, Type = typeof(Transition))]
    public class HamburgerMenu : TabControl
    {
        /// <summary>
        /// 汉堡包按钮
        /// </summary>
        public const string HamburgerButtonPartName = "PART_HamburgerButton";

        /// <summary>
        /// 选项集合控件
        /// </summary>
        public const string OptionsItemsControlPartName = "PART_OptionsItemsControl";

        /// <summary>
        /// 内容转换
        /// </summary>
        public const string ContentTransitionPartName = "PART_ContentTransition";

        #region fields

        private CornerRadius _cornerRadius;
        private MenuBase optionsMenu;
        private Transition contentTransition;

        #endregion fields

        #region properties

        /// <summary>
        /// 圆角半径
        /// </summary>
        public static readonly DependencyProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(typeof(HamburgerMenu), new FrameworkPropertyMetadata(default(CornerRadius), FrameworkPropertyMetadataOptions.AffectsMeasure));

        /// <summary>
        /// 圆角半径
        /// </summary>
        public CornerRadius CornerRadius
        {
            get => _cornerRadius;
            set => SetValue(CornerRadiusProperty, value);
        }

        /// <summary>
        /// 是否展开菜单
        /// </summary>
        public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(
            "IsExpanded", typeof(bool), typeof(HamburgerMenu), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        /// <summary>
        /// 是否展开菜单
        /// </summary>
        public bool IsExpanded
        {
            get { return (bool)GetValue(IsExpandedProperty); }
            set { SetValue(IsExpandedProperty, BooleanBoxes.Box(value)); }
        }

        /// <summary>
        /// 标题
        /// </summary>
        public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
            "Header", typeof(string), typeof(HamburgerMenu), new PropertyMetadata(default(string)));

        /// <summary>
        /// 标题
        /// </summary>
        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        /// <summary>
        /// 面板头部内容
        /// </summary>
        public static readonly DependencyProperty PaneHeaderProperty = DependencyProperty.Register(
            "PaneHeader", typeof(object), typeof(HamburgerMenu), new PropertyMetadata(default(object)));

        /// <summary>
        /// 面板头部内容
        /// </summary>
        public object PaneHeader
        {
            get { return (object)GetValue(PaneHeaderProperty); }
            set { SetValue(PaneHeaderProperty, value); }
        }

        /// <summary>
        /// 面板底部内容
        /// </summary>
        public static readonly DependencyProperty PaneFooterProperty = DependencyProperty.Register(
            "PaneFooter", typeof(object), typeof(HamburgerMenu), new PropertyMetadata(default(object)));

        /// <summary>
        /// 面板底部内容
        /// </summary>
        public object PaneFooter
        {
            get { return (object)GetValue(PaneFooterProperty); }
            set { SetValue(PaneFooterProperty, value); }
        }

        /// <summary>
        /// 折叠宽度
        /// </summary>
        public static readonly DependencyProperty CollapsedWidthProperty = DependencyProperty.Register(
            "CollapsedWidth", typeof(double), typeof(HamburgerMenu), new PropertyMetadata(default(double)));

        /// <summary>
        /// 折叠宽度
        /// </summary>
        public double CollapsedWidth
        {
            get { return (double)GetValue(CollapsedWidthProperty); }
            set { SetValue(CollapsedWidthProperty, value); }
        }

        /// <summary>
        /// 展开宽度
        /// </summary>
        public static readonly DependencyProperty ExpandedWidthProperty = DependencyProperty.Register(
            "ExpandedWidth", typeof(double), typeof(HamburgerMenu), new PropertyMetadata(default(double)));

        /// <summary>
        /// 展开宽度
        /// </summary>
        public double ExpandedWidth
        {
            get { return (double)GetValue(ExpandedWidthProperty); }
            set { SetValue(ExpandedWidthProperty, value); }
        }

        /// <summary>
        /// 是否显示汉堡包按钮
        /// </summary>
        public static readonly DependencyProperty IsShowHamburgerButtonProperty = DependencyProperty.Register(
            "IsShowHamburgerButton", typeof(bool), typeof(HamburgerMenu), new PropertyMetadata(BooleanBoxes.TrueBox));

        /// <summary>
        /// 是否显示汉堡包按钮
        /// </summary>
        public bool IsShowHamburgerButton
        {
            get { return (bool)GetValue(IsShowHamburgerButtonProperty); }
            set { SetValue(IsShowHamburgerButtonProperty, BooleanBoxes.Box(value)); }
        }

        /// <summary>
        /// 是否显示小竖条
        /// </summary>
        public static readonly DependencyProperty IsShowLittleBarProperty = DependencyProperty.Register(
            "IsShowLittleBar", typeof(bool), typeof(HamburgerMenu), new PropertyMetadata(BooleanBoxes.TrueBox));

        /// <summary>
        /// 是否显示小竖条
        /// </summary>
        public bool IsShowLittleBar
        {
            get { return (bool)GetValue(IsShowLittleBarProperty); }
            set { SetValue(IsShowLittleBarProperty, BooleanBoxes.Box(value)); }
        }

        /// <summary>
        /// 转换类型
        /// </summary>
        public static readonly DependencyProperty TransitionTypeProperty =
            DependencyProperty.Register("TransitionType", typeof(TransitionType), typeof(HamburgerMenu), new PropertyMetadata(default(TransitionType)));

        /// <summary>
        /// 转换类型
        /// </summary>
        public TransitionType TransitionType
        {
            get { return (TransitionType)GetValue(TransitionTypeProperty); }
            set { SetValue(TransitionTypeProperty, value); }
        }

        /// <summary>
        /// 动画时长
        /// </summary>
        public static readonly DependencyProperty TransitionDurationProperty =
            DependencyProperty.Register("TransitionDuration", typeof(Duration), typeof(HamburgerMenu), new PropertyMetadata(default(Duration)));

        /// <summary>
        /// 动画时长
        /// </summary>
        public Duration TransitionDuration
        {
            get { return (Duration)GetValue(TransitionDurationProperty); }
            set { SetValue(TransitionDurationProperty, value); }
        }

        /// <summary>
        /// 面板背景
        /// </summary>
        public static readonly DependencyProperty PaneBackgroundProperty =
            DependencyProperty.Register("PaneBackground", typeof(Brush), typeof(HamburgerMenu), new PropertyMetadata(default(Brush)));

        /// <summary>
        /// 面板背景
        /// </summary>
        public Brush PaneBackground
        {
            get { return (Brush)GetValue(PaneBackgroundProperty); }
            set { SetValue(PaneBackgroundProperty, value); }
        }

        /// <summary>
        /// 面板边框
        /// </summary>
        public static readonly DependencyProperty PaneBorderThicknessProperty =
            DependencyProperty.Register("PaneBorderThickness", typeof(Thickness), typeof(HamburgerMenu), new PropertyMetadata(default(Thickness)));

        /// <summary>
        /// 面板边框
        /// </summary>
        public Thickness PaneBorderThickness
        {
            get { return (Thickness)GetValue(PaneBorderThicknessProperty); }
            set { SetValue(PaneBorderThicknessProperty, value); }
        }

        /// <summary>
        /// 面板圆角半径
        /// </summary>
        public static readonly DependencyProperty PaneBorderCornerRadiusProperty =
            DependencyProperty.Register("PaneBorderCornerRadius", typeof(CornerRadius), typeof(HamburgerMenu), new PropertyMetadata(default(CornerRadius)));

        /// <summary>
        /// 面板圆角半径
        /// </summary>
        public CornerRadius PaneBorderCornerRadius
        {
            get { return (CornerRadius)GetValue(PaneBorderCornerRadiusProperty); }
            set { SetValue(PaneBorderCornerRadiusProperty, value); }
        }

        #endregion properties

        #region options item

        // form https://github.com/MahApps/MahApps.Metro
        // MahApps.Metro.Controls.HamburgerMenu

        /// <summary>Identifies the <see cref="OptionsItemsSource"/> dependency property.</summary>
        public static readonly DependencyProperty OptionsItemsSourceProperty = DependencyProperty.Register(
            nameof(OptionsItemsSource), typeof(IEnumerable), typeof(HamburgerMenu), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets an object source used to generate the content of the options.
        /// </summary>
        public IEnumerable OptionsItemsSource
        {
            get => (IEnumerable)this.GetValue(OptionsItemsSourceProperty);
            set => this.SetValue(OptionsItemsSourceProperty, value);
        }

        /// <summary>Identifies the <see cref="OptionsItemContainerStyle"/> dependency property.</summary>
        public static readonly DependencyProperty OptionsItemContainerStyleProperty = DependencyProperty.Register(
            nameof(OptionsItemContainerStyle), typeof(Style), typeof(HamburgerMenu), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets the <see cref="Style"/> used for each item in the options.
        /// </summary>
        public Style OptionsItemContainerStyle
        {
            get => (Style)this.GetValue(OptionsItemContainerStyleProperty);
            set => this.SetValue(OptionsItemContainerStyleProperty, value);
        }

        /// <summary>Identifies the <see cref="OptionsItemTemplate"/> dependency property.</summary>
        public static readonly DependencyProperty OptionsItemTemplateProperty = DependencyProperty.Register(
            nameof(OptionsItemTemplate), typeof(DataTemplate), typeof(HamburgerMenu), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets the <see cref="DataTemplate"/> used to display each item in the options.
        /// </summary>
        public DataTemplate OptionsItemTemplate
        {
            get => (DataTemplate)this.GetValue(OptionsItemTemplateProperty);
            set => this.SetValue(OptionsItemTemplateProperty, value);
        }

        /// <summary>Identifies the <see cref="OptionsItemTemplateSelector"/> dependency property.</summary>
        public static readonly DependencyProperty OptionsItemTemplateSelectorProperty = DependencyProperty.Register(
            nameof(OptionsItemTemplateSelector), typeof(DataTemplateSelector), typeof(HamburgerMenu), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets the <see cref="DataTemplateSelector"/> used to display each item in the options.
        /// </summary>
        public DataTemplateSelector OptionsItemTemplateSelector
        {
            get => (DataTemplateSelector)this.GetValue(OptionsItemTemplateSelectorProperty);
            set => this.SetValue(OptionsItemTemplateSelectorProperty, value);
        }

        /// <summary>Identifies the <see cref="OptionsVisibility"/> dependency property.</summary>
        public static readonly DependencyProperty OptionsVisibilityProperty = DependencyProperty.Register(
            nameof(OptionsVisibility), typeof(Visibility), typeof(HamburgerMenu), new PropertyMetadata(Visibility.Visible));

        /// <summary>
        /// Gets or sets the <see cref="Visibility"/> of the options menu.
        /// </summary>
        public Visibility OptionsVisibility
        {
            get => (Visibility)this.GetValue(OptionsVisibilityProperty);
            set => this.SetValue(OptionsVisibilityProperty, value);
        }

        /// <summary>
        /// Gets the collection used to generate the content of the option list.
        /// </summary>
        /// <exception cref="Exception">
        /// Exception thrown if OptionsListView is not yet defined.
        /// </exception>
        public ItemCollection OptionsItems
        {
            get
            {
                if (this.optionsMenu is null)
                {
                    throw new Exception("OptionsListView is not defined yet. Please use OptionsItemsSource instead.");
                }

                return this.optionsMenu.Items;
            }
        }

        #endregion options item

        #region events

        /// <summary>
        /// 汉堡包按钮点击事件
        /// </summary>
        public static readonly RoutedEvent HamburgerButtonClickEvent = EventManager.RegisterRoutedEvent(nameof(HamburgerButtonClick), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HamburgerMenu));

        /// <summary>
        /// 汉堡包按钮点击
        /// </summary>
        public event RoutedEventHandler HamburgerButtonClick
        {
            add => this.AddHandler(HamburgerButtonClickEvent, value);
            remove => this.RemoveHandler(HamburgerButtonClickEvent, value);
        }

        #endregion events

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

        #region methods

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

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

            optionsMenu = GetTemplateChild(OptionsItemsControlPartName) as MenuBase;

            contentTransition = GetTemplateChild(ContentTransitionPartName) as Transition;

            SelectionChanged += HamburgerMenu_SelectionChanged;
        }

        private void HamburgerMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.OriginalSource.GetHashCode() == GetHashCode())
            {
                Transition.ShowAnimation(contentTransition);
            }
        }

        /// <inheritdoc/>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is HamburgerMenuItem;
        }

        /// <inheritdoc/>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new HamburgerMenuItem();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var args = new RoutedEventArgs(HamburgerButtonClickEvent, sender);
            this.RaiseEvent(args);

            if (!args.Handled)
            {
                IsExpanded = !IsExpanded;
            }
        }

        #endregion methods
    }
}