#if UNITY_EDITOR
namespace NiAkaDev.EzMirror.Editor
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using UdonSharpEditor;
    using UnityEditor;
    using UnityEngine;
    using UnityEngine.UI;
    using VRC.Dynamics;
    using VRC.SDK3.Components;
    using VRC.SDK3.Dynamics.Contact.Components;
    using VRC.SDKBase;
    using VRC.Udon;

    public partial class EzMirrorEditor : UnityEditor.Editor
    {
        [DrawGizmo(GizmoType.Active | GizmoType.Selected | GizmoType.NonSelected)]
        private static void DrawSelectedGizmos(EzMirror mirror, GizmoType gizmoType)
        {
            if (!IsEzMirrorOrDescendantSelected(mirror))
                return;

            BoxCollider trigger = mirror.triggerCollider != null
                ? mirror.triggerCollider
                : mirror.GetComponentInChildren<BoxCollider>(true);
            if (trigger == null)
                return;

            Matrix4x4 oldMatrix = Gizmos.matrix;
            Color oldColor = Gizmos.color;

            DrawTriggerBoundaryGizmo(trigger);
            DrawFadeGizmos(mirror, trigger);

            Gizmos.color = oldColor;
            Gizmos.matrix = oldMatrix;
        }

        private static bool IsEzMirrorOrDescendantSelected(EzMirror mirror)
        {
            if (mirror == null)
                return false;

            Transform[] selectedTransforms = Selection.transforms;
            if (selectedTransforms == null)
                return false;

            Transform mirrorTransform = mirror.transform;
            for (int i = 0; i < selectedTransforms.Length; i++)
            {
                if (IsSameOrChildOf(selectedTransforms[i], mirrorTransform))
                    return true;
            }

            return false;
        }

        private static bool IsSameOrChildOf(Transform candidate, Transform root)
        {
            Transform current = candidate;
            while (current != null)
            {
                if (current == root)
                    return true;
                current = current.parent;
            }

            return false;
        }

        private static void DrawTriggerBoundaryGizmo(BoxCollider trigger)
        {
            Gizmos.matrix = trigger.transform.localToWorldMatrix;
            Gizmos.color = new Color(0.2f, 0.8f, 1f, 0.55f);
            Gizmos.DrawWireCube(trigger.center, trigger.size);
        }

        private static void DrawFadeGizmos(EzMirror mirror, BoxCollider trigger)
        {
            Transform fadeTransform = GetFadeReferenceTransform(mirror, trigger);
            Gizmos.matrix = Matrix4x4.TRS(fadeTransform.position, fadeTransform.rotation, Vector3.one);
            if (!TryCalculateTriggerFadeGizmoBounds(trigger, fadeTransform, out Vector3 gizmoCenter, out Vector3 gizmoSize))
                return;
            if (
                !TryGetCommonFadeRanges(
                    mirror.profileMaterials,
                    out float distanceRange,
                    out float horizontalRange
                )
            )
                return;
            if (
                !TryCalculateTriggerAlignedFadeDistances(
                    trigger,
                    fadeTransform,
                    distanceRange,
                    horizontalRange,
                    out float distanceStart,
                    out float distanceComplete,
                    out float horizontalStart,
                    out float horizontalComplete
                )
            )
                return;
            TryCalculateTriggerAlignedHorizontalFadeCenter(
                trigger,
                fadeTransform,
                out float horizontalCenter
            );

            DrawFadePlanes(gizmoCenter, gizmoSize, distanceStart, distanceComplete);
            DrawHorizontalFadePlanes(gizmoCenter, gizmoSize, horizontalCenter, horizontalStart, horizontalComplete);
        }

        private static Transform GetFadeReferenceTransform(EzMirror mirror, BoxCollider fallback)
        {
            if (mirror.profileRenderer != null)
                return mirror.profileRenderer.transform;
            if (mirror.profileMirror != null)
                return mirror.profileMirror.transform;
            return fallback.transform;
        }

        private static void GetTriggerBoundsInReferenceSpace(
            BoxCollider trigger,
            Transform fadeReference,
            out float minX,
            out float maxX,
            out float minY,
            out float maxY,
            out float minZ,
            out float maxZ
        )
        {
            Vector3 half = trigger.size * 0.5f;
            minX = float.PositiveInfinity;
            maxX = float.NegativeInfinity;
            minY = float.PositiveInfinity;
            maxY = float.NegativeInfinity;
            minZ = float.PositiveInfinity;
            maxZ = float.NegativeInfinity;

            for (int x = -1; x <= 1; x += 2)
            {
                for (int y = -1; y <= 1; y += 2)
                {
                    for (int z = -1; z <= 1; z += 2)
                    {
                        Vector3 localCorner = trigger.center + new Vector3(
                            half.x * x,
                            half.y * y,
                            half.z * z
                        );
                        Vector3 referenceCorner = ProjectWorldPointToFadeReference(
                            trigger.transform.TransformPoint(localCorner),
                            fadeReference
                        );
                        minX = Mathf.Min(minX, referenceCorner.x);
                        maxX = Mathf.Max(maxX, referenceCorner.x);
                        minY = Mathf.Min(minY, referenceCorner.y);
                        maxY = Mathf.Max(maxY, referenceCorner.y);
                        minZ = Mathf.Min(minZ, referenceCorner.z);
                        maxZ = Mathf.Max(maxZ, referenceCorner.z);
                    }
                }
            }
        }

        private static Vector3 ProjectWorldPointToFadeReference(
            Vector3 worldPoint,
            Transform fadeReference
        )
        {
            Vector3 delta = worldPoint - fadeReference.position;
            return new Vector3(
                Vector3.Dot(delta, GetNormalizedReferenceAxis(fadeReference, Vector3.right)),
                Vector3.Dot(delta, GetNormalizedReferenceAxis(fadeReference, Vector3.up)),
                Vector3.Dot(delta, GetNormalizedReferenceAxis(fadeReference, Vector3.forward))
            );
        }

        private static Vector3 GetNormalizedReferenceAxis(Transform transform, Vector3 localAxis)
        {
            Vector3 axis = transform.localToWorldMatrix.MultiplyVector(localAxis);
            if (axis.sqrMagnitude <= 0.000001f)
                return localAxis;
            return axis.normalized;
        }

        private static void DrawFadePlanes(
            Vector3 triggerCenter,
            Vector3 triggerSize,
            float fadeStart,
            float fadeComplete
        )
        {
            Color startColor = new Color(0.25f, 0.9f, 0.35f, 0.85f);
            Color endColor = new Color(0.05f, 0.65f, 0.18f, 0.85f);
            DrawLocalZPlane(triggerCenter, -fadeStart, triggerSize.x, triggerSize.y, startColor, "前後フェード開始");
            DrawLocalZPlane(triggerCenter, -fadeComplete, triggerSize.x, triggerSize.y, endColor, "前後フェード終了（外周）");
        }

        private static void DrawHorizontalFadePlanes(
            Vector3 triggerCenter,
            Vector3 triggerSize,
            float fadeCenter,
            float fadeStart,
            float fadeComplete
        )
        {
            Color startColor = new Color(1f, 0.62f, 0.12f, 0.85f);
            Color endColor = new Color(0.9f, 0.32f, 0.02f, 0.85f);
            DrawLocalXPlane(triggerCenter, fadeCenter - fadeStart, triggerSize.z, triggerSize.y, startColor, "左フェード開始");
            DrawLocalXPlane(triggerCenter, fadeCenter + fadeStart, triggerSize.z, triggerSize.y, startColor, "右フェード開始");
            DrawLocalXPlane(triggerCenter, fadeCenter - fadeComplete, triggerSize.z, triggerSize.y, endColor, "左フェード終了（外周）");
            DrawLocalXPlane(triggerCenter, fadeCenter + fadeComplete, triggerSize.z, triggerSize.y, endColor, "右フェード終了（外周）");
        }

        private static void DrawLocalZPlane(
            Vector3 boxCenter,
            float z,
            float width,
            float height,
            Color color,
            string label
        )
        {
            Gizmos.color = color;
            Vector3 center = new Vector3(boxCenter.x, boxCenter.y, z);
            Vector3 size = new Vector3(Mathf.Max(0.01f, width), Mathf.Max(0.01f, height), 0.01f);
            Gizmos.DrawWireCube(center, size);
            Handles.color = color;
            Handles.Label(Gizmos.matrix.MultiplyPoint(center), label);
        }

        private static void DrawLocalXPlane(
            Vector3 boxCenter,
            float x,
            float depth,
            float height,
            Color color,
            string label
        )
        {
            Gizmos.color = color;
            Vector3 center = new Vector3(x, boxCenter.y, boxCenter.z);
            Vector3 size = new Vector3(0.01f, Mathf.Max(0.01f, height), Mathf.Max(0.01f, depth));
            Gizmos.DrawWireCube(center, size);
            Handles.color = color;
            Handles.Label(Gizmos.matrix.MultiplyPoint(center), label);
        }
    }
}
#endif
