using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Brush = System.Windows.Media.Brush;
using Brushes = System.Windows.Media.Brushes;

namespace Elwig.Helpers {
    public class ControlUtils {

        public enum RenewSourceDefault { None, IfOnly, First }

        private static void SetControlBorderBrush(Control input, Brush brush) {
            if (input is ComboBox cb) {
                var border = GetComboBoxBorder(cb);
                if (border != null) border.BorderBrush = brush;
            } else {
                input.BorderBrush = brush;
            }
        }

        public static void SetInputNotDefault(Control input) {
            SetControlBorderBrush(input, Brushes.Gold);
        }

        public static void SetInputChanged(Control input) {
            SetControlBorderBrush(input, Brushes.Orange);
        }

        public static void SetInputInvalid(Control input) {
            SetControlBorderBrush(input, Brushes.Red);
        }

        public static void ClearInputState(Control input) {
            if (input is ComboBox cb) {
                GetComboBoxBorder(cb)?.ClearValue(Border.BorderBrushProperty);
            } else {
                input.ClearValue(Control.BorderBrushProperty);
            }
        }

        private static Border? GetComboBoxBorder(ComboBox cb) {
            var toggleButton = cb.Template.FindName("toggleButton", cb) as ToggleButton;
            return toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
        }

        public static IEnumerable<T> FindAllChildren<T>(DependencyObject depObj) where T : DependencyObject {
            if (depObj == null)
                yield return (T)Enumerable.Empty<T>();
            foreach (var child in LogicalTreeHelper.GetChildren(depObj)) {
                if (child == null) {
                    continue;
                } else if (child is T t) {
                    yield return t;
                }
                if (child is DependencyObject childDepOpj) {
                    foreach (T childOfChild in FindAllChildren<T>(childDepOpj)) {
                        yield return childOfChild;
                    }
                }
            }
        }

        public static IEnumerable<T> FindAllChildren<T>(DependencyObject depObj, IEnumerable<DependencyObject> exempt) where T : DependencyObject {
            return FindAllChildren<T>(depObj).Where(c => !exempt.Contains(c));
        }

        public static T? FindNextSibling<T>(Control me) where T : DependencyObject {
            var found = false;
            foreach (var child in LogicalTreeHelper.GetChildren(me.Parent)) {
                if (found && child is T c) {
                    return c;
                } else if (child == me) {
                    found = true;
                }
            }
            return null;
        }

        public static void RenewItemsSource(Selector selector, IEnumerable? source, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None) {
            if (selector.ItemsSource == source)
                return;
            var selectedId = Utils.GetEntityIdentifier(selector.SelectedItem);
            object? selItem = null;
            if (selectedId != 0 && source != null)
                selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(Utils.GetEntityIdentifier(i)));
            if (source != null && selItem == null) {
                if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
                    selItem = source.Cast<object>().First();
                }
            }
            if (handler != null && selItem != null) selector.SelectionChanged -= handler;
            selector.ItemsSource = source;
            if (handler != null && selItem != null) selector.SelectionChanged += handler;
            selector.SelectedItem = selItem;
        }

        public static void RenewItemsSource(DataGrid dataGrid, IEnumerable? source, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None, bool keepSort = true) {
            if (dataGrid.ItemsSource == source)
                return;
            var column = dataGrid.CurrentCell.Column;
            var sortColumns = dataGrid.Columns.Select(c => c.SortDirection).ToList();
            var sort = dataGrid.Items.SortDescriptions.ToList();
            var selectedId = Utils.GetEntityIdentifier(dataGrid.SelectedItem);
            object? selItem = null;
            if (selectedId != 0 && source != null)
                selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(Utils.GetEntityIdentifier(i)));
            if (source != null && selItem == null) {
                if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
                    selItem = source.Cast<object>().First();
                }
            }
            if (handler != null && selItem != null) dataGrid.SelectionChanged -= handler;
            dataGrid.ItemsSource = source;
            if (handler != null && selItem != null) dataGrid.SelectionChanged += handler;
            dataGrid.SelectedItem = selItem;
            if (keepSort) {
                for (int i = 0; i < dataGrid.Columns.Count; i++)
                    dataGrid.Columns[i].SortDirection = sortColumns[i];
                foreach (var s in sort)
                    dataGrid.Items.SortDescriptions.Add(s);
            }
            if (dataGrid.SelectedItem != null && column != null)
                dataGrid.CurrentCell = new(dataGrid.SelectedItem, column);
        }

        public static void RenewItemsSource(ListBox listBox, IEnumerable? source, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None) {
            if (listBox.ItemsSource == source)
                return;
            if (listBox.SelectionMode == SelectionMode.Single) {
                var selectedId = Utils.GetEntityIdentifier(listBox.SelectedItem);
                object? selItem = null;
                if (selectedId != 0 && source != null)
                    selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(Utils.GetEntityIdentifier(i)));
                if (source != null && selItem == null) {
                    if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
                        selItem = source.Cast<object>().FirstOrDefault();
                    }
                }
                if (handler != null && selItem != null) listBox.SelectionChanged -= handler;
                listBox.ItemsSource = source;
                if (handler != null && selItem != null) listBox.SelectionChanged += handler;
                listBox.SelectedItem = selItem;
            } else {
                var selectedIds = listBox.SelectedItems.Cast<object>().Select(Utils.GetEntityIdentifier).ToList();
                if (handler != null && selectedIds != null) listBox.SelectionChanged -= handler;
                listBox.ItemsSource = source;
                if (source != null && selectedIds != null) {
                    listBox.SelectedItems.Clear();
                    foreach (var i in source.Cast<object>().Where(i => selectedIds.Contains(Utils.GetEntityIdentifier(i))))
                        listBox.SelectedItems.Add(i);
                }
                if (handler != null && selectedIds != null) listBox.SelectionChanged += handler;
            }
        }

        public static object? GetItemFromSource(IEnumerable source, int? hash) {
            if (source == null)
                return null;
            var items = source.Cast<object>();
            var item = items.Where(i => Utils.GetEntityIdentifier(i) == hash).FirstOrDefault();
            if (item == null && items.Any(i => i is NullItem))
                return items.Where(i => i is NullItem).First();
            return item;
        }

        public static T? GetItemFromSource<T>(IEnumerable source, T? item) {
            return (T?)GetItemFromSource(source, Utils.GetEntityIdentifier(item));
        }

        public static object? GetItemFromSourceWithPk(IEnumerable source, params object?[] primaryKey) {
            return GetItemFromSource(source, (int?)Utils.GetEntityIdetifierForPk(primaryKey));
        }

        public static void SelectItemWithHash(Selector input, int? hash) {
            if (hash == null) {
                input.SelectedItem = null;
            } else {
                input.SelectedItem = GetItemFromSource(input.ItemsSource, hash);
            }
            if (input is ListBox lb && lb.SelectedItem is object lbItem) {
                lb.ScrollIntoView(lbItem);
            } else if (input is DataGrid dg && dg.SelectedItem is object dgItem) {
                dg.ScrollIntoView(dgItem);
            }
        }

        public static void SelectItemWithPk(Selector input, params object?[] pk) {
            SelectItemWithHash(input, Utils.GetEntityIdentifier(pk));
        }

        public static void SelectItem(Selector input, object? item) {
            SelectItemWithHash(input, Utils.GetEntityIdentifier(item));
        }

        public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, IEnumerable<int?> ids) {
            if (source == null)
                return [];
            return source.Cast<object>().Where(i => ids.Any(c => c == Utils.GetEntityIdentifier(i)));
        }

        public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, IEnumerable<object?>? items) {
            if (items == null)
                return [];
            return GetItemsFromSource(source, items.Select(Utils.GetEntityIdentifier));
        }

        public static void SelectItems(ListBox lb, IEnumerable<int?>? ids) {
            lb.SelectedItems.Clear();
            if (ids == null) return;
            foreach (var id in ids)
                lb.SelectedItems.Add(GetItemFromSource(lb.ItemsSource, id));
        }

        public static void SelectItems(ListBox lb, IEnumerable<object>? items) {
            SelectItems(lb, items?.Select(Utils.GetEntityIdentifier));
        }

        public static int? GetInputHashCode(Control input) {
            if (input is TextBox tb) {
                return Utils.GetEntityIdentifier(tb.Text);
            } else if (input is ComboBox sb) {
                return Utils.GetEntityIdentifier(sb.SelectedItem);
            } else if (input is ListBox lb) {
                return Utils.GetEntityIdentifier(lb.SelectedItems);
            } else if (input is CheckBox cb) {
                return Utils.GetEntityIdentifier(cb.IsChecked);
            } else if (input is RadioButton rb) {
                return Utils.GetEntityIdentifier(rb.IsChecked);
            } else {
                return null;
            }
        }

        public static void InitializeDelayTimer(TextBox tb, Action<object, TextChangedEventArgs> handler) {
            var timer = new DispatcherTimer {
                Interval = TimeSpan.FromMilliseconds(250)
            };
            timer.Tick += (object? sender, EventArgs evt) => {
                timer.Stop();
                var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag;
                handler(oSender, oEvent);
            };
            tb.TextChanged += (object sender, TextChangedEventArgs evt) => {
                timer.Stop();
                timer.Tag = (sender, evt);
                timer.Start();
            };
        }
    }
}
