package brn.util;

import org.apache.log4j.Logger;

import jist.runtime.JistAPI;

/**
 * Note: Do not forget the {@link #returnObject(Object)}
 * 
 * @author kurth
 */
public interface ObjectPool {
  
  /**
   * TODO implement dynamic groth
   * 
   * @author kurth
   */
  public abstract class Dynamic extends FixedSize {

    public Dynamic(int poolsize) {
      super(poolsize);
      // TODO Auto-generated constructor stub
    }
  }

  Object getObject();
  
  void returnObject(Object o);
  
  /**
   * Simple, fixed-sized pool.
   * 
   * @author kurth
   */
  public static abstract class FixedSize implements ObjectPool {
    
    private static Logger log = Logger.getLogger(FixedSize.class.getName());

    /**
     * Private array pool of Object arrays of size 'size'.
     */
    private final Object[] pool;

    /**
     * Index into array pool.
     */
    private int count;
    
    private int demand;
    
    private int maxDemand;
    
    /**
     * Create new object array pool of given size, for
     * array of given size.
     *
     * @param poolsize size of object array pool
     */
    public FixedSize(int poolsize) 
    {
      demand = 0;
      maxDemand = 0;

      pool = new Object[poolsize];
      for(count=0; count<pool.length; count++) {
        pool[count] = newObject();
      }
    }

    protected abstract PoolableObject newObject();

    /**
     * Return an array from the pool, 
     * or create a new one if pool is empty.
     *
     * @return initialized Object array
     */
    public Object getObject()
    {
      PoolableObject ret = null;
      demand++;
      if(count>0)
        ret = (PoolableObject) pool[--count];
      else
        ret = newObject();
      ret.init();
      if (demand > maxDemand) {
        maxDemand = demand;
        if (log.isInfoEnabled() && 
            maxDemand > pool.length && maxDemand % 25 == 0)
          log.info(this + "(" + JistAPI.getTime() + "): max demand " + maxDemand);
      }
      return ret;
    }

    /**
     * Place a free (otherwise unused) array object back in the pool, if there
     * is space. Remember to any clear reference from the array to allow 
     * garbage collection to occur.
     *
     * @param o reference to free array object
     */
    public void returnObject(Object o)
    {
//      if (Main.ASSERT)
//        Util.assertion(pool[0].getClass().equals(o.getClass()));
      
      ((PoolableObject)o).returned();
      
      demand--;
      if(count<pool.length)
      {
        pool[count++] = o;
      }
    }

    public String toString() {
      String name = null;
      if (pool.length > 0)
        name = pool[0].getClass().getSimpleName();
      else
        name = newObject().getClass().getSimpleName();
      return "ObjectPool.FixedSize["+name+"]";
    }

//    /**
//     * Small program to test performance of ArrayPool implementation.
//     *
//     * @param args command line parameters
//     */
//    public static void main(String[] args)
//    {
//      // initialize
//      final int size=100, arraysize=1;
//      long num=10000000;
//      Object[][] stuff = new Object[size][];
//      long startTime, endTime;
//      for(int i=0; i<size; i++)
//      {
//        stuff[i] = new Object[arraysize];
//      }
//      for(int repeat=0; repeat<5; repeat++)
//      {
//        // regular allocation
//        startTime = System.currentTimeMillis();
//        for(int i=0; i<num; i++)
//        {
//          stuff[i%size] = new Object[arraysize];
//        }
//        endTime = System.currentTimeMillis();
//        System.out.println(" new: "+((endTime-startTime)/1000.0));
//        // pooled
//        Object lock = new Object();
//        ArrayPool ap = new ArrayPool(arraysize, 100);
//        startTime = System.currentTimeMillis();
//        for(int i=0; i<num; i++)
//        {
//          Thread.currentThread();
//          int index=i%size;
//          ap.put(stuff[index]);
//          stuff[index] = ap.get();
//        }
//        endTime = System.currentTimeMillis();
//        System.out.println("pool: "+((endTime-startTime)/1000.0));
//      }
//    } // function: main
  }

}
