using UnityEngine;
using System;

public static class TransformExtensions {

    public static float ForwardToTargetPositionXZAngle(this Transform transform, Transform target){
        return transform.ForwardToTargetPositionXZAngle(target.position);
    }
    public static float ForwardToTargetPositionXZAngle(this Transform transform, Vector3 targetPosition){
        Vector2 t = (targetPosition - transform.position).ToVector2XZ().normalized;
        Vector2 f = transform.forward.ToVector2XZ().normalized;
        return Mathf.Atan2(t.Cross(f), t.Dot(f)) * Mathf.Rad2Deg;
    }

    // position
    public static void SetPositionX(this Transform t, float f){
        t.position = t.position.SetX(f);
    }
    public static void SetPositionY(this Transform t, float f){
        t.position = t.position.SetY(f);
    }
    public static void SetPositionZ(this Transform t, float f){
        t.position = t.position.SetZ(f);
    }

    public static void AddPositionX(this Transform t, float f){
        t.SetPositionX(t.position.x + f);
    }
    public static void AddPositionY(this Transform t, float f){
        t.SetPositionY(t.position.y + f);
    }
    public static void AddPositionZ(this Transform t, float f){
        t.SetPositionZ(t.position.z + f);
    }

    // localPosition
    public static void SetLocalPositionX(this Transform t, float f){
        t.localPosition = t.localPosition.SetX(f);
    }
    public static void SetLocalPositionY(this Transform t, float f){
        t.localPosition = t.localPosition.SetY(f);
    }
    public static void SetLocalPositionZ(this Transform t, float f){
        t.localPosition = t.localPosition.SetZ(f);
    }

    public static void AddLocalPositionX(this Transform t, float f){
        t.SetLocalPositionX(t.localPosition.x + f);
    }
    public static void AddLocalPositionY(this Transform t, float f){
        t.SetLocalPositionY(t.localPosition.y + f);
    }
    public static void AddLocalPositionZ(this Transform t, float f){
        t.SetLocalPositionZ(t.localPosition.z + f);
    }

    // eulerAngles
    public static void SetEulerAnglesX(this Transform t, float f){
        t.eulerAngles = t.eulerAngles.SetX(f);
    }
    public static void SetEulerAnglesY(this Transform t, float f){
        t.eulerAngles = t.eulerAngles.SetY(f);
    }
    public static void SetEulerAnglesZ(this Transform t, float f){
        t.eulerAngles = t.eulerAngles.SetZ(f);
    }

    public static void AddEulerAnglesX(this Transform t, float f){
        t.SetEulerAnglesX(t.eulerAngles.x + f);
    }
    public static void AddEulerAnglesY(this Transform t, float f){
        t.SetEulerAnglesY(t.eulerAngles.y + f);
    }
    public static void AddEulerAnglesZ(this Transform t, float f){
        t.SetEulerAnglesZ(t.eulerAngles.z + f);
    }

    // localEulerAngles
    public static void SetLocalEulerAnglesX(this Transform t, float f){
        t.localEulerAngles = t.localEulerAngles.SetX(f);
    }
    public static void SetLocalEulerAnglesY(this Transform t, float f){
        t.localEulerAngles = t.localEulerAngles.SetY(f);
    }
    public static void SetLocalEulerAnglesZ(this Transform t, float f){
        t.localEulerAngles = t.localEulerAngles.SetZ(f);
    }

    public static void AddLocalEulerAnglesX(this Transform t, float f){
        t.SetLocalEulerAnglesX(t.localEulerAngles.x + f);
    }
    public static void AddLocalEulerAnglesY(this Transform t, float f){
        t.SetLocalEulerAnglesY(t.localEulerAngles.y + f);
    }
    public static void AddLocalEulerAnglesZ(this Transform t, float f){
        t.SetLocalEulerAnglesZ(t.localEulerAngles.z + f);
    }

    // localScale
    public static void SetLocalScaleX(this Transform t, float f){
        t.localScale = t.localScale.SetX(f);
    }
    public static void SetLocalScaleY(this Transform t, float f){
        t.localScale = t.localScale.SetY(f);
    }
    public static void SetLocalScaleZ(this Transform t, float f){
        t.localScale = t.localScale.SetZ(f);
    }

    public static void MulLocalScaleX(this Transform t, float f){
        t.localScale = t.localScale.MulX(f);
    }
    public static void MulLocalScaleY(this Transform t, float f){
        t.localScale = t.localScale.MulY(f);
    }
    public static void MulLocalScaleZ(this Transform t, float f){
        t.localScale = t.localScale.MulZ(f);
    }

    public static void AddLocalScale(this Transform t, float f){
        t.localScale += Vector3.one * f;
    }
    public static void SetLocalScale(this Transform t, float f){
        t.localScale = Vector3.one * f;
    }


    public static bool NearCheckX(this Transform t1, Transform t2, float max){
        return t1.position.NearCheckX(t2.position, max);
    }
    public static bool NearCheckY(this Transform t1, Transform t2, float max){
        return t1.position.NearCheckY(t2.position, max);
    }
    public static bool NearCheckZ(this Transform t1, Transform t2, float max){
        return t1.position.NearCheckZ(t2.position, max);
    }
    public static bool NearCheck(this Transform t1, Transform t2, float max){
        return t1.position.NearCheck(t2.position, max);
    }
    public static bool NearCheck(this Transform t, Vector3 p, float max){
        return t.position.NearCheck(p, max);
    }

    public static bool LocalNearCheckX(this Transform t1, Transform t2, float max){
        return t1.localPosition.NearCheckX(t2.localPosition, max);
    }
    public static bool LocalNearCheckY(this Transform t1, Transform t2, float max){
        return t1.localPosition.NearCheckY(t2.localPosition, max);
    }
    public static bool LocalNearCheckZ(this Transform t1, Transform t2, float max){
        return t1.localPosition.NearCheckZ(t2.localPosition, max);
    }
    public static bool LocalNearCheck(this Transform t1, Transform t2, float max){
        return t1.localPosition.NearCheck(t2.localPosition, max);
    }
    public static bool LocalNearCheck(this Transform t, Vector3 p, float max){
        return t.localPosition.NearCheck(p, max);
    }

    public static void AddChild(this Transform t, Transform child){
        child.SetParent(t);
        child.localPosition = Vector3.zero;
        child.localEulerAngles = Vector3.zero;
        child.localScale = Vector3.one;
    }

    public static void SetParentKeepState(this Transform t, Transform parent){
        parent.AddChildKeepState(t);
    }
    public static void AddChildKeepState(this Transform t, Transform child){
        Vector3 position = child.position;
        Vector3 angles = child.eulerAngles;
        Vector3 scale = child.localScale;
        child.SetParent(t);
        child.position = position;
        child.eulerAngles = angles;
        child.localScale = scale;
    }

    public static bool FindAllParentsComponent<T>(this Transform t, Action<T> func) {
        var parent = t.parent;
        if(parent != null){
            var component = parent.GetComponent<T>();
            var result = false;
            if(!System.Object.ReferenceEquals(component, null)){
                func(component);
                result = true;
            }
            return parent.FindAllParentsComponent<T>(func) || result;
        }else{
            return false;
        }
    }
    public static bool FindParentsComponent<T>(this Transform t, Action<T> func) {
        var parent = t.parent;
        if(parent != null){
            var component = parent.GetComponent<T>();
            if(!System.Object.ReferenceEquals(component, null)){
                func(component);
                return true;
            }else{
                return parent.FindParentsComponent<T>(func);
            }
        }else{
            return false;
        }
    }
    public static bool FindParentByName(this Transform t, string name, Action<Transform> func){
        var parent = t.parent;
        if(parent == null){
            return false;
        }else{
            if(parent.name == name){
                func(parent);
                return true;
            }else{
                return parent.FindParentByName(name, func);
            }
        }
    }
    public static bool FindParentByContainsName(this Transform t, string name, Action<Transform> func){
        var parent = t.parent;
        if(parent == null){
            return false;
        }else{
            if(parent.name.Contains(name)){
                func(parent);
                return true;
            }else{
                return parent.FindParentByContainsName(name, func);
            }
        }
    }
    /// GetComponentで取得出来ればfuncに渡す
    public static bool FindComponent<T>(this Transform t, Action<T> func) {
        T component = t.GetComponent<T>();
        if(component is UnityEngine.Object){
            if((component as UnityEngine.Object) != null){
                func(component);
                return true;
            }else{
                return false;
            }
        }else{
            if(component != null){
                func(component);
                return true;
            }else{
                return false;
            }
        }
    }
    public static bool FindComponents<T>(this Transform t, Action<T> func){
        var result = false;
        var components = t.GetComponents<T>();
        foreach(var component in components){
            if(component is UnityEngine.Object){
                if((component as UnityEngine.Object) != null){
                    func(component);
                    result = true;
                }
            }else{
                if(component != null){
                    func(component);
                    result = true;
                }
            }
        }
        return result;
    }
    /// 子オブジェクト達の中からGetComponentで最初に取得出来たものだけをfuncに渡す
    public static bool FindChildComponent<T>(this Transform t, Action<T> func) {
        bool result = false;
        for(int i = 0; i < t.childCount; ++i){
            Transform transform = t.GetChild(i);
            if(transform.FindComponent<T>(func)){
                result = true;
                break;
            }
        }
        return result;
    }
    /// 子オブジェクト達の中からGetComponentで取得出来たもの全てをfuncに渡す
    public static bool FindAllChildComponent<T>(this Transform t, Action<T> func) {
        bool result = false;
        for(int i = 0; i < t.childCount; ++i){
            Transform transform = t.GetChild(i);
            if(transform.FindComponent<T>(func)){
                result = true;
            }
        }
        return result;
    }
    /// 下階層全てのオブジェクト達の中からGetComponentで最初に取得出来たものだけをfuncに渡す
    public static bool FindChildrensComponent<T>(this Transform t, Action<T> func) {
        for(int i = 0; i < t.childCount; ++i){
            Transform transform = t.GetChild(i);
            if(transform.FindComponent<T>(func)){
                return true;
            }
        }
        for(int i = 0; i < t.childCount; ++i){
            Transform transform = t.GetChild(i);
            if(transform.FindChildrensComponent<T>(func)){
                return true;
            }
        }
        return false;
    }
    /// 下階層全てのオブジェクト達の中からGetComponentで取得出来たもの全てをfuncに渡す
    public static bool FindAllChildrensComponent<T>(this Transform t, Action<T> func) {
        bool result = false;
        for(int i = 0; i < t.childCount; ++i){
            Transform transform = t.GetChild(i);
            if(transform.FindComponents<T>(func)){
                result = true;
            }
            if(transform.FindAllChildrensComponent<T>(func)){
                result = true;
            }
        }
        return result;
    }

    /// 子オブジェクト達の中から同じ名前で最初に取得出来たものだけをfuncに渡す
    public static bool FindChildByName(this Transform t, string name, Action<Transform> func){
        bool result = false;
        for(int i = 0; i < t.childCount; ++i){
            Transform child = t.GetChild(i);
            if(child.name == name){
                func(child);
                result = true;
                break;
            }
        }
        return result;
    }
    public static bool FindChildrensByName(this Transform t, string name, Action<Transform> func){
        for(int i = 0; i < t.childCount; ++i){
            Transform child = t.GetChild(i);
            if(child.name == name){
                func(child);
                return true;
            }
        }
        for(int i = 0; i < t.childCount; ++i){
            Transform child = t.GetChild(i);
            if(child.FindChildrensByName(name, func)){
                return true;
            }
        }
        return false;
    }

    public static void AllChildrens(this Transform t, Action<Transform> func){
        for(int i = 0; i < t.childCount; ++i){
            Transform child = t.GetChild(i);
            func(child);
            child.AllChildrens(func);
        }
    }
    public static void DestroyAllChildrens(this Transform t){
        while(t.childCount > 0){
            GameObject.DestroyImmediate(t.GetChild(0).gameObject);
        }
    }

    public static void SetLayer(this Transform t, string layerName){
        t.SetLayer(LayerMask.NameToLayer(layerName));
    }
    public static void SetLayer(this Transform t, int layer){
        t.gameObject.layer = layer;
        t.AllChildrens(c => {
            c.gameObject.layer = layer;
        });
    }

    public static Bounds TransformBounds(this Transform t, Bounds bounds)
    {
        var ret = new Bounds();
        ret.center = t.TransformPoint(bounds.center);
        var max = t.TransformPoint(bounds.max);
        var size = (max - ret.center) * 2;
        ret.size = size;
        return ret;
    }
    public static Bounds InverseTransformBounds(this Transform t, Bounds bounds)
    {
        var ret = new Bounds();
        ret.center = t.InverseTransformPoint(bounds.center);
        var max = t.InverseTransformPoint(bounds.max);
        var size = (max - ret.center) * 2;
        ret.size = size;
        return ret;
    }

    public static string GetPath(this Transform t){
        var path = t.gameObject.name;
        var parent = t.parent;
        while(parent != null){
            path = parent.name + "/" + path;
            parent = parent.parent;
        }
        return path;
    }
}
