/******************************************************************************\
* Copyright (C) 2012-2016 Leap Motion, Inc. All rights reserved.               *
* Leap Motion proprietary and confidential. Not for distribution.              *
* Use subject to the terms of the Leap Motion SDK Agreement available at       *
* https://developer.leapmotion.com/sdk_agreement, or another agreement         *
* between Leap Motion and you, your company or other organization.             *
\******************************************************************************/
using System;

namespace LeapInternal
{
  public class ObjectPool<T> where T : PooledObject, new()
  {
    private T[] pool; //the pooled objects
    private UInt64 age = 0;
    private const double _growRate = 1.5;

    public bool Growable { get; set; }

    /**
     * If Growable is true, then Capacity is only the **current**
     * size of the underlying memory store.
     */
    public int Capacity { get { return pool.Length; } }

    public ObjectPool(int initialCapacity, bool growable = false)
    {
      this.pool = new T[initialCapacity];
      this.Growable = growable;
    }

    public T CheckOut()
    {
      UInt64 eldest = UInt64.MaxValue;
      uint indexToUse = 0;
      bool freeObjectFound = false;
      for (uint p = 0; p < Capacity; p++)
      {
        if (this.pool[p] == null || this.pool[p].age == 0)
        {
          indexToUse = p;
          freeObjectFound = true;
          break;
        }
        if (this.pool[p].age < eldest)
        {
          eldest = this.pool[p].age;
          indexToUse = p;
        }
      }
      if (!freeObjectFound)
      {
        if (Growable)
        {
          indexToUse = (uint)pool.Length;
          expand();
        }
      } //else recycle existing object

      if (this.pool[indexToUse] == null)
        this.pool[indexToUse] = new T();

      this.pool[indexToUse].poolIndex = indexToUse;
      this.pool[indexToUse].age = ++age;

      return this.pool[indexToUse];
    }

    public T FindByPoolIndex(UInt64 index)
    {
      for (int e = 0; e < this.pool.Length; e++)
      {
        T item = this.pool[e];
        if (item != null && item.poolIndex == index && item.age > 0)
          return item;
      }
      return null;
    }

    private void addItem(uint index)
    {
      this.pool[index] = new T();
      this.pool[index].poolIndex = index;
      this.pool[index].age = 0;
    }

    private void expand()
    {
      int newSize = (int)Math.Floor(Capacity * _growRate);
      T[] newPool = new T[newSize];
      uint m = 0;
      for (; m < this.pool.Length; m++)
      {
        newPool[m] = this.pool[m];
      }
      this.pool = newPool;
    }
  }
}

