using System;
using UnityEngine;

namespace Leap.Unity.Graphing
{
	public class RingBuffer<T>
	{
		public RingBuffer(int minCapacity = 8)
		{
			if (minCapacity <= 0)
			{
				throw new ArgumentException("Capacity must be positive and nonzero.");
			}
			int num = Mathf.ClosestPowerOfTwo(minCapacity);
			if (num < minCapacity)
			{
				num *= 2;
			}
			this._array = new T[num];
			this.recalculateIndexMask();
			this._front = 0u;
			this._count = 0u;
		}

		public int Count
		{
			get
			{
				return (int)this._count;
			}
		}

		public void Clear()
		{
			if (this._count != 0u)
			{
				Array.Clear(this._array, 0, this._array.Length);
				this._front = 0u;
				this._count = 0u;
			}
		}

		public void PushBack(T t)
		{
			this.doubleCapacityIfFull();
			this._count += 1u;
			this._array[(int)((UIntPtr)this.getBackIndex())] = t;
		}

		public void PushFront(T t)
		{
			this.doubleCapacityIfFull();
			this._count += 1u;
			this._front = (this._front - 1u & this._indexMask);
			this._array[(int)((UIntPtr)this._front)] = t;
		}

		public void PopBack()
		{
			this.checkForEmpty("pop back");
			this._array[(int)((UIntPtr)this.getBackIndex())] = default(T);
			this._count -= 1u;
		}

		public void PopFront()
		{
			this.checkForEmpty("pop front");
			this._array[(int)((UIntPtr)this._front)] = default(T);
			this._count -= 1u;
			this._front = (this._front + 1u & this._indexMask);
		}

		public void PopBack(out T back)
		{
			this.checkForEmpty("pop back");
			uint backIndex = this.getBackIndex();
			back = this._array[(int)((UIntPtr)backIndex)];
			this._array[(int)((UIntPtr)backIndex)] = default(T);
			this._count -= 1u;
		}

		public void PopFront(out T front)
		{
			this.checkForEmpty("pop front");
			front = this._array[(int)((UIntPtr)this._front)];
			this._array[(int)((UIntPtr)this._front)] = default(T);
			this._front = (this._front + 1u & this._indexMask);
			this._count -= 1u;
		}

		public T Front
		{
			get
			{
				this.checkForEmpty("get front");
				return this._array[(int)((UIntPtr)this._front)];
			}
			set
			{
				this.checkForEmpty("set front");
				this._array[(int)((UIntPtr)this._front)] = value;
			}
		}

		public T Back
		{
			get
			{
				this.checkForEmpty("get back");
				return this._array[(int)((UIntPtr)this.getBackIndex())];
			}
			set
			{
				this.checkForEmpty("set back");
				this._array[(int)((UIntPtr)this.getBackIndex())] = value;
			}
		}

		public T this[int index]
		{
			get
			{
				this.checkForValidIndex((uint)index);
				return this._array[(int)((UIntPtr)this.getIndex((uint)index))];
			}
			set
			{
				this.checkForValidIndex((uint)index);
				this._array[(int)((UIntPtr)this.getIndex((uint)index))] = value;
			}
		}

		public string ToDebugString()
		{
			string str = "[";
			uint backIndex = this.getBackIndex();
			uint num = 0u;
			while ((ulong)num < (ulong)((long)this._array.Length))
			{
				bool flag;
				if (this._count == 0u)
				{
					flag = true;
				}
				else if (this._count == 1u)
				{
					flag = (num != this._front);
				}
				else if (this._front < backIndex)
				{
					flag = (num < this._front || num > backIndex);
				}
				else
				{
					flag = (num < this._front && num > backIndex);
				}
				string text = string.Empty;
				if (num == this._front)
				{
					text = "{";
				}
				else
				{
					text = " ";
				}
				if (flag)
				{
					text += ".";
				}
				else
				{
					text += this._array[(int)((UIntPtr)num)].ToString();
				}
				if (num == backIndex)
				{
					text += "}";
				}
				else
				{
					text += " ";
				}
				str += text;
				num += 1u;
			}
			return str + "]";
		}

		private uint getBackIndex()
		{
			return this._front + this._count - 1u & this._indexMask;
		}

		private uint getIndex(uint index)
		{
			return this._front + index & this._indexMask;
		}

		private void doubleCapacityIfFull()
		{
			if ((ulong)this._count >= (ulong)((long)this._array.Length))
			{
				T[] array = new T[this._array.Length * 2];
				uint backIndex = this.getBackIndex();
				if (this._front <= backIndex)
				{
					Array.Copy(this._array, (long)((ulong)this._front), array, 0L, (long)((ulong)this._count));
				}
				else
				{
					uint num = (uint)(this._array.Length - (int)this._front);
					Array.Copy(this._array, (long)((ulong)this._front), array, 0L, (long)((ulong)num));
					Array.Copy(this._array, 0L, array, (long)((ulong)num), (long)((ulong)(this._count - num)));
				}
				this._front = 0u;
				this._array = array;
				this.recalculateIndexMask();
			}
		}

		private void recalculateIndexMask()
		{
			this._indexMask = (uint)(this._array.Length - 1);
		}

		private void checkForValidIndex(uint index)
		{
			if (index >= this._count)
			{
				throw new IndexOutOfRangeException(string.Concat(new object[]
				{
					"The index ",
					index,
					" was out of range for the RingBuffer with size ",
					this._count,
					"."
				}));
			}
		}

		private void checkForEmpty(string actionName)
		{
			if (this._count == 0u)
			{
				throw new InvalidOperationException("Cannot " + actionName + " because the RingBuffer is empty.");
			}
		}

		private T[] _array;

		private uint _front;

		private uint _count;

		private uint _indexMask;
	}
}
