ユーティリティ関数2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using Unity.Linq;
using UniRx;
using UniRx.Triggers;
using TMPro;
using DG.Tweening;
using Cysharp.Threading.Tasks;

namespace Assets.Scripts.Utility
{
    public static class Extentions
    {
        public static IEnumerable<GameObject> GetChilds(this GameObject gameObject)
        {
            return gameObject.transform.GetChilds();
        }

        public static IEnumerable<GameObject> GetChilds(this Transform transform)
        {
            return transform.Cast<Transform>().Select(t => t.gameObject);
        }

        public static void RemoveAllChilds(this GameObject gameObject)
        {
            gameObject.transform.RemoveAllChilds();
        }

        public static void RemoveAllChilds(this Transform transform)
        {
            foreach (var child in transform.GetChilds().ToList())
            {
                child.transform.SetParent(null);
                child.Destroy();
            }
        }

        static readonly Color defaultNormalColor = Color.white;
        static readonly Color defaultHighlightedColor = new Color(.8f, .8f, .8f);
        static readonly Color defaultPressedColor = new Color(.6f, .6f, .6f);

        public static void ResetColor(this Button button)
        {
            button.ResetBackgroundColor();
        }

        static void ResetBackgroundColor(this Button button)
        {
            button.SetColors(defaultNormalColor, defaultHighlightedColor, defaultPressedColor);
        }

        static readonly Color defaultTextColor = Defines.DefaultTextColor;
        static readonly Color defaultOutlineColor = new Color(1f, 1f, 1f, 0.5f);

        static void ResetButtonTextColor(this Text text)
        {
            text.color = defaultTextColor;
        }

        static void ResetButtonOutlineColor(this Outline outline)
        {
            outline.effectColor = defaultOutlineColor;
        }

        static readonly Color normalColor1 = Color.green;
        static readonly Color highlightedColor1 = new Color(.46f, 1f, .32f);
        static readonly Color pressedColor1 = new Color(0f, .68f, 0f);

        static void SetSelectedBackgroundColor1(this Button button)
        {
            button.SetColors(normalColor1, highlightedColor1, pressedColor1);
        }

        static readonly Color textColor1 = Color.white;
        static readonly Color outlineColor1 = new Color(0f, 0f, 0f, .5f);

        static void SetSelectedButtonTextColor1(this Text text)
        {
            text.color = textColor1;
        }

        static void SetSelectedButtonOutlineColor1(this Outline outline)
        {
            outline.effectColor = outlineColor1;
        }

        public static void SetSelectedColor1(this Button button, bool bSelect)
        {
            button.SetSelectedBackgroundColor1(bSelect);
        }

        public static void SetSelectedBackgroundColor1(this Button button, bool bSelect)
        {
            if (bSelect)
            {
                button.SetSelectedBackgroundColor1();
            }
            else
            {
                button.ResetBackgroundColor();
            }
        }

        public static void SetSelectedButtonTextColor1(this Text text, bool bSelect)
        {
            if (bSelect)
            {
                text.SetSelectedButtonTextColor1();
            }
            else
            {
                text.ResetButtonTextColor();
            }
        }

        public static void SetSelectedButtonOutlineColor1(this Outline outline, bool bSelect)
        {
            if (bSelect)
            {
                outline.SetSelectedButtonOutlineColor1();
            }
            else
            {
                outline.ResetButtonOutlineColor();
            }
        }

        static readonly Color normalColor2 = new Color(1f, 0.398f, 0.8476396f);
        static readonly Color highlightedColor2 = new Color(1f, 0.1f, 0.7720783f);
        static readonly Color pressedColor2 = new Color(1f, 0.7f, 0.9240175f);

        static void SetSelectedBackgroundColor2(this Button button)
        {
            button.SetColors(normalColor2, highlightedColor2, pressedColor2);
        }

        static readonly Color textColor2 = Color.white;
        static readonly Color outlineColor2 = new Color(0f, 0f, 0f, .5f);

        static void SetSelectedButtonTextColor2(this Text text)
        {
            text.color = textColor2;
        }

        static void SetSelectedButtonOutlineColor2(this Outline outline)
        {
            outline.effectColor = outlineColor2;
        }

        public static void SetSelectedColor2(this Button button, bool bSelect)
        {
            button.SetSelectedBackgroundColor2(bSelect);
        }

        static void SetSelectedBackgroundColor2(this Button button, bool bSelect)
        {
            if (bSelect)
            {
                button.SetSelectedBackgroundColor2();
            }
            else
            {
                button.ResetBackgroundColor();
            }
        }

        public static void SetSelectedButtonTextColor2(this Text text, bool bSelect)
        {
            if (bSelect)
            {
                text.SetSelectedButtonTextColor2();
            }
            else
            {
                text.ResetButtonTextColor();
            }
        }

        public static void SetSelectedButtonOutlineColor2(this Outline outline, bool bSelect)
        {
            if (bSelect)
            {
                outline.SetSelectedButtonOutlineColor2();
            }
            else
            {
                outline.ResetButtonOutlineColor();
            }
        }

        static void SetColors(this Button button, Color normalColor, Color highlightedColor, Color pressedColor)
        {
            var colors = button.colors;
            colors.normalColor = normalColor;
            colors.selectedColor = normalColor;
            colors.highlightedColor = highlightedColor;
            colors.pressedColor = pressedColor;
            button.colors = colors;
        }

        public static float NormalizeAngle(this float angle)
        {
            if (360f < angle) return NormalizeAngle(angle - 360f);//再帰
            if (angle < 0f) return NormalizeAngle(angle + 360f);//再帰
            return angle;
        }

        // エンディアン変換
        // https://takachan.hatenablog.com/entry/2018/03/10/020555

        // .NETはintは32bitという風にサイズが固定で変化しない

        // 共通化できるものは処理を移譲する
        public static char Reverse(this char value) => (char)((ushort)value).Reverse();

        public static short Reverse(this short value) => (short)((ushort)value).Reverse();

        public static int Reverse(this int value) => (int)((uint)value).Reverse();

        public static long Reverse(this long value) => (long)((ulong)value).Reverse();

        // 伝統的な16ビット入れ替え処理16bit
        public static ushort Reverse(this ushort value)
        {
            return (ushort)((value & 0xFF) << 8 | (value >> 8) & 0xFF);
        }

        // 伝統的な16ビット入れ替え処理32bit
        public static uint Reverse(this uint value)
        {
            return (value & 0xFF) << 24 |
                    ((value >> 8) & 0xFF) << 16 |
                    ((value >> 16) & 0xFF) << 8 |
                    ((value >> 24) & 0xFF);
        }

        // 伝統的な16ビット入れ替え処理64bit
        public static ulong Reverse(this ulong value)
        {
            return (value & 0xFF) << 56 |
                    ((value >> 8) & 0xFF) << 48 |
                    ((value >> 16) & 0xFF) << 40 |
                    ((value >> 24) & 0xFF) << 32 |
                    ((value >> 32) & 0xFF) << 24 |
                    ((value >> 40) & 0xFF) << 16 |
                    ((value >> 48) & 0xFF) << 8 |
                    ((value >> 56) & 0xFF);
        }

        // 浮動小数点はちょっと効率悪いけどライブラリでできる操作でカバーする
        public static float Reverse(this float value)
        {
            byte[] bytes = BitConverter.GetBytes(value); // これ以上いい処理が思いつかない
            Array.Reverse(bytes);
            return BitConverter.ToSingle(bytes, 0);
        }

        public static double Reverse(this double value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            Array.Reverse(bytes);
            return BitConverter.ToDouble(bytes, 0);
        }

        /// <summary>
        /// Checks if a GameObject has been destroyed.
        /// </summary>
        /// <param name="gameObject">GameObject reference to check for destructedness</param>
        /// <returns>If the game object has been marked as destroyed by UnityEngine</returns>
        public static bool IsDestroyed(this GameObject gameObject)
        {
            // UnityEngine overloads the == opeator for the GameObject type
            // and returns null when the object has been destroyed, but 
            // actually the object is still there but has not been cleaned up yet
            // if we test both we can determine if the object has been destroyed.
            return gameObject == null && !ReferenceEquals(gameObject, null);
        }

        public static bool IsDestroyed(this MonoBehaviour monoBehaviour)
        {
            try
            {
                if (monoBehaviour == null) return false;
                return monoBehaviour.gameObject.IsDestroyed();
            }
            catch
            {
                return false;
            }
        }

        // 参考 https://stackoverflow.com/questions/42043017/check-if-ui-elements-recttransform-are-overlapping
        public static bool Overlaps(this RectTransform a, RectTransform b)
        {
            return a.WorldRect().Overlaps(b.WorldRect());
        }

        public static bool Overlaps(this RectTransform a, RectTransform b, bool allowInverse)
        {
            return a.WorldRect().Overlaps(b.WorldRect(), allowInverse);
        }

        public static Rect WorldRect(this RectTransform rectTransform)
        {
            var position = new Vector2(rectTransform.position.x, rectTransform.position.y);
            var size = Vector2.Scale(rectTransform.rect.size, rectTransform.lossyScale);
            position.y -= size.y;
            return new Rect(position, size);
        }

        // 参考 https://stackoverflow.com/questions/4015602/equivalent-of-stringbuilder-for-byte-arrays
        public static void Append(this System.IO.MemoryStream stream, byte[] values)
        {
            stream.Write(values, 0, values.Length);
        }

        public static Vector3 GetCenter(this GameObject gameObject)
        {
            return gameObject.transform.GetCenter();
        }

        public static Vector3 GetCenter(this Transform transform)
        {
            var pos = transform.position;
            pos.y += transform.lossyScale.y * 0.5f;
            return pos;
        }

        public static Vector3 GetTop(this GameObject gameObject)
        {
            return gameObject.transform.GetTop();
        }

        public static Vector3 GetTop(this Transform transform)
        {
            var pos = transform.position;
            pos.y += transform.lossyScale.y;
            return pos;
        }

        public static Vector3 GetBottom(this GameObject gameObject)
        {
            return gameObject.transform.GetBottom();
        }

        public static Vector3 GetBottom(this Transform transform)
        {
            return transform.position;
        }

        public static Bounds GetBounds(this GameObject gameObject)
        {
            return gameObject.transform.GetBounds();
        }

        public static Bounds GetBounds(this Transform transform)
        {
            return new Bounds(transform.GetCenter(), transform.lossyScale);
        }

        public static Vector3 GetTouchPosition(this GameObject gameObject, Bounds bounds)
        {
            var srcPosition = gameObject.transform.position;
            var dstPosition = bounds.center;

            var srcClosestPoint = gameObject.GetBounds().ClosestPoint(dstPosition);
            var srcThickness = Vector3.Distance(srcClosestPoint, srcPosition);

            var dstClosestPoint = bounds.ClosestPoint(srcPosition);
            var distance = Vector3.Distance(dstClosestPoint, srcPosition) - srcThickness;
            return srcPosition + Vector3.ClampMagnitude(dstClosestPoint - srcPosition, distance);
        }

        public static Dictionary<TKey,TValue> Duplicate<TKey, TValue>(this Dictionary<TKey, TValue> src) where TValue : struct
        {
            return src.Duplicate(value => value);
        }

        public static Dictionary<TKey,TValue> Duplicate<TKey, TValue>(this Dictionary<TKey, TValue> src, Func<TValue, TValue> cloner)
        {
            var result = new Dictionary<TKey, TValue>();
            foreach (var pair in src)
            {
                result.Add(pair.Key, cloner(pair.Value));
            }

            return result;
        }

#if PLAYFAB_SUPPORT
        public static PlayFab.ProfilesModels.EntityKey ToProfilesModelsEntityKey(this PlayFab.ClientModels.EntityKey entityKey)
        {
            return new PlayFab.ProfilesModels.EntityKey()
            {
                Id = entityKey.Id,
                Type = entityKey.Type
            };
        }

        public static PlayFab.ClientModels.EntityKey ToClientModelsEntityKey(this PlayFab.ProfilesModels.EntityKey entityKey)
        {
            return new PlayFab.ClientModels.EntityKey()
            {
                Id = entityKey.Id,
                Type = entityKey.Type
            };
        }

        public static PlayFab.MultiplayerModels.EntityKey ToMultiplayerModelsEntityKey(this PlayFab.ClientModels.EntityKey entityKey)
        {
            return new PlayFab.MultiplayerModels.EntityKey()
            {
                Id = entityKey.Id,
                Type = entityKey.Type
            };
        }

        public static PlayFab.ClientModels.EntityKey ToClientModelsEntityKey(this PlayFab.MultiplayerModels.EntityKey entityKey)
        {
            return new PlayFab.ClientModels.EntityKey()
            {
                Id = entityKey.Id,
                Type = entityKey.Type
            };
        }
#endif

        public static void AddValues<T>(this Dictionary<T, uint> s, Dictionary<T, uint> d)
        {
            foreach (var item in d)
            {
                if (s.ContainsKey(item.Key))
                {
                    s[item.Key] += item.Value;
                }
                else
                {
                    s.Add(item.Key, item.Value);
                }
            }
        }

        public static void AddValues<T>(this Dictionary<T, byte> s, Dictionary<T, byte> d)
        {
            foreach (var item in d)
            {
                if (s.ContainsKey(item.Key))
                {
                    s[item.Key] += item.Value;
                }
                else
                {
                    s.Add(item.Key, item.Value);
                }
            }
        }

        public static Dictionary<T, uint> Duplicate<T>(this Dictionary<T, uint> s)
        {
            var d = new Dictionary<T, uint>();
            foreach (var pair in s)
            {
                d.Add(pair.Key, pair.Value);
            }
            return d;
        }

        public static Vector3 AddX(this GameObject gameObject, float x, bool bModifyMe = false)
        {
            return gameObject.transform.AddX(x, bModifyMe);
        }

        public static Vector3 AddX(this Transform transform, float x, bool bModifyMe = false)
        {
            var result = transform.position.AddX(x);
            if (bModifyMe)
            {
                transform.position = result;
            }
            return result;
        }

        public static Vector3 AddX(this Vector3 vector3, float x)
        {
            return vector3 + new Vector3(x, 0f, 0f);
        }

        public static Vector3 AddY(this GameObject gameObject, float y, bool bModifyMe = false)
        {
            return gameObject.transform.AddY(y, bModifyMe);
        }

        public static Vector3 AddY(this Transform transform, float y, bool bModifyMe = false)
        {
            var result = transform.position.AddY(y);
            if (bModifyMe)
            {
                transform.position = result;
            }
            return result;
        }

        public static Vector3 AddY(this Vector3 vector3, float y)
        {
            return vector3 + new Vector3(0f, y, 0f);
        }

        public static Vector3 AddZ(this GameObject gameObject, float z, bool bModifyMe = false)
        {
            return gameObject.transform.AddZ(z, bModifyMe);
        }

        public static Vector3 AddZ(this Transform transform, float z, bool bModifyMe = false)
        {
            var result = transform.position.AddZ(z);
            if (bModifyMe)
            {
                transform.position = result;
            }
            return result;
        }

        public static Vector3 AddZ(this Vector3 vector3, float z)
        {
            return vector3 + new Vector3(0f, 0f, z);
        }

        public static Vector3 SetX(this GameObject gameObject, float x, bool bModifyMe = false)
        {
            return gameObject.transform.SetX(x, bModifyMe);
        }

        public static Vector3 SetX(this Transform transform, float x, bool bModifyMe = false)
        {
            var result = transform.position.SetX(x);
            if (bModifyMe)
            {
                transform.position = result;
            }
            return result;
        }

        public static Vector3 SetX(this Vector3 vector3, float x)
        {
            return new Vector3(x, vector3.y, vector3.z);
        }

        public static Vector3 SetY(this GameObject gameObject, float y, bool bModifyMe = false)
        {
            return gameObject.transform.SetY(y, bModifyMe);
        }

        public static Vector3 SetY(this Transform transform, float y, bool bModifyMe = false)
        {
            var result = transform.position.SetY(y);
            if (bModifyMe)
            {
                transform.position = result;
            }
            return result;
        }

        public static Vector3 SetY(this Vector3 vector3, float y)
        {
            return new Vector3(vector3.x, y, vector3.z);
        }

        public static Vector3 SetZ(this GameObject gameObject, float z, bool bModifyMe = false)
        {
            return gameObject.transform.SetZ(z, bModifyMe);
        }

        public static Vector3 SetZ(this Transform transform, float z, bool bModifyMe = false)
        {
            var result = transform.position.SetZ(z);
            if (bModifyMe)
            {
                transform.position = result;
            }
            return result;
        }

        public static Vector3 SetZ(this Vector3 vector3, float z)
        {
            return new Vector3(vector3.x, vector3.y, z);
        }

        // https://stackoverflow.com/questions/4133377/splitting-a-string-number-every-nth-character-number
        public static IEnumerable<String> SplitInParts(this String s, Int32 partLength)
        {
            if (s == null)
                throw new ArgumentNullException(nameof(s));
            if (partLength <= 0)
                throw new ArgumentException("Part length has to be positive.", nameof(partLength));

            for (var i = 0; i < s.Length; i += partLength)
                yield return s.Substring(i, Math.Min(partLength, s.Length - i));
        }

        // https://stackoverflow.com/questions/30766020/how-to-scroll-to-a-specific-element-in-scrollrect-with-unity-ui/61632269#61632269
        public static void EnsureVisibleItem(this ScrollRect scroller, RectTransform child, float padding = 0f)
        {
            Canvas.ForceUpdateCanvases();

            float viewportHeight = scroller.viewport.rect.height;
            Vector2 scrollPosition = scroller.content.anchoredPosition;

            float elementTop = child.anchoredPosition.y;
            float elementBottom = elementTop - child.rect.height;

            float visibleContentTop = -scrollPosition.y - padding;
            float visibleContentBottom = -scrollPosition.y - viewportHeight + padding;

            float scrollDelta =
                elementTop > visibleContentTop ? visibleContentTop - elementTop :
                elementBottom < visibleContentBottom ? visibleContentBottom - elementBottom :
                0f;

            scrollPosition.y += scrollDelta;
            scroller.content.anchoredPosition = scrollPosition;
        }

        public static bool DictionaryEquals<TKey, TValue>(this Dictionary<TKey, TValue< THIS, Dictionary<TKey, TValue> other) where TValue : IEquatable<TValue>
        {
            if (other == null) return false;
            if (THIS == other) return true;
            if (THIS.Count != other.Count) return false;
            return THIS.All(pair => other.ContainsKey(pair.Key) && pair.Value.Equals(other[pair.Key]));
        }

        public static void ReplaceAllChildsMaterial(this GameObject gameObject, Material material)
        {
            var renderer = gameObject.GetComponent<Renderer>();
            if (renderer != null)
            {
                renderer.material = material;
            }

            foreach (var child in gameObject.GetChilds())
            {
                child.ReplaceAllChildsMaterial(material);//再帰
            }
        }

        public static void SubscribeBack(this MonoBehaviour monoBehaviour, Action action)
        {
#if UNITY_ANDROID
            monoBehaviour.UpdateAsObservable()
                .Where(_ => UnityEngine.InputSystem.Keyboard.current.escapeKey.wasPressedThisFrame)
                .ThrottleFirst(TimeSpan.FromMilliseconds(3000))
                .Subscribe(async _ =>
                {
                    if (!SceneBackDisabler.CanBack()) return;
                    await SoundScript.Instance.PlayAsync(Defines.SoundEffect.Cancel);
                    action();
                })
                .AddTo(monoBehaviour);
#endif
        }

        public static void FitSize(this Text text)
        {
            // 参考 https://kan-kikuchi.hatenablog.com/entry/Text_Preferred
            text.rectTransform.sizeDelta = new Vector2(text.preferredWidth, text.preferredHeight);
            text.rectTransform.sizeDelta = new Vector2(text.preferredWidth, text.preferredHeight);
        }

#if PLAYFAB_SUPPORT
        public static string GetString(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out object value) && !string.IsNullOrEmpty(value?.ToString()) ? value.ToString() : null;
        }

        public static int? GetInt(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out var value) && int.TryParse(value.ToString(), out var v) ? v : null;
        }

        public static DateTime? GetDateTime(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out var value) && DateTime.TryParse(value.ToString(), out var v) ? v : null;
        }

        public static Version GetVersion(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out var value) && Version.TryParse(value.ToString(), out var v) ? v : null;
        }

        public static byte? GetByte(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out var value) && byte.TryParse(value.ToString(), out var v) ? v : null;
        }

        public static short? GetShort(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out var value) && short.TryParse(value.ToString(), out var v) ? v : null;
        }

        public static bool? GetBool(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out var value) && bool.TryParse(value.ToString(), out var v) ? v : null;
        }

        public static PlayFab.Json.JsonArray GetJsonArray(this PlayFab.Json.JsonObject jsonObject, string key)
        {
            return jsonObject.TryGetValue(key, out var value) && value is PlayFab.Json.JsonArray jsonArray ? jsonArray : null;
        }
#endif

        public static async void SetAndFade(this TextMeshProUGUI textMeshProUGUI, string valueText, GameObject parent)
        {
            if (textMeshProUGUI.text == valueText) return;

            await textMeshProUGUI.DOFade(0f, 0.5f).SetAutoKill().SetLink(parent);
            textMeshProUGUI.text = valueText;
            await textMeshProUGUI.DOFade(1f, 0.5f).SetAutoKill().SetLink(parent);
        }

        // https://baba-s.hatenablog.com/entry/2022/01/10/210000
        public static float RoundByMultiple(this float value, float unit)
        {
            return Mathf.Round(value / unit) * unit;
        }
    }
}