namespace UnityEngine.Rendering
{
    internal static class ProbeVolumePositioning
    {
        internal static Vector3[] m_Axes = new Vector3[6];
        internal static Vector3[] m_AABBCorners = new Vector3[8];

        public static bool OBBIntersect(in ProbeReferenceVolume.Volume a, in ProbeReferenceVolume.Volume b)
        {
            // First we test if the bounding spheres intersects, in which case we case do the more complex OBB test
            a.CalculateCenterAndSize(out var aCenter, out var aSize);
            b.CalculateCenterAndSize(out var bCenter, out var bSize);

            var aRadius = aSize.sqrMagnitude / 2.0f;
            var bRadius = bSize.sqrMagnitude / 2.0f;
            if (Vector3.SqrMagnitude(aCenter - bCenter) > aRadius + bRadius)
                return false;

            m_Axes[0] = a.X.normalized;
            m_Axes[1] = a.Y.normalized;
            m_Axes[2] = a.Z.normalized;
            m_Axes[3] = b.X.normalized;
            m_Axes[4] = b.Y.normalized;
            m_Axes[5] = b.Z.normalized;

            for (int i = 0; i < 6; i++)
            {
                Vector2 aProj = ProjectOBB(in a, m_Axes[i]);
                Vector2 bProj = ProjectOBB(in b, m_Axes[i]);

                if (aProj.y < bProj.x || bProj.y < aProj.x)
                {
                    return false;
                }
            }

            return true;
        }

        public static bool OBBContains(in ProbeReferenceVolume.Volume obb, Vector3 point)
        {
            float lenX2 = obb.X.sqrMagnitude;
            float lenY2 = obb.Y.sqrMagnitude;
            float lenZ2 = obb.Z.sqrMagnitude;

            // Project in OBB space
            point -= obb.corner;
            point = new Vector3(Vector3.Dot(point, obb.X), Vector3.Dot(point, obb.Y), Vector3.Dot(point, obb.Z));

            return (0.0f < point.x && point.x < lenX2) && (0.0f < point.y && point.y < lenY2) && (0.0f < point.z && point.z < lenZ2);
        }

        // Test between a OBB and an AABB. The AABB of the OBB is requested to avoid recalculating it
        public static bool OBBAABBIntersect(in ProbeReferenceVolume.Volume a, in Bounds b, in Bounds aAABB)
        {
            // First perform fast AABB test
            if (!aAABB.Intersects(b))
                return false;

            // Perform complex OBB test
            Vector3 boundsMin = b.min, boundsMax = b.max;
            m_AABBCorners[0] = new Vector3(boundsMin.x, boundsMin.y, boundsMin.z);
            m_AABBCorners[1] = new Vector3(boundsMax.x, boundsMin.y, boundsMin.z);
            m_AABBCorners[2] = new Vector3(boundsMax.x, boundsMax.y, boundsMin.z);
            m_AABBCorners[3] = new Vector3(boundsMin.x, boundsMax.y, boundsMin.z);
            m_AABBCorners[4] = new Vector3(boundsMin.x, boundsMin.y, boundsMax.z);
            m_AABBCorners[5] = new Vector3(boundsMax.x, boundsMin.y, boundsMax.z);
            m_AABBCorners[6] = new Vector3(boundsMax.x, boundsMax.y, boundsMax.z);
            m_AABBCorners[7] = new Vector3(boundsMin.x, boundsMax.y, boundsMax.z);

            m_Axes[0] = a.X.normalized;
            m_Axes[1] = a.Y.normalized;
            m_Axes[2] = a.Z.normalized;

            for (int i = 0; i < 3; i++)
            {
                Vector2 aProj = ProjectOBB(in a, m_Axes[i]);
                Vector2 bProj = ProjectAABB(m_AABBCorners, m_Axes[i]);

                if (aProj.y < bProj.x || bProj.y < aProj.x)
                {
                    return false;
                }
            }

            return true;
        }

        static Vector2 ProjectOBB(in ProbeReferenceVolume.Volume a, Vector3 axis)
        {
            float min = Vector3.Dot(axis, a.corner);
            float max = min;

            for (int x = 0; x < 2; x++)
            {
                for (int y = 0; y < 2; y++)
                {
                    for (int z = 0; z < 2; z++)
                    {
                        Vector3 vert = a.corner + a.X * x + a.Y * y + a.Z * z;

                        float proj = Vector3.Dot(axis, vert);

                        if (proj < min)
                        {
                            min = proj;
                        }
                        else if (proj > max)
                        {
                            max = proj;
                        }
                    }
                }
            }

            return new Vector2(min, max);
        }

        static Vector2 ProjectAABB(in Vector3[] corners, Vector3 axis)
        {
            float min = Vector3.Dot(axis, corners[0]);
            float max = min;
            for (int i = 1; i < 8; i++)
            {
                float proj = Vector3.Dot(axis, corners[i]);
                if (proj < min) min = proj;
                else if (proj > max)  max = proj;
            }

            return new Vector2(min, max);
        }
    }
}