using FasdDesktopUi.Basics.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using static C4IT.Logging.cLogManager;

namespace FasdDesktopUi.Basics.Models
{
    public interface IFocusInvoker
    {
        int? ParentIndex { get; }
        UIElement ParentElement { get; }
    }

    public static class cFocusInvoker
    {
        public static EventHandler GotFocus = delegate { };
        public static EventHandler LostFocus = delegate { };

        private static void ReAllocate(this IFocusInvoker focusInvoker)
        {
            try
            {
                if (!(focusInvoker is FrameworkElement focusInvokerElement))
                    return;

                if (focusInvoker.ParentElement is null)
                    return;

                if (focusInvokerElement.Parent is Panel actualParentPanel)
                    actualParentPanel.Children.Remove(focusInvokerElement);
                else if (focusInvokerElement.Parent is Decorator actualParentDecorator)
                    actualParentDecorator.Child = null;
                else
                    return;

                if (focusInvoker.ParentElement is Panel parentPanel)
                {
                    if (focusInvoker.ParentIndex != null)
                    {
                        parentPanel.Children.RemoveAt(focusInvoker.ParentIndex.Value);
                        parentPanel.Children.Insert(focusInvoker.ParentIndex.Value, focusInvokerElement);
                    }
                    else
                    {
                        parentPanel.Children.Add(focusInvokerElement);
                    }
                }
                else if (focusInvoker.ParentElement is Decorator parentDecorator)
                {
                    parentDecorator.Child = focusInvokerElement;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private static void ReAllocate(this FrameworkElement focusInvokerElement)
        {
            try
            {
                var preFocusParent = GetPreFocusParent(focusInvokerElement);

                if (preFocusParent is null)
                    return;

                if (focusInvokerElement.Parent is Panel actualParentPanel)
                    actualParentPanel.Children.Remove(focusInvokerElement);
                else if (focusInvokerElement.Parent is Decorator actualParentDecorator)
                    actualParentDecorator.Child = null;
                else
                    return;

                if (preFocusParent is Panel parentPanel)
                {
                    var preFocusChildIndex = GetPreFocusChildIndex(focusInvokerElement);

                    if (preFocusChildIndex != null)
                    {
                        parentPanel.Children.RemoveAt(preFocusChildIndex.Value);
                        parentPanel.Children.Insert(preFocusChildIndex.Value, focusInvokerElement);
                    }
                    else
                    {
                        parentPanel.Children.Add(focusInvokerElement);
                    }
                }
                else if (preFocusParent is Decorator parentDecorator)
                {
                    parentDecorator.Child = focusInvokerElement;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public static void InvokeGotFocus(object sender, EventArgs e)
        {
            try
            {
                if (sender is FrameworkElement senderElement)
                {
                    SetPreFocusParent(senderElement, senderElement.Parent);

                    if (senderElement.Parent is Panel actualParentPanel)
                        SetPreFocusChildIndex(senderElement, actualParentPanel.Children.IndexOf(senderElement));
                }

                GotFocus?.Invoke(sender, e);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public static void InvokeLostFocus(object sender, EventArgs e)
        {
            LostFocus?.Invoke(sender, e);

            if (sender is IFocusInvoker focusSender)
                focusSender.ReAllocate();
            else if (sender is FrameworkElement senderElement)
                senderElement.ReAllocate();
        }

        #region Attached Properties

        #region PreFocusChildIndex

        private static int? GetPreFocusChildIndex(DependencyObject obj)
        {
            return (int?)obj.GetValue(PreFocusChildIndexProperty);
        }

        private static void SetPreFocusChildIndex(DependencyObject obj, int? value)
        {
            obj.SetValue(PreFocusChildIndexProperty, value);
        }

        public static readonly DependencyProperty PreFocusChildIndexProperty =
            DependencyProperty.RegisterAttached("PreFocusChildIndex", typeof(int?), typeof(UIElement), new PropertyMetadata(null));

        #endregion

        #region PreFocusParent

        private static DependencyObject GetPreFocusParent(DependencyObject obj)
        {
            return (DependencyObject)obj.GetValue(PreFocusParentProperty);
        }

        private static void SetPreFocusParent(DependencyObject obj, DependencyObject value)
        {
            obj.SetValue(PreFocusParentProperty, value);
        }

        public static readonly DependencyProperty PreFocusParentProperty =
            DependencyProperty.RegisterAttached("PreFocusParent", typeof(DependencyObject), typeof(UIElement), new PropertyMetadata(null));

        #endregion

        #endregion
    }
}
