/**
 * Copyright (C) 2006-2007  NTT DATA CORPORATION
 * 
 * Version: 1.0.0 2007/04/01
 *  
 */
package net.cellcomputing.himawari.accessory;

import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.AbstractList;
import java.util.Collection;

import net.cellcomputing.himawari.accessory.primitive.p_String;
import net.cellcomputing.himawari.accessory.primitive.p_float;
import net.cellcomputing.himawari.accessory.primitive.p_int;
import net.cellcomputing.himawari.library.types.CqColor;
import net.cellcomputing.himawari.library.types.CqMatrix;
import net.cellcomputing.himawari.library.types.CqVector3D;
import net.cellcomputing.himawari.library.types.CqVector4D;
import net.cellcomputing.himawari.util.HimawariLogger;

/**
 * Ｃ言語のＳＴＬのArrayクラスを模倣したもの。
 * 内部配列を、テンプレートで指定されたインスタンスで埋める。
 * 
 * @author NTT DATA Corporation
 */
public class STLArray<T> extends AbstractList<T>{

	private Class<? extends T> myClass;
	
	protected T[] elementData;

    protected int elementCount;
	/**
	 * @param name
	 */
	public STLArray(Class<? extends T> cls) {
		this.myClass = cls;
		elementData = (T[]) new Object[10];
//		try {
//			elementData[0] = cls.newInstance();
//		} catch (Exception e) {
//			
//			//インターフェースや抽象クラス以外でExceptionが発生した場合、エラー内容を表示。
//			int mod = cls.getModifiers();
//			if( !Modifier.isInterface(mod) && !Modifier.isAbstract(mod) ) HimawariLogger.outputException( e );
//			
//			//なんかエラーがあればnull
//		}
	}

	/**
	 * @param class1
	 * @param i
	 */
	public STLArray(Class<? extends T> cls, int initialCapacity) {
		this.myClass = cls;
		elementData = (T[]) new Object[initialCapacity];
		try {
			for(int i=0;i<initialCapacity;i++)
			elementData[i] = cls.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		elementCount = initialCapacity;
	}

	public void resize( int newSize ) {
		
		Class cls = this.myClass;
		int oldSize = elementCount;
		elementCount = newSize;
		try {
		if( newSize > oldSize ){
			T[] oldData = elementData;
//			    int newCapacity = (capacityIncrement > 0) ?
//				(oldCapacity + capacityIncrement) : (oldCapacity * 2);
//		    	    if (newCapacity < minCapacity) {
//				newCapacity = minCapacity;
//			    }
		    elementData = (T[]) new Object[newSize];
		    System.arraycopy(oldData, 0, elementData, 0, oldSize);
		    for(int i = oldSize;i<newSize;i++)
				elementData[i] = this.myClass.newInstance();
				
		}
		
		} 
		catch (Exception e) {
			
			//インターフェースや抽象クラス以外でExceptionが発生した場合、エラー内容を表示。
			int mod = cls.getModifiers();
			if( !Modifier.isInterface(mod) && !Modifier.isAbstract(mod) ) HimawariLogger.outputException( e );
			
			//なんかエラーがあればnull
		}
	}

	/**
	 * 
	 * 
	 * 
	 * @param i
	 * @return
	 */
	public T elementAt(int index) {
		if (index >= elementCount) {
		    throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
		}

	        return (T)elementData[index];
	}
	
	public T get(int index) {
		if (index >= elementCount) {
		    throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
		}

	        return (T)elementData[index];
	}
	/**
	 * 
	 * 
	 * 
	 * @param color
	 * @param i
	 */
	public void setElementAt(T obj, int index) {
		if (index >= elementCount) {
		    throw new ArrayIndexOutOfBoundsException(index + " >= " + 
							     elementCount);
		}
		elementData[index] = obj;
	}
	/**
	 * 
	 * 
	 * 
	 * @param envVars_P
	 * @param data
	 */
	public T set(int index, T obj) {
		if (index >= elementCount) {
		    throw new ArrayIndexOutOfBoundsException(index + " >= " + 
							     elementCount);
		}
		elementData[index] = obj;
		return obj;
	}
	/**
	 * 
	 * 
	 * 
	 * @return
	 */
	public int size() {
		return elementCount;
	}

	/**
	 * 
	 * 
	 * 
	 * @param i
	 * @param def
	 */
	public void assign( int num, final T val ) {
		resize( num );
		
		if(myClass.equals(CqVector3D.class))
		{
			for(int i=0; i<elementCount;i++){
				((CqVector3D)this.elementData[i]).assignment( (CqVector3D)val );
			}
		}
		else if(myClass.equals(CqVector4D.class))
		{
			for(int i=0; i<elementCount;i++){
					((CqVector4D)this.elementData[i]).assignment( (CqVector4D)val );
				}
		}
		else if(myClass.equals(p_float.class))
		{
			for(int i=0; i<elementCount;i++){
					((p_float)this.elementData[i]).value = ((p_float)val).value;
				}
		}
		else if(myClass.equals(CqColor.class))
		{
			for(int i=0; i<elementCount;i++){
					((CqColor)this.elementData[i]).assignment( (CqColor)val );
				}
		}
		else if(myClass.equals(CqMatrix.class))
		{
			for(int i=0; i<elementCount;i++){
				((CqMatrix)this.elementData[i]).assignment( (CqMatrix)val );
			}
		}
		else if(myClass.equals(p_int.class))
		{
			for(int i=0; i<elementCount;i++){
				((p_int)this.elementData[i]).value = ((p_int)val).value;
			}
		}
		else if(myClass.equals(p_String.class))
		{
			for(int i=0; i<elementCount;i++){
				((p_String)this.elementData[i]).value = ((p_String)val).value;
			}
		}
		else
		{
			for( int i=0; i<elementCount; i++ ){
				setElementAt( val ,i);
			}
		}
	}

	/**
	 * 
	 * 
	 * 
	 * @param envVars_Last
	 */
	public void setSize(int size) {
		resize(size);
	}

	/**
	 * 
	 * 
	 * 
	 */
	public void clear() {
	 	// Let gc do its work
	 	for (int i = 0; i < elementCount; i++)
	 	    elementData[i] = null;

	 	elementCount = 0;
	}

	public T[] toArray(int StartPoint) {
		T[] array;
		array = (T[])Array.newInstance( getClass(), elementCount - StartPoint);
		System.arraycopy(elementData, StartPoint, array, 0, elementCount - StartPoint);
		return array;
	}

	public T[] toArray() {
		return elementData;
	}
	
	  public void ensureCapacity(int minCapacity) {
			int oldCapacity = elementData.length;
			if (minCapacity > oldCapacity) {
			    Object oldData[] = elementData;
			    int newCapacity = (oldCapacity * 3)/2 + 1;
		    	    if (newCapacity < minCapacity)
				newCapacity = minCapacity;
			    elementData = (T[])new Object[newCapacity];
			    System.arraycopy(oldData, 0, elementData, 0, elementCount);
			}
	}
	  
	 public boolean add(T o) {
			ensureCapacity(elementCount + 1);  // Increments modCount!!
			elementData[elementCount++] = o;
			return true;
	 }
	
	 public boolean addAll(Collection<? extends T> c) {
		    Object oldData[] = c.toArray();    
		    int numNew = c.size();
		    ensureCapacity(elementCount + numNew);  // Increments modCount
		        System.arraycopy(oldData, 0, elementData, elementCount, numNew);
		        elementCount += numNew;
			return numNew != 0;
	 }
}