﻿using System;
using UnityEngine.UIElements;

namespace JLGames.RocketDriver.Actions.Extensions
{
    public static class ExtVisualElement
    {
        private static readonly Exception ErrByName =
            new Exception("FindName empty at :ExtVisualElement.GetElementByName");

        private static readonly Exception ErrByChain =
            new Exception("ChainName empty at :ExtVisualElement.GetElementByChain");

        private static readonly char SplitChar = '/';

        #region 基于Children遍历查找

        public static VisualElement GetElementByName(this VisualElement root, string findName, bool useAsc = true)
        {
            if (string.IsNullOrEmpty(findName)) throw ErrByName;

            if (useAsc) return GetElementByNameAsc(root, findName);

            return GetElementByNameDesc(root, findName);
        }

        public static T GetElementByName<T>(this VisualElement root, string findName, bool useAsc = true)
            where T : VisualElement
        {
            if (string.IsNullOrEmpty(findName)) throw ErrByName;

            if (useAsc) return GetElementByNameAsc<T>(root, findName);
            return GetElementByNameDesc<T>(root, findName);
        }

        public static VisualElement GetElementByChain(this VisualElement root, string chainName, bool useAsc = true)
        {
            if (string.IsNullOrEmpty(chainName)) throw ErrByChain;

            var names = chainName.Split(SplitChar);

            var rs = root;
            for (var index = 0; index < names.Length; index++)
            {
                var name = names[index];
                rs = rs.GetElementByName(name, useAsc);
                if (null == rs) return null;
            }

            return rs;
        }

        public static T GetElementByChain<T>(this VisualElement root, string chainName, bool useAsc = true)
            where T : VisualElement
        {
            if (string.IsNullOrEmpty(chainName)) throw ErrByChain;

            var names = chainName.Split(SplitChar);

            var rs = root;
            for (var index = 0; index < names.Length; index++)
            {
                var name = names[index];
                if (index < names.Length - 1)
                {
                    rs = rs.GetElementByName<VisualElement>(name, useAsc);
                }
                else
                {
                    rs = rs.GetElementByName<T>(name, useAsc);
                }

                if (null == rs) return null;
            }

            return rs as T;
        }

        private static VisualElement GetElementByNameAsc(VisualElement root, string findName)
        {
            var count = root.childCount;
            for (var index = 0; index < count; index++)
            {
                var rs = root[index];
                if (rs.name == findName)
                {
                    return rs;
                }
            }

            return null;
        }

        private static T GetElementByNameAsc<T>(VisualElement root, string findName) where T : VisualElement
        {
            var count = root.childCount;
            for (var index = 0; index < count; index++)
            {
                var rs = root[index];
                if (rs.name == findName && rs is T)
                {
                    return rs as T;
                }
            }

            return null;
        }

        private static VisualElement GetElementByNameDesc(VisualElement root, string findName)
        {
            var count = root.childCount;
            for (var index = count - 1; index >= 0; index--)
            {
                var rs = root[index];
                if (rs.name == findName)
                {
                    return rs;
                }
            }

            return null;
        }

        private static T GetElementByNameDesc<T>(VisualElement root, string findName) where T : VisualElement
        {
            var count = root.childCount;
            for (var index = count - 1; index >= 0; index--)
            {
                var rs = root[index];
                if (rs.name == findName && rs is T)
                {
                    return rs as T;
                }
            }

            return null;
        }

        #endregion

        #region 基于UQuery查找

        public static T GetFirstTreeElementByName<T>(this VisualElement element, string toggleName)
            where T : VisualElement
        {
            return element.GetFirstTreeElement<T>((toggle => toggle.name == toggleName));
        }

        public static T GetLastTreeElementByName<T>(this VisualElement element, string toggleName)
            where T : VisualElement
        {
            return element.GetLastTreeElement<T>((toggle => toggle.name == toggleName));
        }

        public static T GetFirstTreeElement<T>(this VisualElement element, Predicate<T> match) where T : VisualElement
        {
            T result = null;
            element.Query<T>().ForEach(e =>
            {
                if (result != null || !match.Invoke(e)) return;
                result = e;
            });
            return result;
        }

        public static T GetLastTreeElement<T>(this VisualElement element, Predicate<T> match) where T : VisualElement
        {
            T result = null;
            element.Query<T>().ForEach(e =>
            {
                if (match.Invoke(e)) result = e;
            });
            return result;
        }

        #endregion
    }
}