// 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.library.types.CqVector2D;
import net.cellcomputing.himawari.library.types.CqVector3D;

/**
 * 
 * Class which stores a single moving micropolygon.
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqMicroPolygonMotionPoints extends CqMicroPolygon {
	
	private CqBound  m_Bound = new CqBound();   ///< Stored bound.
	private CqBoundList m_BoundList = new CqBoundList(); ///< List of bounds to get a tighter fit.
	private boolean  m_BoundReady;     ///< Flag indicating the boundary has been initialised.
	private STLVector<p_float> m_Times = new STLVector<p_float>(p_float.class);
	private STLVector<CqMovingMicroPolygonKeyPoints> m_Keys = new STLVector<CqMovingMicroPolygonKeyPoints>(CqMovingMicroPolygonKeyPoints.class);
	
	public CqMicroPolygonMotionPoints(){
		super();
		m_BoundReady = false;
	}
	
//	public ~CqMicroPolygonMotionPoints()
//	{
//	std::vector<CqMovingMicroPolygonKeyPoints*>::iterator ikey;
//	for( ikey = m_Keys.begin(); ikey != m_Keys.end(); ikey++ )
//	delete( (*ikey) );
//	}
	
	/** 
	 * Store the vectors of the micropolygon at the specified shutter time.
	 * @param vA 3D Vector.
	 * @param vB 3D Vector.
	 * @param vC 3D Vector.
	 * @param vD 3D Vector.
	 * @param time Float shutter time that this MPG represents.
	 */
	public void AppendKey( final CqVector3D vA, float radius, float time )
	{
		//	assert( time >= m_Times.back() );
		
		// Add a new planeset at the specified time.
		CqMovingMicroPolygonKeyPoints pMP = new CqMovingMicroPolygonKeyPoints( vA, radius );
		m_Times.add( new p_float(time) );
		m_Keys.add( pMP );
		if ( m_Times.size() == 1 )
			m_Bound = new CqBound( pMP.GetTotalBound() );
		else
			m_Bound.Encapsulate( pMP.GetTotalBound() );
	}
	
	public void DeleteVariables( boolean all )
	{}
	
	// Overrides from CqMicroPolygon
	
	/** 
	 * Determinde whether the 2D point specified lies within this micropolygon.
	 * @param vecP 2D vector to test for containment.
	 * @param Depth Place to put the depth if valid intersection.
	 * @param time The frame time at which to check containment.
	 * @return Boolean indicating sample hit.
	 */
	public boolean fContains( final CqVector2D vecP, p_float Depth, float time )
	{
		int iIndex = 0;
		float Fraction = 0.0f;
		boolean Exact = true;
		
		if ( time > m_Times.firstElement().value )
		{
			if ( time >= m_Times.lastElement().value )
				iIndex = m_Times.size() - 1;
			else
			{
				// Find the appropriate time span.
				iIndex = 0;
				while ( time >= m_Times.get( iIndex + 1 ).value )
					iIndex += 1;
				Fraction = ( time - m_Times.get( iIndex ).value ) / ( m_Times.get( iIndex + 1 ).value - m_Times.get( iIndex ).value );
				Exact = ( m_Times.get( iIndex ).value == time );
			}
		}
		
		if( Exact )
		{
			CqMovingMicroPolygonKeyPoints pMP1 = m_Keys.get( iIndex );
			return( pMP1.fContains( vecP, Depth, time ) );
		}
		else
		{
			CqMovingMicroPolygonKeyPoints pMP1 = m_Keys.get( iIndex );
			CqMovingMicroPolygonKeyPoints pMP2 = m_Keys.get( iIndex + 1 );
			// Check against each line of the quad, if outside any then point is outside MPG, therefore early exit.
			CqVector3D MidPoint = ( ( pMP2.m_Point0.sub( pMP1.m_Point0 ) ).mul( Fraction ) ).add( pMP1.m_Point0 );
			float MidRadius = ( ( pMP2.m_radius - pMP1.m_radius ) * Fraction ) + pMP1.m_radius;
			if( ( new CqVector2D( MidPoint.x, MidPoint.y ).sub( vecP ) ).Magnitude() < MidRadius )
			{
				Depth.value = MidPoint.z;
				return( true );
			}
			return ( false );
		}
		
	}
	
	//【改】aqsis 1.01で変更有り追加*********************************
	public void CalculateTotalBound()
	{
	    assert( null != m_Keys.get( 0 ) );

	    m_Bound = m_Keys.get( 0 ).GetTotalBound();
	    for ( CqMovingMicroPolygonKeyPoints i : m_Keys )
	        m_Bound.Encapsulate( i.GetTotalBound() );
	}
	//***********************************************************
	
	//【改】aqsis 1.01で変更有り*********************************
	public CqBound GetTotalBound( boolean fForce )
	{
		/*変更後消去
		if ( fForce )
		{
			assert( null != m_Keys.get(0) );
			
			m_Bound = m_Keys.get(0).GetTotalBound();
			for ( CqMovingMicroPolygonKeyPoints i : m_Keys )
				m_Bound.Encapsulate( (i).GetTotalBound() );
		}
		*/
		return ( new CqBound( m_Bound ) );
	}
	//**********************************************************
	public final CqBound GetTotalBound()
	{
		return GetTotalBound( false );
	}
	
	public int cSubBounds()
	{
		if ( !m_BoundReady )
			BuildBoundList();
		return ( m_BoundList.Size() );
	}
	
	public CqBound SubBound( int iIndex, p_float time )
	{
		if ( !m_BoundReady )
			BuildBoundList();
		assert( iIndex < m_BoundList.Size() );
		time.value = m_BoundList.GetTime( iIndex );
		return ( m_BoundList.GetBound( iIndex ) );
	}
	
	/** 
	 * Calculate a list of 2D bounds for this micropolygon,
	 */
	public void BuildBoundList()
	{
		m_BoundList.Clear();
		
		assert( null != m_Keys.get(0) );
		
		CqBound start = m_Keys.get(0).GetTotalBound();
		float	startTime = m_Times.get(0).value;
		int		cTimes = m_Keys.size();
		for ( int i = 1; i < cTimes; i++ )
		{
			CqBound end  = m_Keys.get(i).GetTotalBound();
			CqBound mid0 = new CqBound( start );
			CqBound mid1 = new CqBound();
			float endTime = m_Times.get( i ).value;
			float time = startTime;
			
			int d;
			// arbitary number of divisions, should be related to screen size of blur
			int divisions = 4;
			float delta = 1.0f / (float)( divisions );
			m_BoundList.SetSize( divisions );
			for ( d = 1; d <= divisions; d++ )
			{
				mid1.vecMin().assignment( ( end.vecMin().sub( start.vecMin() ) ).mul(delta).add( start.vecMin() ) );
				mid1.vecMax().assignment( ( end.vecMax().sub( start.vecMax() ) ).mul(delta).add( start.vecMax() ) );
				m_BoundList.Set( d-1, mid0.Combine( mid1 ), time );
				time = delta * ( endTime - startTime ) + startTime;
				mid0.assignment( mid1 );
				delta += delta;
			}
			start = end;
			startTime = endTime;
		}
		m_BoundReady = true;
		
	}
	

	//【改】aqsis1.01変更・追加**********************************************************
	public  boolean IsMoving()
	{
		return true;
	}
	
	/** 
	 * Sample the specified point against the MPG at the specified time.
	 * @param vecSample 2D vector to sample against.
	 * @param time Shutter time to sample at.
	 * @param D Storage for depth if valid hit.
	 * @return Boolean indicating smaple hit.
	 */
//	public boolean Sample( final CqVector2D vecSample, p_float D, float time )
	public boolean Sample( final SqSampleData sample, p_float D, float time, boolean UsingDof )
	{
		final CqVector2D vecSample = sample.m_Position;
		return( fContains( vecSample, D, time ) );
	}
	
	//ここまで*********************************************************************************
	@SuppressWarnings("unused")
	private CqMicroPolygonMotionPoints( final CqMicroPolygonMotionPoints From )
	{}
	
	
}
