using System;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(VelocityTracker))]
public class VRGrabber : MonoBehaviour
{
	public void ForceRelease(VRGrabbable grabbable)
	{
		bool flag = this.m_grabbedObj != null && this.m_grabbedObj == grabbable;
		if (flag)
		{
			this.GrabEnd();
		}
	}

	public void SetGripTransform(string name)
	{
		for (int i = 0; i < this.gripTransforms.Length; i++)
		{
			if (this.gripTransforms[i].name == name)
			{
				this.gripTransform = this.gripTransforms[i].transform;
				break;
			}
		}
	}

	private void Awake()
	{
		this.m_anchorOffsetPosition = base.transform.localPosition;
		this.m_anchorOffsetRotation = base.transform.localRotation;
		if (this.gripTransform == null)
		{
			this.gripTransform = this.gripTransforms[0].transform;
		}
	}

	private void Start()
	{
		this.m_velocityTracker = base.GetComponent<VelocityTracker>();
		this.m_lastPos = base.transform.position;
		this.m_lastRot = base.transform.rotation;
		this.m_parentTransform = base.gameObject.transform.parent.transform;
	}

	private void Update()
	{
		if (this.m_vrController == null)
		{
			return;
		}
		if (this.m_vrController.GetPressDown(AVRControllerButtons.BTN.TRIGGER))
		{
			this.GrabBegin();
		}
		else if (this.m_vrController.GetPressUp(AVRControllerButtons.BTN.TRIGGER))
		{
			this.GrabEnd();
		}
	}

	private void FixedUpdate()
	{
		if (this.m_vrController == null)
		{
			return;
		}
		Vector3 position = this.m_vrController.transform.position;
		Quaternion rotation = this.m_vrController.transform.rotation;
		Vector3 vector = this.m_parentTransform.TransformPoint(this.m_anchorOffsetPosition + position);
		Quaternion rot = this.m_parentTransform.rotation * rotation;
		base.GetComponent<Rigidbody>().MovePosition(vector);
		base.GetComponent<Rigidbody>().MoveRotation(rot);
		if (!this.m_useFixedJointForGrabbedObject && !this.m_parentHeldObject)
		{
			this.MoveGrabbedObject(vector, rot, false);
		}
		this.m_lastPos = base.transform.position;
		this.m_lastRot = base.transform.rotation;
	}

	private void OnDestroy()
	{
		if (this.m_grabbedObj != null)
		{
			this.GrabEnd();
		}
	}

	private void OnTriggerEnter(Collider otherCollider)
	{
		VRGrabbable vrgrabbable = otherCollider.GetComponent<VRGrabbable>() ?? otherCollider.GetComponentInParent<VRGrabbable>();
		if (vrgrabbable == null)
		{
			return;
		}
		int num = 0;
		this.m_grabCandidates.TryGetValue(vrgrabbable, out num);
		this.m_grabCandidates[vrgrabbable] = num + 1;
	}

	private void OnTriggerExit(Collider otherCollider)
	{
		VRGrabbable vrgrabbable = otherCollider.GetComponent<VRGrabbable>() ?? otherCollider.GetComponentInParent<VRGrabbable>();
		if (vrgrabbable == null)
		{
			return;
		}
		int num = 0;
		if (!this.m_grabCandidates.TryGetValue(vrgrabbable, out num))
		{
			return;
		}
		if (num > 1)
		{
			this.m_grabCandidates[vrgrabbable] = num - 1;
		}
		else
		{
			this.m_grabCandidates.Remove(vrgrabbable);
		}
	}

	private void CheckForGrabOrRelease(float prevFlex)
	{
		if (this.m_prevFlex >= 0.55f && prevFlex < 0.55f)
		{
			this.GrabBegin();
		}
		else if (this.m_prevFlex <= 0.35f && prevFlex > 0.35f)
		{
			this.GrabEnd();
		}
	}

	public void GrabBegin(VRGrabbable grabbable)
	{
		if (grabbable == null || this.m_grabbedObj != null || grabbable.GrabPoints == null || grabbable.GrabPoints.Length <= 0)
		{
			return;
		}
		this.OnTriggerEnter(grabbable.GrabPoints[0]);
		this.GrabVolumeEnable(true);
		this.GrabBegin();
	}

	private void GrabBegin()
	{
		float num = float.MaxValue;
		VRGrabbable vrgrabbable = null;
		Collider grabPoint = null;
		foreach (VRGrabbable vrgrabbable2 in this.m_grabCandidates.Keys)
		{
			if (!vrgrabbable2.IsGrabbed || vrgrabbable2.AllowOffhandGrab)
			{
				for (int i = 0; i < vrgrabbable2.GrabPoints.Length; i++)
				{
					Collider collider = vrgrabbable2.GrabPoints[i];
					Vector3 b = collider.ClosestPointOnBounds(this.gripTransform.position);
					float sqrMagnitude = (this.gripTransform.position - b).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						vrgrabbable = vrgrabbable2;
						grabPoint = collider;
					}
				}
			}
		}
		this.GrabVolumeEnable(false);
		if (vrgrabbable != null)
		{
			if (vrgrabbable.IsGrabbed)
			{
				vrgrabbable.GrabbedBy.OffhandGrabbed(vrgrabbable);
			}
			this.m_grabbedObj = vrgrabbable;
			this.m_grabbedObj.GrabBegin(this, grabPoint);
			if (this.m_useFixedJointForGrabbedObject)
			{
				FixedJoint fixedJoint = base.gameObject.GetComponent<FixedJoint>() ?? base.gameObject.AddComponent<FixedJoint>();
				fixedJoint.connectedBody = this.m_grabbedObj.GetComponent<Rigidbody>();
			}
			else
			{
				this.m_lastPos = base.transform.position;
				this.m_lastRot = base.transform.rotation;
				if (this.m_grabbedObj.SnapPosition)
				{
					this.m_grabbedObjectPosOff = this.gripTransform.localPosition;
					if (this.m_grabbedObj.SnapOffset)
					{
						Vector3 localPosition = this.m_grabbedObj.SnapOffset.localPosition;
						if (this.m_vrController.HandSide)
						{
							localPosition.x = -localPosition.x;
						}
						this.m_grabbedObjectPosOff += localPosition;
					}
				}
				else
				{
					Vector3 vector = this.m_grabbedObj.transform.position - base.transform.position;
					vector = Quaternion.Inverse(base.transform.rotation) * vector;
					this.m_grabbedObjectPosOff = vector;
				}
				if (this.m_grabbedObj.SnapOrientation)
				{
					this.m_grabbedObjectRotOff = this.gripTransform.localRotation;
					if (this.m_grabbedObj.SnapOffset)
					{
						this.m_grabbedObjectRotOff = this.m_grabbedObj.SnapOffset.rotation * this.m_grabbedObjectRotOff;
					}
				}
				else
				{
					Quaternion grabbedObjectRotOff = Quaternion.Inverse(base.transform.rotation) * this.m_grabbedObj.transform.rotation;
					this.m_grabbedObjectRotOff = grabbedObjectRotOff;
				}
				this.MoveGrabbedObject(this.m_lastPos, this.m_lastRot, true);
				if (this.m_parentHeldObject)
				{
					this.m_grabbedObj.transform.parent = base.transform;
				}
			}
		}
	}

	private void MoveGrabbedObject(Vector3 pos, Quaternion rot, bool forceTeleport = false)
	{
		if (this.m_grabbedObj == null)
		{
			return;
		}
		if (this.m_grabbedObj.SnapPosition && this.m_frameUpdateSnapPosition)
		{
			this.m_grabbedObjectPosOff = this.gripTransform.localPosition;
			if (this.m_grabbedObj.SnapOffset)
			{
				Vector3 localPosition = this.m_grabbedObj.SnapOffset.localPosition;
				if (this.m_vrController.HandSide)
				{
					localPosition.x = -localPosition.x;
				}
				this.m_grabbedObjectPosOff += localPosition;
			}
			this.m_grabbedObjectRotOff = this.gripTransform.localRotation;
			if (this.m_grabbedObj.SnapOffset)
			{
				this.m_grabbedObjectRotOff = this.m_grabbedObj.SnapOffset.rotation * this.m_grabbedObjectRotOff;
			}
		}
		Rigidbody grabbedRigidbody = this.m_grabbedObj.GrabbedRigidbody;
		Vector3 position = pos + rot * this.m_grabbedObjectPosOff;
		Quaternion quaternion = rot * this.m_grabbedObjectRotOff;
		if (forceTeleport)
		{
			grabbedRigidbody.transform.position = position;
			grabbedRigidbody.transform.rotation = quaternion;
		}
		else
		{
			grabbedRigidbody.MovePosition(position);
			grabbedRigidbody.MoveRotation(quaternion);
		}
	}

	public void GrabEnd(Vector3 trackedLinearVelocity, Vector3 TrackedAngularVelocity)
	{
		if (this.m_grabbedObj != null)
		{
			this.VRGrabbableRelease(trackedLinearVelocity, TrackedAngularVelocity);
		}
		this.GrabVolumeEnable(true);
	}

	public void GrabEnd()
	{
		if (this.m_grabbedObj != null)
		{
			bool flag = this.m_velocityTracker.TrackedLinearVelocity.magnitude >= 1f;
			Vector3 linearVelocity = Vector3.zero;
			Vector3 angularVelocity = Vector3.zero;
			if (flag)
			{
				linearVelocity = this.m_velocityTracker.TrackedLinearVelocity;
				angularVelocity = this.m_velocityTracker.TrackedAngularVelocity;
			}
			else
			{
				linearVelocity = this.m_velocityTracker.FrameLinearVelocity;
				angularVelocity = this.m_velocityTracker.FrameAngularVelocity;
			}
			this.VRGrabbableRelease(linearVelocity, angularVelocity);
		}
		this.GrabVolumeEnable(true);
	}

	private void VRGrabbableRelease(Vector3 linearVelocity, Vector3 angularVelocity)
	{
		Debug.Log(linearVelocity.ToString("G"));
		Debug.Log(angularVelocity.ToString("G"));
		UnityEngine.Object.Destroy(base.gameObject.GetComponent<FixedJoint>());
		this.m_grabbedObj.GrabEnd(linearVelocity, angularVelocity);
		if (this.m_parentHeldObject)
		{
			this.m_grabbedObj.transform.parent = null;
		}
		this.m_grabbedObj = null;
	}

	private void GrabVolumeEnable(bool enabled)
	{
		if (this.m_grabVolumeEnabled == enabled)
		{
			return;
		}
		this.m_grabVolumeEnabled = enabled;
		for (int i = 0; i < this.m_grabVolumes.Length; i++)
		{
			Collider collider = this.m_grabVolumes[i];
			collider.enabled = this.m_grabVolumeEnabled;
		}
		if (!this.m_grabVolumeEnabled)
		{
			this.m_grabCandidates.Clear();
		}
	}

	private void OffhandGrabbed(VRGrabbable grabbable)
	{
		if (this.m_grabbedObj == grabbable)
		{
			this.VRGrabbableRelease(Vector3.zero, Vector3.zero);
		}
	}

	public const float THRESH_GRAB_BEGIN = 0.55f;

	public const float THRESH_GRAB_END = 0.35f;

	public const float THRESH_THROW_SPEED = 1f;

	[SerializeField]
	private bool m_useFixedJointForGrabbedObject;

	[SerializeField]
	private bool m_parentHeldObject;

	[SerializeField]
	private VRGrabber.GripTransformData[] gripTransforms;

	[SerializeField]
	private Collider[] m_grabVolumes;

	[SerializeField]
	public AVRControllerButtons m_vrController;

	[SerializeField]
	private bool m_frameUpdateSnapPosition;

	private VelocityTracker m_velocityTracker;

	private bool m_grabVolumeEnabled = true;

	private Vector3 m_lastPos;

	private Quaternion m_lastRot;

	private Quaternion m_anchorOffsetRotation;

	private Vector3 m_anchorOffsetPosition;

	private float m_prevFlex;

	private Transform m_parentTransform;

	private VRGrabbable m_grabbedObj;

	private Vector3 m_grabbedObjectPosOff;

	private Quaternion m_grabbedObjectRotOff;

	private Dictionary<VRGrabbable, int> m_grabCandidates = new Dictionary<VRGrabbable, int>();

	private Transform gripTransform;

	[Serializable]
	private struct GripTransformData
	{
		public Transform transform;

		public string name;
	}
}
