// Aqsis
// Copyright (c) 1997 - 2001, Paul C. Gregory
//
// Contact: pgregory@aqsis.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

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

import net.cellcomputing.himawari.accessory.STLVector;
import net.cellcomputing.himawari.accessory.primitive.p_float;
import net.cellcomputing.himawari.accessory.primitive.p_int;

/**
 * モーションブラーを利用するクラスの基底となるクラス。<br>
 * handling any class capable of being motion blurred.
 * 
 * @author NTT DATA Corporation
 */

public abstract strictfp class CqMotionSpec<T> implements IqMotionSpec<T> {
	
	//タイムスロット。
	//  タイムスロットとはフレーム時間を保存するVectorです。
	private STLVector<Float>	m_aTimes = new STLVector<Float>( Float.class );		///< Array of float timeslot times.
	private STLVector<T>		m_aObjects;		///< Array of motion objects for each timeslot.
	private T					m_DefObject;						///< Default motion object, used when expanding the list of timeslots
	
	/**
	 * デフォルトコンストラクタ
	 * @param a モーションオブジェクトのデフォルトの値。<br>
	 * 
	 */
	@SuppressWarnings("unchecked")
	public CqMotionSpec( final T a )
	{
		m_DefObject = a;
		m_aObjects = new STLVector( a.getClass() );
	}
	
	public CqMotionSpec( final T a, final Class<T> cls )
	{
		m_DefObject = a;
		m_aObjects = new STLVector<T>( cls );
	}
	
	/** 
	 * コピーコンストラクタ
	 */
	@SuppressWarnings("unchecked")
	public CqMotionSpec( final CqMotionSpec<T> From )
	{
		m_aObjects = new STLVector( From.m_DefObject.getClass() );
		this.assignment( From );
	}
	
	/**
	 * デストラクタ
	 */
	public void destruct()
	{
		//for(std::vector<T>::iterator i=m_aObjects.begin(); i<m_aObjects.end(); i++)
		//	ClearMotionObject((*i));
	}
	
	/** 
	 * 代入演算子。<br>
	 * 全ての要素をコピーする。
	 */
	public void assignment( final CqMotionSpec<T> From )
	{
		m_aTimes.clear();
		m_aObjects.clear();
		
		m_aTimes.addAll( From.m_aTimes );
		m_aObjects.addAll( From.m_aObjects );
		m_DefObject = From.m_DefObject;
	}
	
	/** 
	 * モーションオブジェクトとそのフレーム時間を追加。
	 * もしその時間のデータが存在しなければ新規に作成し、存在すればそれを上書きします。
	 * @param time 追加するオブジェクトの時間位置（フレーム時間）
	 * @param Object 指定時間に追加するオブジェクト
	 */
	public void	AddTimeSlot( float time, final T Object )
	{
		p_int iIndex = new p_int();
		if ( iTimes() == 0 )
		{
			m_aTimes.add( time );
			m_aObjects.add( Object );
			return ;
		}
		
		if ( TimeSlotExists( time, iIndex ) )
		{
			ClearMotionObject( m_aObjects.elementAt( iIndex.value ) );
			m_aObjects.setElementAt(Object ,iIndex.value );
		}
		else
		{
			// Insert the timeslot at the proper place.
			int index = 0;
			while(index != m_aTimes.size() && m_aTimes.elementAt(index) < time )
			{
				index++;
			}
			m_aTimes.add( index, time );
			m_aObjects.add( index, Object );
		}
	}
	
	/** 
	 * モーションオブジェクトを指定フレーム時間のオブジェクトと結合する。
	 * もし、その時間位置にオブジェクトがなければ、指定オブジェクトをそのまま新規追加する。
	 * 
	 * @param time 結合対象のフレーム時間
	 * @param Object 結合するオブジェクト
	 */
	public void	ConcatTimeSlot( float time, final T Object )
	{
		p_int iIndex = new p_int();
		if ( TimeSlotExists( time, iIndex ) )
		{
			m_aObjects.setElementAt( ConcatMotionObjects( m_aObjects.elementAt( iIndex.value ), Object ), iIndex.value );
		}
		else
		{
			// Add a new slot a nd set it to the default value before concatenating the
			// specified value.
			AddTimeSlot( time, m_DefObject );
			TimeSlotExists( time, iIndex );
			m_aObjects.setElementAt( ConcatMotionObjects( m_aObjects.elementAt( iIndex.value ), Object ), iIndex.value );
		}
	}
	
	/** 
	 * 全てのタイムスロットに対して指定オブジェクトを結合する
	 * @param Object 結合するオブジェクト
	 */
	public void	ConcatAllTimeSlots( final T Object )
	{
		for( int index=0; index<m_aObjects.size(); index++ ){
			m_aObjects.setElementAt( ConcatMotionObjects( m_aObjects.elementAt( index ), Object ), index );
		}
	}
	
	
	/** 
	 * 指定位置に格納されているフレーム時間を返す。
	 * @param index タイムスロットのインデックス
	 * @return フレーム時間
	 */
	public float	Time( int index )
	{
		if ( m_aTimes.size() == 0 )
			return ( 0.0f );
		else if ( index < 0 )
			return ( m_aTimes.elementAt( 0 ) );
		else if ( index < iTimes() )
			return ( m_aTimes.elementAt( index ) );
		else
			return ( m_aTimes.lastElement() );
	}
	
	/** 
	 * タイムスロットに格納されている数を返す。
	 * @return タイムスロットのサイズ
	 */
	public int	iTimes()
	{
		return ( m_aTimes.size() );
	}
	
	/** 
	 * 
	 * このモーションブロックが実際にモーションブラーを行っているか否か。
	 * @return モーションブラーを行っているか否か
	 */
	public boolean fMotionBlurred()
	{
		return ( iTimes() > 1 );
	}
	
	/** 
	 * 
	 * 指定フレーム時間がタイムスロットに格納されているインデックスを返す。<br>
	 * もし、その時間に正確に一致するインデックスがない場合には、最も近いインデックスを返し、０〜１の範囲でその誤差がFractionにセットされる。
	 * Get the index of the timeslot for the specified time, and if not exact, the fractional
	 * distance between it and the following timeslot 0-1.
	 * 
	 * @param time I)フレーム時間
	 * @param iIndex O)タイムスロットのインデックス
	 * @param Fraction O)誤差比率
	 * @return インデックスが正確かどうか。
	 */
	public boolean GetTimeSlot( float time, p_int iIndex, p_float Fraction )
	{
		assert( iTimes() > 0 );
		
		if ( time >= m_aTimes.lastElement() )
		{
			iIndex.value = m_aTimes.size() - 1;
			return ( true );
		}
		else if ( time <= m_aTimes.firstElement() )
		{
			iIndex.value = 0;
			return ( true );
		}
		else
		{
			// Find the appropriate time span.
			iIndex.value = 0;
			while ( time >= m_aTimes.elementAt( iIndex.value + 1 ) )
				iIndex.value += 1;
			Fraction.value = ( time - m_aTimes.elementAt( iIndex.value ) ) / ( m_aTimes.elementAt( iIndex.value + 1 ) - m_aTimes.elementAt( iIndex.value ) );
			return ( m_aTimes.elementAt( iIndex.value ) == time );
		}
	}
	
	/** 
	 * タイムスロット中の指定フレーム時間が格納されているインデックスを返す。
	 * @param time I)フレーム時間
	 * @param iIndex O)インデックス
	 * @return インデックスが取得できたかどうか。
	 */
	public boolean	TimeSlotExists( float time, p_int iIndex )
	{
		//assert( cTimes() > 0 );
		//assert(time>=0.0f);
		
		// Find the appropriate time span.
		iIndex.value = 0;
		while ( iIndex.value < iTimes() && time != m_aTimes.elementAt( iIndex.value ) )
			iIndex.value += 1;
		return ( iIndex.value < iTimes() );
	}
	
	/** 
	 * 指定時間のモーションオブジェクトの取得。<br>
	 * 指定した時間がない場合、周辺のデータからリニアに補完されたモーションオブジェクトが返る。
	 * @param time フレーム時間
	 * @return 指定時間のモーションオブジェクト
	 */
	public T	GetMotionObjectInterpolated( float time )
	{
		p_int iIndex = new p_int();
		p_float Fraction = new p_float();
		if ( GetTimeSlot( time, iIndex, Fraction ) )
			return ( m_aObjects.elementAt(iIndex.value) );
		else
			return ( LinearInterpolateMotionObjects( Fraction.value, m_aObjects.elementAt(iIndex.value), m_aObjects.elementAt( iIndex.value + 1 ) ) );
	}
	
	/** 
	 * 指定時間のモーションオブジェクトの取得。<br>
	 * 指定した時間がない場合、デフォルトのモーションオブジェクトが返る。
	 * @param time フレーム時間
	 * @return 指定時間のモーションオブジェクト
	 */
	public T GetMotionObject( float time )
	{
		p_int iIndex = new p_int();
		p_float Fraction = new p_float();
		if ( GetTimeSlot( time, iIndex, Fraction ) )
			return ( m_aObjects.elementAt(iIndex.value) );
		else
		{
			assert( false );
			return ( m_DefObject );
		}
	}
	
	/** 
	 * 指定インデックスに格納されているモーションオブジェクトを返す。
	 * @param iIndex タイムスロット（Vector）のインデックス
	 * @return 見つかったモーションオブジェクト
	 */
	public T GetMotionObject( int iIndex )
	{
		assert( iIndex >= 0 && iIndex < iTimes() );
		return ( m_aObjects.elementAt(iIndex) );
	}
	
	/** 
	 * デフォルトのモーションオブジェクトを設定。<br>
	 * デフォルトのモーションオブジェクトはタイムスロットが拡張される際の初期値などに使われる。
	 * @param a デフォルトのモーションオブジェクト
	 */
	public void	SetDefaultObject( final T a )
	{
		m_DefObject = a;
	}
	
	/** 
	 * 全てのオブジェクトとタイムスロットをクリアする。
	 *
	 */
	public void	Reset()
	{
		m_aTimes.clear();
		for ( T tmp : m_aObjects ) {
			ClearMotionObject(tmp);
		}
		m_aObjects.clear();
	}
	
	/**
	 * 
	 * モーションオブジェクトをクリアする。<br>
	 * オブジェクトの実装形態にあわせて実装してください。
	 * 
	 * @param A	クリア対象のオブジェクト
	 */
//	abstract public void	ClearMotionObject( T A );
	
	/**
	 * 
	 * モーションオブジェクトを結合する。<br>
	 * オブジェクトの実装形態にあわせて実装してください。
	 * 
	 * @param A	モーションオブジェクト
	 * @param B	モーションオブジェクト
	 * @return	結合結果
	 */
//	abstract public T ConcatMotionObjects( final T A, final T B );
	
	/**
	 * 
	 * モーションオブジェクト間をリニアに補完する。<br>
	 * オブジェクトの実装形態にあわせて実装してください。
	 * 
	 * @param Fraction	誤差比率
	 * @param A	補完対象の位置より前の位置にあるモーションオブジェクト
	 * @param B	補完対象の位置より後の位置にあるモーションオブジェクト
	 * @return	補完結果
	 */
//	abstract public T LinearInterpolateMotionObjects( float Fraction, final T A, final T B );
	
	
}
