using System;
using System.Collections;
using Leap.Unity.Attributes;
using UnityEngine;

namespace Leap.Unity
{
	public class LeapServiceProvider : LeapProvider
	{
		public override Frame CurrentFrame
		{
			get
			{
				return this._transformedUpdateFrame;
			}
		}

		public override Image CurrentImage
		{
			get
			{
				return this._currentImage;
			}
		}

		public override Frame CurrentFixedFrame
		{
			get
			{
				if (this._reuseFramesForPhysics)
				{
					return this._transformedUpdateFrame;
				}
				return this._transformedFixedFrame;
			}
		}

		public bool UseInterpolation
		{
			get
			{
				return this._useInterpolation;
			}
			set
			{
				this._useInterpolation = value;
			}
		}

		public long InterpolationDelay
		{
			get
			{
				return this._interpolationDelay;
			}
			set
			{
				this._interpolationDelay = value;
			}
		}

		public Controller GetLeapController()
		{
			return this.leap_controller_;
		}

		public bool IsConnected()
		{
			return this.GetLeapController().IsConnected;
		}

		public LeapDeviceInfo GetDeviceInfo()
		{
			if (this._overrideDeviceType)
			{
				return new LeapDeviceInfo(this._overrideDeviceTypeWith);
			}
			DeviceList devices = this.GetLeapController().Devices;
			if (devices.Count == 1)
			{
				LeapDeviceInfo result = new LeapDeviceInfo(LeapDeviceType.Peripheral);
				if (devices[0].SerialNumber.Length >= 2)
				{
					string text = devices[0].SerialNumber.Substring(0, 2);
					if (text != null)
					{
						if (!(text == "LP"))
						{
							if (text == "LE")
							{
								result = new LeapDeviceInfo(LeapDeviceType.Dragonfly);
							}
						}
						else
						{
							result = new LeapDeviceInfo(LeapDeviceType.Peripheral);
						}
					}
				}
				result.isEmbedded = devices[0].IsEmbedded;
				result.horizontalViewAngle = devices[0].HorizontalViewAngle * 57.29578f;
				result.verticalViewAngle = devices[0].VerticalViewAngle * 57.29578f;
				result.trackingRange = devices[0].Range / 1000f;
				result.serialID = devices[0].SerialNumber;
				return result;
			}
			if (devices.Count > 1)
			{
				return new LeapDeviceInfo(LeapDeviceType.Peripheral);
			}
			return new LeapDeviceInfo(LeapDeviceType.Invalid);
		}

		public void ReTransformFrames()
		{
			this.transformFrame(this._untransformedUpdateFrame, this._transformedUpdateFrame);
			this.transformFrame(this._untransformedFixedFrame, this._transformedFixedFrame);
		}

		protected virtual void Awake()
		{
			this.clockCorrelator = new ClockCorrelator();
			this._fixedOffset.delay = 0.4f;
		}

		protected virtual void Start()
		{
			this.createController();
			this._transformedUpdateFrame = new Frame();
			this._transformedFixedFrame = new Frame();
			this._untransformedUpdateFrame = new Frame();
			this._untransformedFixedFrame = new Frame();
			base.StartCoroutine(this.waitCoroutine());
		}

		protected IEnumerator waitCoroutine()
		{
			WaitForEndOfFrame endWaiter = new WaitForEndOfFrame();
			for (;;)
			{
				yield return endWaiter;
				long unityTime = (long)((double)Time.time * 1000000.0);
				this.clockCorrelator.UpdateRebaseEstimate(unityTime);
			}
			yield break;
		}

		protected virtual void Update()
		{
			this._fixedOffset.Update(Time.time - Time.fixedTime, Time.deltaTime);
			if (this._useInterpolation)
			{
				long num = (long)((double)Time.time * 1000000.0);
				long applicationClock = num - this._interpolationDelay * 1000L;
				long time = this.clockCorrelator.ExternalClockToLeapTime(applicationClock);
				this.leap_controller_.GetInterpolatedFrame(this._untransformedUpdateFrame, time);
			}
			else
			{
				this.leap_controller_.Frame(this._untransformedUpdateFrame);
			}
			if (this._untransformedUpdateFrame != null)
			{
				this.transformFrame(this._untransformedUpdateFrame, this._transformedUpdateFrame);
				base.DispatchUpdateFrameEvent(this._transformedUpdateFrame);
			}
		}

		protected virtual void FixedUpdate()
		{
			if (this._reuseFramesForPhysics)
			{
				base.DispatchFixedFrameEvent(this._transformedUpdateFrame);
				return;
			}
			if (this._useInterpolation)
			{
				long num = (long)((double)(Time.fixedTime + this._fixedOffset.value) * 1000000.0);
				long applicationClock = num - this._interpolationDelay * 1000L;
				long time = this.clockCorrelator.ExternalClockToLeapTime(applicationClock);
				this.leap_controller_.GetInterpolatedFrame(this._untransformedFixedFrame, time);
			}
			else
			{
				this.leap_controller_.Frame(this._untransformedFixedFrame);
			}
			if (this._untransformedFixedFrame != null)
			{
				this.transformFrame(this._untransformedFixedFrame, this._transformedFixedFrame);
				base.DispatchFixedFrameEvent(this._transformedFixedFrame);
			}
		}

		protected virtual void OnDestroy()
		{
			this.destroyController();
		}

		protected virtual void OnApplicationPause(bool isPaused)
		{
			if (this.leap_controller_ != null)
			{
				if (isPaused)
				{
					this.leap_controller_.StopConnection();
				}
				else
				{
					this.leap_controller_.StartConnection();
				}
			}
		}

		protected virtual void OnApplicationQuit()
		{
			this.destroyController();
		}

		protected void initializeFlags()
		{
			if (this.leap_controller_ == null)
			{
				return;
			}
			if (this._isHeadMounted)
			{
				this.leap_controller_.SetPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_HMD);
			}
			else
			{
				this.leap_controller_.ClearPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_HMD);
			}
		}

		protected void createController()
		{
			if (this.leap_controller_ != null)
			{
				this.destroyController();
			}
			this.leap_controller_ = new Controller();
			if (this.leap_controller_.IsConnected)
			{
				this.initializeFlags();
			}
			else
			{
				this.leap_controller_.Device += new EventHandler<DeviceEventArgs>(this.onHandControllerConnect);
			}
		}

		protected void destroyController()
		{
			if (this.leap_controller_ != null)
			{
				if (this.leap_controller_.IsConnected)
				{
					this.leap_controller_.ClearPolicy(Controller.PolicyFlag.POLICY_OPTIMIZE_HMD);
				}
				this.leap_controller_.StopConnection();
				this.leap_controller_ = null;
			}
		}

		protected void onHandControllerConnect(object sender, LeapEventArgs args)
		{
			this.initializeFlags();
			this.leap_controller_.Device -= new EventHandler<DeviceEventArgs>(this.onHandControllerConnect);
		}

		protected void transformFrame(Frame source, Frame dest)
		{
			LeapTransform leapMatrix;
			if (this._temporalWarping != null)
			{
				Vector3 vector;
				Quaternion quaternion;
				this._temporalWarping.TryGetWarpedTransform(LeapVRTemporalWarping.WarpedAnchor.CENTER, out vector, out quaternion, source.Timestamp);
				quaternion *= base.transform.localRotation;
				leapMatrix = new LeapTransform(vector.ToVector(), quaternion.ToLeapQuaternion(), base.transform.lossyScale.ToVector() * 0.001f);
				leapMatrix.MirrorZ();
			}
			else
			{
				leapMatrix = base.transform.GetLeapMatrix();
			}
			dest.CopyFrom(source).Transform(leapMatrix);
		}

		protected const float NS_TO_S = 1E-06f;

		protected const float S_TO_NS = 1000000f;

		protected const float FIXED_UPDATE_OFFSET_SMOOTHING_DELAY = 0.1f;

		[Tooltip("Set true if the Leap Motion hardware is mounted on an HMD; otherwise, leave false.")]
		[SerializeField]
		protected bool _isHeadMounted;

		[AutoFind(AutoFindLocations.All)]
		[SerializeField]
		protected LeapVRTemporalWarping _temporalWarping;

		[Tooltip("When true, update frames will be re-used for physics.  This is an optimization, since the total number of frames that need to be calculated is halved.  However, this introduces extra latency and inaccuracy into the physics frames.")]
		[SerializeField]
		protected bool _reuseFramesForPhysics;

		[Header("Device Type")]
		[SerializeField]
		protected bool _overrideDeviceType;

		[Tooltip("If overrideDeviceType is enabled, the hand controller will return a device of this type.")]
		[SerializeField]
		protected LeapDeviceType _overrideDeviceTypeWith = LeapDeviceType.Peripheral;

		[Header("Interpolation")]
		[Tooltip("Interpolate frames to deliver a potentially smoother experience.  Currently experimental.")]
		[SerializeField]
		protected bool _useInterpolation;

		[Tooltip("How much delay should be added to interpolation.  A non-zero amount is needed to prevent extrapolation artifacts.")]
		[SerializeField]
		protected long _interpolationDelay = 15L;

		protected Controller leap_controller_;

		protected SmoothedFloat _fixedOffset = new SmoothedFloat();

		protected Frame _untransformedUpdateFrame;

		protected Frame _transformedUpdateFrame;

		protected Frame _untransformedFixedFrame;

		protected Frame _transformedFixedFrame;

		protected Image _currentImage;

		private ClockCorrelator clockCorrelator;
	}
}
