/*
 *  
 *
 *  This file is licensed to You under the Eclipse Public License (EPL);
 *  You may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *      http://www.opensource.org/licenses/eclipse-1.0.php
 *
 *  (C) Copyright IBM Corporation 2006-2010.
 */

/**
 * This is a basic utility for buffering the elements of a rank-1 Array
 * of unpredictable size.  It implements the Builder interface and 
 * virtually nothing else.
 */

public class ArrayBuilder[T] implements x10.util.Builder[T, Array[T]] {
	/** how many entries to allow by default in the initial buffer */
	public static DEFAULT_INITIAL_CAPACITY = 16;
	/** the default factor to multiply the capacity by when growing the buffer */
	public static DEFAULT_GROWTH_RATE = 2.0;
	private var buffer: Array[T](1);
	private var size: Int = 0;        // the number of entries added
	private val maximumCapacity: Int; // don't let the buffer grow beyond this size
	private val growthRate: Double; // what to multiply the capacity by when growing the buffer
	
	/** 
	 * create a builder with the default initial capacity and growth rate,
	 * and no capacity bound
	 */
	public def this() {
		this(DEFAULT_INITIAL_CAPACITY, Int.MAX_VALUE, DEFAULT_GROWTH_RATE);
	}
	
	/** 
	 * create a builder with the default growth rate and no capacity bound,
	 *  but with the given initial capacity
	 * @param initialCapacity the number of entries in the buffer initially
	 */
	public def this(initialCapacity: Int) {
		this(initialCapacity, Int.MAX_VALUE, DEFAULT_GROWTH_RATE);
	}
	
	/** 
	 * create a builder with the default growth rate, but with
	 * the given initial capacity and overall capacity bound
	 * @param initialCapacity the number of entries in the buffer initially
	 * @param maximumCapacity do not allow more than this many entries
	 */
	public def this(initialCapacity: Int, maximumCapacity: Int) {
		this(initialCapacity, maximumCapacity, DEFAULT_GROWTH_RATE);
	}
	
	/** 
	 * create a builder with all of the parameters specified.
	 * @param initialCapacity the number of entries in the buffer initially
	 * @param maximumCapacity do not allow more than this many entries
	 * @param growthRate what to multiply the capacity by when growing the buffer
	 */
	public def this(initialCapacity: Int, maximumCapacity: Int, growthRate: Double) {
		if (initialCapacity > maximumCapacity) {
			val msg = "Initial capacity, "     +initialCapacity+
						 ", exceeds the maximum, "+maximumCapacity;
			throw new IllegalArgumentException(msg);
		}
		buffer = new Array[T](0..initialCapacity-1);
		this.maximumCapacity = maximumCapacity;
		this.growthRate = growthRate;
	}
	
	/**
	 * add an entry, growing the buffer if need be
	 * @param t the new item to be added
	 * @return this builder
	 */
	public def add(t: T): ArrayBuilder[T] {
		if (this.size >= buffer.size) growTheBuffer();
		buffer(this.size++) = t;
		return this;
	}
	
	/**
	 * return the current number of entries in the whole buffer
	 * @return the currentnumber of entries in the whole buffer
	 */
	public def capacity() = buffer.size;
	
	/**
	 * return the entries indexed by 0 through size-1 as an array
	 * @return the entries indexed by 0 through size-1 as an array
	 */
	public def result(): Array[T](1) {
		return new Array[T](size, (n: Int)=>buffer(n)); 
	}
	
	/**
	 * return the number of entries added to the buffer
	 * @return the number of entries added to the buffer
	 */
	public def size() = this.size;
	
	/**
	 * a new buffer array is created whose capacity is the current 
	 * capacity multiplied by the growthRate, and then the current
	 * entries are copied into the new buffer.
	 */
	public def growTheBuffer() {
		if (buffer.size >= maximumCapacity) {
			val msg = "Maximum capacity, "+maximumCapacity+" exceeded.";
			throw new IllegalArgumentException(msg);
		}
		var newSize:Int  = Math.floor(growthRate*buffer.size) as Int;
		if (newSize >= maximumCapacity) newSize = maximumCapacity;
		val newBuffer = new Array[T](newSize);
		Array.copy(buffer, 0, newBuffer, 0, size);
	}
}