using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Timers;
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.Navigation;
using static C4IT.Logging.cLogManager;

namespace FasdDesktopUi.Basics.UserControls
{
    public partial class ComboBoxPageable : UserControl, IFocusInvoker
    {
        #region Fields & Properties

        private const int itemsPerPage = 10;
        private const double searchDelayInMilliseconds = 350;

        private int pageIndex = 0;
        private int pageCount = 1;
        private readonly Timer timer = new Timer(searchDelayInMilliseconds);

        private readonly cF4sdHealthSelectionDataRequest searchData = new cF4sdHealthSelectionDataRequest() { Search = string.Empty, Page = 0, PageSize = itemsPerPage };

        public int? ParentIndex { get; set; }
        public UIElement ParentElement { get; set; }


        public string ResetFilterLabel
        {
            get { return (string)GetValue(ResetFilterLabelProperty); }
            set { SetValue(ResetFilterLabelProperty, value); }
        }

        public static readonly DependencyProperty ResetFilterLabelProperty =
            DependencyProperty.Register("ResetFilterLabel", typeof(string), typeof(ComboBoxPageable), new PropertyMetadata(cMultiLanguageSupport.GetItem("Dialog.CloseCase.ResetFilter")));

        public bool ResetFilter
        {
            get { return (bool)GetValue(ResetFilterProperty); }
            set { SetValue(ResetFilterProperty, value); }
        }

        public static readonly DependencyProperty ResetFilterProperty =
            DependencyProperty.Register("ResetFilter", typeof(bool), typeof(ComboBoxPageable), new PropertyMetadata(false, new PropertyChangedCallback(ResetFilterChanged)));

        private static void ResetFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            try
            {
                if (!(d is ComboBoxPageable me))
                    return;

                me.searchData.ResetFilter = me.ResetFilter;
                me.InvokeSearchValueChanged();
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }



        #region TotalItemCount

        public int TotalItemCount
        {
            get { return (int)GetValue(TotalItemCountProperty); }
            set { SetValue(TotalItemCountProperty, value); }
        }

        public static readonly DependencyProperty TotalItemCountProperty =
            DependencyProperty.Register("TotalItemCount", typeof(int), typeof(ComboBoxPageable), new PropertyMetadata(0, new PropertyChangedCallback(TotalItemCountChanged)));

        private static void TotalItemCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

            try
            {
                if (!(d is ComboBoxPageable me))
                    return;

                me.pageCount = me.TotalItemCount % itemsPerPage == 0 ? me.TotalItemCount / itemsPerPage : (me.TotalItemCount / itemsPerPage) + 1;
                me.UpdatePageTracker();
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        #endregion

        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
    "SelectedItem",
    typeof(object),
    typeof(ComboBoxPageable),
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        #region SearchDataChanged

        public EventHandler<cF4sdHealthSelectionDataRequest> SearchDataChanged
        {
            get { return (EventHandler<cF4sdHealthSelectionDataRequest>)GetValue(SearchDataChangedProperty); }
            set { SetValue(SearchDataChangedProperty, value); }
        }


        public static readonly DependencyProperty SearchDataChangedProperty =
            DependencyProperty.Register("SearchDataChanged", typeof(EventHandler<cF4sdHealthSelectionDataRequest>), typeof(ComboBoxPageable), new PropertyMetadata(null));

        #endregion

        #region RestoreParentScrollFocusOnDropDownClose

        public bool RestoreParentScrollFocusOnDropDownClose
        {
            get { return (bool)GetValue(RestoreParentScrollFocusOnDropDownCloseProperty); }
            set { SetValue(RestoreParentScrollFocusOnDropDownCloseProperty, value); }
        }

        public static readonly DependencyProperty RestoreParentScrollFocusOnDropDownCloseProperty =
            DependencyProperty.Register("RestoreParentScrollFocusOnDropDownClose", typeof(bool), typeof(ComboBoxPageable), new PropertyMetadata(false));

        #endregion

        #region ItemData

        public ObservableCollection<KeyValuePair<string, object>> ItemData
        {
            get { return (ObservableCollection<KeyValuePair<string, object>>)GetValue(ItemDataProperty); }
            set
            {
                SetValue(ItemDataProperty, value);
            }
        }

        public static readonly DependencyProperty ItemDataProperty =
            DependencyProperty.Register("ItemData", typeof(ObservableCollection<KeyValuePair<string, object>>), typeof(ComboBoxPageable), new PropertyMetadata(null));

        #endregion

        #region Styles

        public Brush ComboBoxBackground
        {
            get { return (Brush)GetValue(ComboBoxBackgroundProperty); }
            set { SetValue(ComboBoxBackgroundProperty, value); }
        }

        public static readonly DependencyProperty ComboBoxBackgroundProperty =
            DependencyProperty.Register("ComboBoxBackground", typeof(Brush), typeof(ComboBoxPageable), new PropertyMetadata(Brushes.Transparent));

        #endregion

        #endregion

        public ComboBoxPageable()
        {
            InitializeComponent();
            timer.Elapsed += TimerElapsed;
        }

        private void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                timer.Stop();
                Dispatcher.Invoke(InvokeSearchValueChanged);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }
        private void InvokeSearchValueChanged()
        {
            try
            {
                searchData.Page = pageIndex;

                if (SearchDataChanged != null)
                    SearchDataChanged.Invoke(this, searchData);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public KeyValuePair<string, object>? GetSelectedValue()
        {
            try
            {
                if (ComboBoxControl.SelectedItem is KeyValuePair<string, object> selectedItem)
                    return selectedItem;
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return null;
        }

        public bool CloseDropDownIfOpen()
        {
            if (ComboBoxControl?.IsDropDownOpen != true)
                return false;

            ComboBoxControl.IsDropDownOpen = false;
            return true;
        }

        #region Paging Events

        #region PageBack

        private void PageBackButton_Click(RoutedEventArgs e)
        {
            if (pageIndex <= 0)
            {
                e.Handled = true;
                return;
            }
            pageIndex--;
            InvokeSearchValueChanged();
            UpdatePageTracker();
            e.Handled = true;
        }

        private void PageBackButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            PageBackButton_Click(e);
        }

        private void PageBackButton_TouchDown(object sender, TouchEventArgs e)
        {
            PageBackButton_Click(e);
        }

        #endregion

        #region PageFor

        private void PageForButton_Click(RoutedEventArgs e)
        {
            if (pageIndex + 1 >= pageCount)
                return;

            pageIndex++;
            InvokeSearchValueChanged();
            UpdatePageTracker();
            e.Handled = true;
        }

        private void PageForButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            PageForButton_Click(e);
        }

        private void PageForButton_TouchDown(object sender, TouchEventArgs e)
        {
            PageForButton_Click(e);
        }


        #endregion

        #endregion

        private void UpdatePageTracker()
        {
            try
            {
                var pageTrackerElement = ComboBoxControl.Template.FindName("PageTrackerTextBlock", ComboBoxControl);

                if (!(pageTrackerElement is TextBlock pageTrackerTextBlock))
                    return;

                pageTrackerTextBlock.Text = $"{pageIndex + 1} / {pageCount}";
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            try
            {
                if (!(sender is TextBox searchTextBox))
                    return;

                pageIndex = 0;
                searchData.Search = searchTextBox.Text;
                UpdatePageTracker();

                timer.Stop();
                timer.Start();
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void SearchTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key != Key.Escape)
                return;

            if (CloseDropDownIfOpen())
                e.Handled = true;
        }

        private void ComboBoxControl_DropDownClosed(object sender, EventArgs e)
        {
            timer.Stop();
            cFocusInvoker.InvokeLostFocus(this, e);

            if (RestoreParentScrollFocusOnDropDownClose)
            {
                _ = Dispatcher.BeginInvoke((Action)(() =>
                {
                    try
                    {
                        var parentScrollViewer = cUiElementHelper.GetFirstParentOfType<ScrollViewer>(this);
                        Keyboard.ClearFocus();

                        if (parentScrollViewer != null)
                        {
                            parentScrollViewer.Focus();
                            Keyboard.Focus(parentScrollViewer);
                        }
                    }
                    catch (Exception exception)
                    {
                        LogException(exception);
                    }
                }), System.Windows.Threading.DispatcherPriority.Input);
            }
        }

        private void ComboBoxControl_DropDownOpened(object sender, EventArgs e)
        {
            try
            {
                // Elterninfo nur setzen, wenn (noch) nicht sinnvoll vorhanden
                bool invalidIndex = false;
                if (ParentElement is Panel p && (ParentIndex == null || ParentIndex < 0 || ParentIndex > p.Children.Count))
                    invalidIndex = true;

                if (ParentElement == null || invalidIndex)
                {
                    ParentElement = this.Parent as UIElement;

                    if (this.Parent is Panel panel)
                        ParentIndex = panel.Children.IndexOf(this);
                    else
                        ParentIndex = null;
                }


                cFocusInvoker.InvokeGotFocus(this, e);
                InvokeSearchValueChanged();


                #region Focus auf Textfeld setzen beim Öffnen der Combobox
                if (!(sender is ComboBox comboBox)) return;

                // Zugriff auf das Popup-Element
                Popup partPopup = comboBox.Template.FindName("PART_Popup", comboBox) as Popup;
                if (partPopup == null) return;

                // Verzögerter Fokus
                var _h = Dispatcher.BeginInvoke((Action)(() =>
                {
                    if (partPopup.IsOpen)
                    {
                        // Suchen des SearchTextBox-Element innerhalb des Popups
                        TextBox searchTextBox = FindVisualChild<TextBox>(partPopup.Child, "SearchTextBox");
                        if (searchTextBox != null)
                        {
                            searchTextBox.PreviewKeyDown -= SearchTextBox_PreviewKeyDown;
                            searchTextBox.PreviewKeyDown += SearchTextBox_PreviewKeyDown;
                            // Setzen des Fokus auf TextBox
                            searchTextBox.Focus();
                        }
                    }
                }), System.Windows.Threading.DispatcherPriority.Loaded);
                #endregion
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private static T FindVisualChild<T>(DependencyObject parent, string childName) where T : DependencyObject
        {
            if (parent == null) return null;

            T foundChild = null;
            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);

            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                var childType = child as T;
                if (childType == null)
                {
                    foundChild = FindVisualChild<T>(child, childName);
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        return (T)child;
                    }
                }
                else
                {
                    return (T)child;
                }
            }
            return foundChild;
        }
    }
}
