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

namespace Leap.Unity
{
	[RequireComponent(typeof(Camera))]
	public class LeapImageRetriever : MonoBehaviour
	{
		public LeapImageRetriever.EyeTextureData TextureData
		{
			get
			{
				return this._eyeTextureData;
			}
		}

		private void Start()
		{
			if (this._provider == null)
			{
				Debug.LogWarning("Cannot use LeapImageRetriever if there is no LeapProvider!");
				base.enabled = false;
				return;
			}
			this.ApplyGammaCorrectionValues();
			this.ApplyCameraProjectionValues(base.GetComponent<Camera>());
		}

		private void OnEnable()
		{
			Controller leapController = this._provider.GetLeapController();
			if (leapController != null)
			{
				this.onController(leapController);
			}
			else
			{
				base.StartCoroutine(this.waitForController());
			}
			LeapVRCameraControl.OnLeftPreRender += this.ApplyCameraProjectionValues;
			LeapVRCameraControl.OnRightPreRender += this.ApplyCameraProjectionValues;
		}

		private void OnDisable()
		{
			base.StopAllCoroutines();
			Controller leapController = this._provider.GetLeapController();
			if (leapController != null)
			{
				this._provider.GetLeapController().DistortionChange -= new EventHandler<DistortionEventArgs>(this.onDistortionChange);
			}
			LeapVRCameraControl.OnLeftPreRender -= this.ApplyCameraProjectionValues;
			LeapVRCameraControl.OnRightPreRender -= this.ApplyCameraProjectionValues;
		}

		private void OnDestroy()
		{
			base.StopAllCoroutines();
			Controller leapController = this._provider.GetLeapController();
			if (leapController != null)
			{
				this._provider.GetLeapController().DistortionChange -= new EventHandler<DistortionEventArgs>(this.onDistortionChange);
			}
		}

		private void OnPreRender()
		{
			if (this.imagesEnabled)
			{
				Controller leapController = this._provider.GetLeapController();
				long num = leapController.Now();
				while (!this._requestedImage.IsComplete)
				{
					if (leapController.Now() - num > this.ImageTimeout)
					{
						break;
					}
				}
				if (this._requestedImage.IsComplete)
				{
					if (this._eyeTextureData.CheckStale(this._requestedImage, this._requestedImage))
					{
						this._eyeTextureData.Reconstruct(this._requestedImage, this._requestedImage);
					}
					this._eyeTextureData.UpdateTextures(this._requestedImage, this._requestedImage);
				}
				else if (!this.checkingImageState)
				{
					base.StartCoroutine(this.checkImageMode());
				}
			}
		}

		private void Update()
		{
			if (this.imagesEnabled)
			{
				Frame currentFrame = this._provider.CurrentFrame;
				Controller leapController = this._provider.GetLeapController();
				this._requestedImage = leapController.RequestImages(currentFrame.Id, Image.ImageType.DEFAULT);
			}
			else if (!this.checkingImageState)
			{
				base.StartCoroutine(this.checkImageMode());
			}
		}

		private IEnumerator waitForController()
		{
			Controller controller = null;
			do
			{
				controller = this._provider.GetLeapController();
				yield return null;
			}
			while (controller == null);
			this.onController(controller);
			yield break;
		}

		private IEnumerator checkImageMode()
		{
			this.checkingImageState = true;
			yield return new WaitForSeconds(2f);
			this._provider.GetLeapController().Config.Get<int>("images_mode", delegate(int enabled)
			{
				this.imagesEnabled = (enabled != 0);
				this.checkingImageState = false;
			});
			yield break;
		}

		private void onController(Controller controller)
		{
			controller.DistortionChange += new EventHandler<DistortionEventArgs>(this.onDistortionChange);
			controller.Connect += delegate(object A_1, ConnectionEventArgs A_2)
			{
				this._provider.GetLeapController().Config.Get<int>("images_mode", delegate(int enabled)
				{
					this.imagesEnabled = (enabled != 0);
				});
			};
			if (!this.checkingImageState)
			{
				base.StartCoroutine(this.checkImageMode());
			}
		}

		public void ApplyGammaCorrectionValues()
		{
			float value = 1f;
			if (QualitySettings.activeColorSpace != ColorSpace.Linear)
			{
				value = -Mathf.Log10(Mathf.GammaToLinearSpace(0.1f));
			}
			Shader.SetGlobalFloat("_LeapGlobalColorSpaceGamma", value);
			Shader.SetGlobalFloat("_LeapGlobalGammaCorrectionExponent", 1f / this._gammaCorrection);
		}

		public void ApplyCameraProjectionValues(Camera camera)
		{
			Shader.SetGlobalVector("_LeapGlobalProjection", new Vector4
			{
				x = camera.projectionMatrix[0, 2],
				y = 0f,
				z = camera.projectionMatrix[0, 0],
				w = camera.projectionMatrix[1, 1]
			});
		}

		private void onDistortionChange(object sender, LeapEventArgs args)
		{
			this._eyeTextureData.MarkStale();
		}

		public const string GLOBAL_COLOR_SPACE_GAMMA_NAME = "_LeapGlobalColorSpaceGamma";

		public const string GLOBAL_GAMMA_CORRECTION_EXPONENT_NAME = "_LeapGlobalGammaCorrectionExponent";

		public const string GLOBAL_CAMERA_PROJECTION_NAME = "_LeapGlobalProjection";

		public const int IMAGE_WARNING_WAIT = 10;

		public const int LEFT_IMAGE_INDEX = 0;

		public const int RIGHT_IMAGE_INDEX = 1;

		public const float IMAGE_SETTING_POLL_RATE = 2f;

		[AutoFind(AutoFindLocations.All)]
		[SerializeField]
		private LeapServiceProvider _provider;

		[SerializeField]
		[FormerlySerializedAs("gammaCorrection")]
		private float _gammaCorrection = 1f;

		[MinValue(0f)]
		[SerializeField]
		protected long ImageTimeout = 9000L;

		private LeapImageRetriever.EyeTextureData _eyeTextureData = new LeapImageRetriever.EyeTextureData();

		protected Image _requestedImage = new Image();

		protected bool imagesEnabled = true;

		private bool checkingImageState;

		public class LeapTextureData
		{
			public Texture2D CombinedTexture
			{
				get
				{
					return this._combinedTexture;
				}
			}

			public bool CheckStale(Image image)
			{
				return this._combinedTexture == null || this._intermediateArray == null || (image.Width != this._combinedTexture.width || image.Height * 2 != this._combinedTexture.height) || this._combinedTexture.format != this.getTextureFormat(image);
			}

			public void Reconstruct(Image image, string globalShaderName, string pixelSizeName)
			{
				int width = image.Width;
				int num = image.Height * 2;
				TextureFormat textureFormat = this.getTextureFormat(image);
				if (this._combinedTexture != null)
				{
					UnityEngine.Object.DestroyImmediate(this._combinedTexture);
				}
				this._combinedTexture = new Texture2D(width, num, textureFormat, false, true);
				this._combinedTexture.wrapMode = TextureWrapMode.Clamp;
				this._combinedTexture.filterMode = FilterMode.Bilinear;
				this._combinedTexture.name = globalShaderName;
				this._combinedTexture.hideFlags = HideFlags.DontSave;
				this._intermediateArray = new byte[width * num * this.bytesPerPixel(textureFormat)];
				Shader.SetGlobalTexture(globalShaderName, this._combinedTexture);
				Shader.SetGlobalVector(pixelSizeName, new Vector2(1f / (float)image.Width, 1f / (float)image.Height));
			}

			public void UpdateTexture(Image image)
			{
				Array.Copy(image.Data, 0, this._intermediateArray, 0, this._intermediateArray.Length);
				this._combinedTexture.LoadRawTextureData(this._intermediateArray);
				this._combinedTexture.Apply();
			}

			private TextureFormat getTextureFormat(Image image)
			{
				switch (image.Format)
				{
				case Image.FormatType.INFRARED:
					return TextureFormat.Alpha8;
				case Image.FormatType.IBRG:
				case (Image.FormatType)4:
					return TextureFormat.RGBA32;
				}
				throw new Exception("Unexpected image format " + image.Format + "!");
			}

			private int bytesPerPixel(TextureFormat format)
			{
				switch (format)
				{
				case TextureFormat.Alpha8:
					return 1;
				default:
					if (format != TextureFormat.BGRA32)
					{
						throw new Exception("Unexpected texture format " + format);
					}
					break;
				case TextureFormat.RGBA32:
				case TextureFormat.ARGB32:
					break;
				}
				return 4;
			}

			private Texture2D _combinedTexture;

			private byte[] _intermediateArray;
		}

		public class LeapDistortionData
		{
			public Texture2D CombinedTexture
			{
				get
				{
					return this._combinedTexture;
				}
			}

			public bool CheckStale()
			{
				return this._combinedTexture == null;
			}

			public void Reconstruct(Image image, string shaderName)
			{
				int num = image.DistortionWidth / 2;
				int num2 = image.DistortionHeight * 2;
				if (this._combinedTexture != null)
				{
					UnityEngine.Object.DestroyImmediate(this._combinedTexture);
				}
				Color32[] array = new Color32[num * num2];
				this._combinedTexture = new Texture2D(num, num2, TextureFormat.RGBA32, false, true);
				this._combinedTexture.filterMode = FilterMode.Bilinear;
				this._combinedTexture.wrapMode = TextureWrapMode.Clamp;
				this._combinedTexture.hideFlags = HideFlags.DontSave;
				this.addDistortionData(image, array, 0);
				this._combinedTexture.SetPixels32(array);
				this._combinedTexture.Apply();
				Shader.SetGlobalTexture(shaderName, this._combinedTexture);
			}

			private void addDistortionData(Image image, Color32[] colors, int startIndex)
			{
				float[] distortion = image.Distortion;
				for (int i = 0; i < distortion.Length; i += 2)
				{
					byte r;
					byte g;
					this.encodeFloat(distortion[i], out r, out g);
					byte b;
					byte a;
					this.encodeFloat(distortion[i + 1], out b, out a);
					colors[i / 2 + startIndex] = new Color32(r, g, b, a);
				}
			}

			private void encodeFloat(float value, out byte byte0, out byte byte1)
			{
				value = (value + 0.6f) / 2.3f;
				float num = value;
				float num2 = value * 255f;
				num -= (float)((int)num);
				num2 -= (float)((int)num2);
				num -= 0.003921569f * num2;
				byte0 = (byte)(num * 256f);
				byte1 = (byte)(num2 * 256f);
			}

			private Texture2D _combinedTexture;
		}

		public class EyeTextureData
		{
			public EyeTextureData()
			{
				this.BrightTexture = new LeapImageRetriever.LeapTextureData();
				this.RawTexture = new LeapImageRetriever.LeapTextureData();
				this.Distortion = new LeapImageRetriever.LeapDistortionData();
			}

			public static void ResetGlobalShaderValues()
			{
				Texture2D texture2D = new Texture2D(1, 1, TextureFormat.ARGB32, false, false);
				texture2D.name = "EmptyTexture";
				texture2D.hideFlags = HideFlags.DontSave;
				texture2D.SetPixel(0, 0, new Color(0f, 0f, 0f, 0f));
				Shader.SetGlobalTexture("_LeapGlobalBrightnessTexture", texture2D);
				Shader.SetGlobalTexture("_LeapGlobalRawTexture", texture2D);
				Shader.SetGlobalTexture("_LeapGlobalDistortion", texture2D);
			}

			public bool CheckStale(Image bright, Image raw)
			{
				return this.BrightTexture.CheckStale(bright) || this.RawTexture.CheckStale(raw) || this.Distortion.CheckStale() || this._isStale;
			}

			public void MarkStale()
			{
				this._isStale = true;
			}

			public void Reconstruct(Image bright, Image raw)
			{
				this.BrightTexture.Reconstruct(bright, "_LeapGlobalBrightnessTexture", "_LeapGlobalBrightnessPixelSize");
				this.RawTexture.Reconstruct(raw, "_LeapGlobalRawTexture", "_LeapGlobalRawPixelSize");
				this.Distortion.Reconstruct(raw, "_LeapGlobalDistortion");
				Image.FormatType format = raw.Format;
				if (format != Image.FormatType.INFRARED)
				{
					if (format != (Image.FormatType)4)
					{
						Debug.LogWarning("Unexpected format type " + raw.Format);
					}
					else
					{
						Shader.DisableKeyword("LEAP_FORMAT_IR");
						Shader.EnableKeyword("LEAP_FORMAT_RGB");
					}
				}
				else
				{
					Shader.DisableKeyword("LEAP_FORMAT_RGB");
					Shader.EnableKeyword("LEAP_FORMAT_IR");
				}
				this._isStale = false;
			}

			public void UpdateTextures(Image bright, Image raw)
			{
				this.BrightTexture.UpdateTexture(bright);
				this.RawTexture.UpdateTexture(raw);
			}

			private const string IR_SHADER_VARIANT_NAME = "LEAP_FORMAT_IR";

			private const string RGB_SHADER_VARIANT_NAME = "LEAP_FORMAT_RGB";

			private const string GLOBAL_BRIGHT_TEXTURE_NAME = "_LeapGlobalBrightnessTexture";

			private const string GLOBAL_RAW_TEXTURE_NAME = "_LeapGlobalRawTexture";

			private const string GLOBAL_DISTORTION_TEXTURE_NAME = "_LeapGlobalDistortion";

			private const string GLOBAL_BRIGHT_PIXEL_SIZE_NAME = "_LeapGlobalBrightnessPixelSize";

			private const string GLOBAL_RAW_PIXEL_SIZE_NAME = "_LeapGlobalRawPixelSize";

			public readonly LeapImageRetriever.LeapTextureData BrightTexture;

			public readonly LeapImageRetriever.LeapTextureData RawTexture;

			public readonly LeapImageRetriever.LeapDistortionData Distortion;

			private bool _isStale;
		}
	}
}
