﻿/* Copyright (C) Itseez3D, Inc. - All Rights Reserved
* You may not use this file except in compliance with an authorized license
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* UNLESS REQUIRED BY APPLICABLE LAW OR AGREED BY ITSEEZ3D, INC. IN WRITING, SOFTWARE DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED
* See the License for the specific language governing permissions and limitations under the License.
* Written by Itseez3D, Inc. <support@avatarsdk.com>, June 2026
*/

using UnityEngine;

namespace AvatarSDKMove
{
	public static class ColliderUtility
	{
		// We reuse a very small sphere collider to act as a "point" for penetration tests.
		private static GameObject _proxyObject;
		private static SphereCollider _proxySphere;

		// Static constructor ensures the proxy exists exactly once.
		static ColliderUtility()
		{
			_proxyObject = new GameObject("ColliderUtility_ProxySphere")
			{
				hideFlags = HideFlags.HideAndDontSave
			};
			_proxySphere = _proxyObject.AddComponent<SphereCollider>();
			_proxySphere.radius = 0.0001f; // extremely small – practically a point
			_proxySphere.isTrigger = false; // irrelevant for ComputePenetration
			Object.DontDestroyOnLoad(_proxyObject);
		}

		/// <summary>
		/// If the transform is inside the collider, its position is set to the nearest point on the collider’s surface.
		/// Otherwise it remains unchanged.
		/// </summary>
		/// <param name="collider">The collider to test against (primitives / convex MeshCollider).</param>
		/// <param name="targetTransform">The transform to adjust if inside.</param>
		public static void ClampToSurface(Collider collider, Transform targetTransform)
		{
			if (collider == null || targetTransform == null)
			{
				Debug.LogError("Collider and Transform must not be null.");
				return;
			}

			Vector3 point = targetTransform.position;

			// Place the proxy sphere at the exact position we want to test.
			_proxyObject.transform.position = point;
			_proxyObject.transform.rotation = Quaternion.identity;

			// ComputePenetration returns true if the two colliders overlap.
			// It does not depend on layer collision settings – it’s pure geometry.
			bool penetrating = Physics.ComputePenetration(
				_proxySphere, _proxyObject.transform.position, _proxyObject.transform.rotation,
				collider, collider.transform.position, collider.transform.rotation,
				out Vector3 direction, out float distance
			);

			if (penetrating)
			{
				// Move the transform along the separation direction by the penetration distance.
				// This places it exactly on the surface (the shortest push-out).
				targetTransform.position += direction * distance;
			}
			// If not penetrating, the point is outside → do nothing.
		}
	}
}