/**
 * 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.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
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;

/**
 * 
 * C++のSTLのvectorを実現するためのクラス。
 * 
 * @author NTT DATA Corporation
 */
@SuppressWarnings("unchecked")
public strictfp class STLVector<T> extends Vector<T> {
	
	private static final long serialVersionUID = 5325093071986534510L;
	
	private Class<? extends T> myClass;
	
	//ネスト用
	private int nests = 1;
	
	/**
	 * 
	 * 基底クラスの動作に加え、内部を指定クラスのインスタンスで埋める。
	 * 
	 * @param cls	ベクタの中身となるクラス。Generics指定の代わり。
	 * @param initialCapacity	Vectorの初期容量
	 * @param capacityIncrement	Vectorがあふれたときに増加される容量 
	 * @see java.util.Vector#Vector(int, int)
	 */
	public STLVector( Class<? extends T> cls, int initialCapacity, int capacityIncrement ) {
		super(initialCapacity, capacityIncrement);
		this.myClass = cls;
		
		fillInstance(0, initialCapacity);
	}
	
	/**
	 * 
	 * 基底クラスの動作に加え、内部を指定クラスのインスタンスで埋める。
	 * 
	 * @param cls	ベクタの中身となるクラス。Generics指定の代わり。
	 * @param initialCapacity	Vectorの初期容量
	 * @see java.util.Vector#Vector(int)
	 */
	public STLVector( Class<? extends T> cls, int initialCapacity ) {
		super(initialCapacity);
		this.myClass = cls;
		
		fillInstance(0, initialCapacity);
	}
	
	/**
	 * 基底クラスと同動作。
	 * 
	 * @param cls	ベクタの中身となるクラス。Generics指定の代わり。
	 * @see java.util.Vector#Vector()
	 */
	public STLVector( Class<? extends T> cls ) {
		super();
		this.myClass = cls;
	}

	
	/**
	 * 基底クラスと同動作。
	 * 
	 * @param cls	ベクタの中身となるクラス。Generics指定の代わり。
	 * @param c	要素がVectorに配置されるコレクション 
	 * @see java.util.Vector#Vector(java.util.Collection)
	 */
	public STLVector( Class<? extends T> cls, Collection<? extends T> c ) {
		super(c);
		this.myClass = cls;
	}
	
	
	//-----------------------------
	// ネスト用コンストラクタ郡
	//-----------------------------
	public STLVector( int nests, Class cls, int initialCapacity, int capacityIncrement ) {
		super(initialCapacity, capacityIncrement);
		this.myClass = cls;
		
		fillInstance(0, initialCapacity);
	}
	public STLVector( int nests, Class cls, int initialCapacity ) {
		super(initialCapacity);
		this.myClass = cls;
		this.nests = nests;
		
		fillInstance(0, initialCapacity );
	}
	public STLVector( int nests, Class cls ) {
		super();
		this.myClass = cls;
		this.nests = nests;
	}
	
	/**
	 * 
	 * 基底クラスの動作に加え、増えた部分は指定クラスのインスタンスで埋める。
	 * 
	 * @param newSize	新しいサイズ
	 * @see java.util.Vector#setSize(int)
	 */
	public void resize( int newSize ) {
		
		int oldSize = elementCount;
		
		if( newSize > oldSize ){
			fillInstance( oldSize, newSize );
		}
		
		super.setSize(newSize);
	}
	
	/**
	 * 
	 * 内部的にresize()へフォワードするだけのメソッドに変更。
	 * 
	 * @param newSize	新しいサイズ
	 * @see java.util.Vector#setSize(int)
	 */
	@Override
	public void setSize(int newSize) {
		this.resize(newSize);
	}
	
	/**
	 * 
	 * 内部ののすべての要素を正しい順序で保持する配列を返す。<br>
	 * このとき、Genericsで指定されたクラスにキャストして返す。
	 * 
	 * @see java.util.Collection#toArray()
	 */
	@Override
	public T[] toArray() {
		
		T[] array;
		
		if( nests <= 1 ){
			array = (T[])Array.newInstance( myClass, elementCount );
			System.arraycopy(elementData, 0, array, 0, elementCount);
		}
		else{
			array = (T[])Array.newInstance( getClass(), elementCount );
			System.arraycopy(elementData, 0, array, 0, elementCount);
		}
		
		return array;
	}

	/**
	 * 
	 * 内部ののすべての要素を正しい順序で保持する配列を返す。<br>
	 * このとき、Genericsで指定されたクラスにキャストして返す。
	 * 
	 * @see java.util.Collection#toArray()
	 */
	public T[] toArray(int StartPoint) {
		
		T[] array;
		
		if( nests <= 1 ){
			array = (T[])Array.newInstance( myClass, elementCount - StartPoint);
			System.arraycopy(elementData, StartPoint, array, 0, elementCount - StartPoint);
		}
		else{
			array = (T[])Array.newInstance( getClass(), elementCount - StartPoint);
			System.arraycopy(elementData, StartPoint, array, 0, elementCount - StartPoint);
		}
		
		return array;
	}
	
	/**
	 * 
	 * STLのassignの動作を行う。<br>
	 * それまでの値は全て破棄し、指定数分の指定オブジェクトで内部を埋める。<br>
	 * ただし、ネストには未対応。 
	 * 
	 * @param num	埋めるオブジェクトの数
	 * @param val	埋めるオブジェクト
	 */
//	public void assign( int num, final T val ) {
//		
//		//今までのものは破棄
//		//buti 高速化
//		//clear();
//		
//		//指定サイズにサイズを変更しインスタンスで埋める
//		resize( num );
//		
//		try {
//			
//			//assignmentメソッドを持つか？
//			Method assignment = myClass.getMethod( "assignment", myClass );
//			
//			//持っていればassinmentで代入
//			if( assignment != null ){
//				for( T ele : this ){
//					assignment.invoke( ele, val );
//				}
//				return;//終了
//			}
//			
//		} catch (NoSuchMethodException e) {
//			
//			//では、valueフィールドをもつか？
//			try {
//				Field value = myClass.getField("value");
//				
//				//持っていればvalueに代入
//				if( value != null ){
//					for( T ele : this ){
//						Object valfield = value.get(val);
//						value.set( ele, valfield );
//					}
//					return;//終了
//				}
//				
//			} catch (NoSuchFieldException e1) {
//				
//				//そうでもなければ普通に=で。
//				for( int i=0; i<elementCount; i++ ){
//					set( i, val );
//				}
//				return;//終了
//				
//			//エラー
//			} catch (Exception e1) {
//				HimawariLogger.outputException( e );
//			}
//			
//		//エラー
//		} catch (Exception e) {
//			HimawariLogger.outputException( e );
//		}
//			
//	}
	
public void assign( int num, final T val ) {
		
		//今までのものは破棄
		//buti 高速化 無駄な破棄しない
		//clear();
		
		//指定サイズにサイズを変更しインスタンスで埋める
		resize( num );
				
		if(myClass.equals(CqVector3D.class))
		{
				for( T ele : this ){
					((CqVector3D)ele).assignment( (CqVector3D)val );
				}
		}
		else if(myClass.equals(CqVector4D.class))
		{
				for( T ele : this ){
					((CqVector4D)ele).assignment( (CqVector4D)val );
				}
		}
		else if(myClass.equals(p_float.class))
		{
				for( T ele : this ){
					((p_float)ele).value = ((p_float)val).value;
				}
		}
		else if(myClass.equals(CqColor.class))
		{
				for( T ele : this ){
					((CqColor)ele).assignment( (CqColor)val );
				}
		}
		else if(myClass.equals(CqMatrix.class))
		{
			for( T ele : this ){
				((CqMatrix)ele).assignment( (CqMatrix)val );
			}
		}
		else if(myClass.equals(p_int.class))
		{
			for( T ele : this ){
				((p_int)ele).value = ((p_int)val).value;
			}
		}
		else if(myClass.equals(p_String.class))
		{
			for( T ele : this ){
				((p_String)ele).value = ((p_String)val).value;
			}
		}
		else
		{
			for( int i=0; i<elementCount; i++ ){
				set( i, val );
			}
		}
	}

	//
	// ◆その他の置換メモ◆
	//
	//	[C++]			[Java]
	//
	//	empty()			isEmpty()
	//	size()			size()
	//	capacity()		capacity()
	//	
	//	insert(i,Obj)	add(i, Obj)
	//	clear()			clear()
	//	
	//	=				=
	//	==				equals()
	//	
	//	
	//	
	//	
	//	vec[1]=2.0		vec.set( 1, 2.0 );	or vec.set( 1, new p_float(2.0) )
	//	
	
	/**
	 * 
	 * このベクタの中身をインスタンスで埋める。
	 * 引数なしコンストラクタでインスタンス化できない場合は、nullで埋める。
	 * 
	 * @param start	埋める範囲の開始位置
	 * @param end		埋める範囲の終了位置
	 */
	private void fillInstance( int start, int end ){
		
		for( int i=start; i<end; i++ ){
			
			try {
				
				if( nests > 1 ){
					this.add( i, newThisInstance() );
				}
				else{
					this.add( i, newInstance(myClass) );
				}
				
			} catch (Exception e) {
//				e.printStackTrace();
				this.add( null );
			}
			
		}//end of for
		
	}
	
	
	/**
	 * 
	 * 指定クラスのインスタンスを生成する。
	 * 
	 * @param cls	インスタンス化したいクラス
	 * @return	指定クラスのインスタンス
	 */
	private T newInstance( Class<? extends T> cls ){
		try {
			
			//プリミティブのラッパか？
			Class superClass = cls.getSuperclass();
			if( superClass.equals( Number.class) ){
				
				//ラッパクラスならそのプリミティブ型を示すClassを取得
				Class primitiveClass = (Class)cls.getField("TYPE").get(cls);
				
				//プリミティブを引数に持つコンストラクタを取得	
				Constructor<? extends T> cnst = cls.getConstructor(primitiveClass);
				
				//そのコンストラクタでインスタンス化
				return cnst.newInstance( (byte)0 );
			}
			//はたまたBooleanか？
			else if( cls.equals( Boolean.class ) ){
				
				//コンストラクタの取得
				Constructor<? extends T> cnst = cls.getConstructor(boolean.class);
				
				//インスタンス化
				return cnst.newInstance( false );
			}
			
			//その他は普通にインスタンス化
			return cls.newInstance();
			
		} catch (Exception e) {
			
			//インターフェースや抽象クラス以外でExceptionが発生した場合、エラー内容を表示。
			int mod = cls.getModifiers();
			if( !Modifier.isInterface(mod) && !Modifier.isAbstract(mod) ) HimawariLogger.outputException( e );
			
			//なんかエラーがあればnull
			return null;
		}
	}
	/**
	 * 
	 * このクラスのインスタンスを生成する。
	 * 
	 * @return	このクラスのインスタンス
	 */
	private T newThisInstance(){
		try {
			Class cls = this.getClass();
			
			//コンストラクタを取得	
			Constructor cnst = cls.getConstructor( int.class, Class.class );
			
			//そのコンストラクタでインスタンス化
			return (T)cnst.newInstance( nests-1, myClass );
			
		} catch (Exception e) {

			//インターフェースや抽象クラス以外でExceptionが発生した場合、エラー内容を表示。
			int mod = myClass.getModifiers();
			if( !Modifier.isInterface(mod) && !Modifier.isAbstract(mod) ) HimawariLogger.outputException( e );
			
			//なんかエラーがあればnull
			return null;
		}
	}
	
}

