// 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 static net.cellcomputing.himawari.library.Float_h.*;
import static net.cellcomputing.himawari.library.EqVariableType.*;
import static net.cellcomputing.himawari.library.EqVariableClass.*;
import static net.cellcomputing.himawari.library.EqEnvVars.*;
import static net.cellcomputing.himawari.library.RiGlobal.*;
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;
import net.cellcomputing.himawari.library.types.CqVector4D;

/**
 *  Bilinear spline patch mesh
 *  双線型のスプラインパッチメッシュ
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqSurfacePatchMeshBilinear extends CqSurface{
	
	
	protected int m_uPatches;   	///< Number of patches in u.
	protected int m_vPatches;		///< Number of patches in v.
	protected int m_nu;   			///< Number of control points in u.
	protected int m_nv;				///< Number of control points in v.
	protected boolean m_uPeriodic; 	///< Is patches mesh periodic in u?
	protected boolean m_vPeriodic;	///< Is patches mesh periodic in v?
	
	
	/**
	 * コンストラクタ<BR>初期値が2つ与えられた場合。
	 * 
	 * @param nu
	 * @param nv
	 */
	public CqSurfacePatchMeshBilinear( int nu, int nv )
	{
		this( nu, nv, false, false );
	}
	
	/**
	 * コンストラクタ<BR>初期値が3つ与えられた場合。
	 * 
	 * @param nu
	 * @param nv
	 * @param uPeriodic
	 */
	public CqSurfacePatchMeshBilinear( int nu, int nv, boolean uPeriodic )
	{
		this( nu, nv, uPeriodic, false );
	}
	
	/**
	 * コンストラクタ<BR>初期値が全て与えられた場合。
	 * 
	 * @param nu
	 * @param nv
	 * @param uPeriodic
	 * @param vPeriodic
	 */
	public CqSurfacePatchMeshBilinear( int nu, int nv, boolean uPeriodic, boolean vPeriodic)
	{
		super();
		m_nu = nu ;
		m_nv = nv ;
		m_uPeriodic = uPeriodic ;
		m_vPeriodic = vPeriodic ;
		m_uPatches = ( uPeriodic ) ? nu : nu - 1;
		m_vPatches = ( vPeriodic ) ? nv : nv - 1;
	}
	
	/**
	 * コピーコンストラクタ
	 * 
	 * @param From	コピー元
	 */
	public CqSurfacePatchMeshBilinear( final CqSurfacePatchMeshBilinear From )
	{
		this.assignment( From );
	}
	
	/**
	 * デストラクタ
	 */
	public void destruct()
	{}
	
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.CqSurface#SetDefaultPrimitiveVariables(boolean)
	 */
	public void SetDefaultPrimitiveVariables( boolean bUseDef_st )
	{}
	
	/** 
	 * Get the boundary extents in camera space of the surface patch mesh
	 * @see net.cellcomputing.himawari.library.IqSurface#Bound()
	 */
	public	CqBound	Bound()
	{
		assert( null != P() );
		
		// Get the boundary in camera space.
		CqVector3D	vecA = new CqVector3D( FLT_MAX, FLT_MAX, FLT_MAX );
		CqVector3D	vecB = new CqVector3D( -FLT_MAX, -FLT_MAX, -FLT_MAX );
		
		for ( int i = 0; i < P().Size(); i++ )
		{
			CqVector3D	vecV = new CqVector3D( ((CqVector4D)P().pValue_get( i ,0 )) );
			if ( vecV.x < vecA.x ) vecA.x( vecV.x );
			if ( vecV.y < vecA.y ) vecA.y( vecV.y );
			if ( vecV.x > vecB.x ) vecB.x( vecV.x );
			if ( vecV.y > vecB.y ) vecB.y( vecV.y );
			if ( vecV.z < vecA.z ) vecA.z( vecV.z );
			if ( vecV.z > vecB.z ) vecB.z( vecV.z );
		}
		CqBound	B = new CqBound();
		B.vecMin().assignment( vecA );
		B.vecMax().assignment( vecB );
		return ( AdjustBoundForTransformationMotion( B ) );
		
	}
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#Dice()
	 */
	public	CqMicroPolyGridBase Dice()
	{
		return null;
	}
	
	public int PatchCoord( int v, int u )
	{
		return ((((v)%m_nv)*m_nu)+((u)%m_nu));
	}
	
	public int PatchCorner( int v, int u, int nvaryingu, int nvaryingv )
	{
		return ((((v)%nvaryingv)*nvaryingu)+((u)%nvaryingu));
	}
	
	/**
	 * Split the patch mesh into individual patches and post them.
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#Split(net.cellcomputing.himawari.accessory.STLVector)
	 */
	public int Split( STLVector<CqBasicSurface> aSplits )
	{
		int cSplits = 0;
		
		// Create a surface patch
		int iP = 0;
		int MyUses = Uses();
		
		final float[] pTC = pAttributes().GetFloatAttribute( "System", "TextureCoordinates" );
		CqVector2D st1 = new CqVector2D( pTC[ 0 ], pTC[ 1 ] );
		CqVector2D st2 = new CqVector2D( pTC[ 2 ], pTC[ 3 ] );
		CqVector2D st3 = new CqVector2D( pTC[ 4 ], pTC[ 5 ] );
		CqVector2D st4 = new CqVector2D( pTC[ 6 ], pTC[ 7 ] );
		
		for ( int i = 0; i < m_vPatches; i++ )
		{
			float v0 = ( 1.0f / m_vPatches ) * i;
			float v1 = ( 1.0f / m_vPatches ) * ( i + 1 );
			int j;
			for ( j = 0; j < m_uPatches; j++ )
			{
				CqSurfacePatchBilinear pSurface = new CqSurfacePatchBilinear();
				pSurface.SetSurfaceParameters( this );
				
				int iTa = PatchCoord( i, j );
				int iTb = PatchCoord( i, j + 1 );
				int iTc = PatchCoord( i + 1, j );
				int iTd = PatchCoord( i + 1, j + 1 );
				
				float u0 = ( 1.0f / m_uPatches ) * j;
				float u1 = ( 1.0f / m_uPatches ) * ( j + 1 );
				
				// Copy any primitive variables.
				for ( CqParameter iUP : aUserParams() )
				{
					if ( iUP.Class().getValue() == class_varying )
					{
						// Copy any 'varying' class primitive variables.
						CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
						pNewUP.SetSize( pSurface.cVarying() );
						
						pNewUP.SetValue( iUP, 0, iTa );
						pNewUP.SetValue( iUP, 1, iTb );
						pNewUP.SetValue( iUP, 2, iTc );
						pNewUP.SetValue( iUP, 3, iTd );
						
						pSurface.AddPrimitiveVariable( pNewUP );
					}
					else if ( iUP.Class().getValue() == class_vertex )
					{
						// Copy any 'vertex' class primitive variables.
						CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
						pNewUP.SetSize( pSurface.cVertex() );
						
						iP = PatchCoord( i, j );
						pNewUP.SetValue( iUP, 0, iP );
						iP = PatchCoord( i, j + 1 );
						pNewUP.SetValue( iUP, 1, iP );
						iP = PatchCoord( i + 1, j );
						pNewUP.SetValue( iUP , 2, iP );
						iP = PatchCoord( i + 1, j + 1 );
						pNewUP.SetValue( iUP, 3, iP );
						
						pSurface.AddPrimitiveVariable( pNewUP );
					}
					else if ( iUP.Class().getValue() == class_uniform )
					{
						// Copy any 'uniform' class primitive variables.
						CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
						pNewUP.SetSize( pSurface.cUniform() );
						pNewUP.SetValue( iUP, 0, j );
						pSurface.AddPrimitiveVariable( pNewUP );
					}
					else if ( iUP.Class().getValue() == class_constant )
					{
						// Copy any 'constant' class primitive variables.
						CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
						pNewUP.SetSize( 1 );
						
						pNewUP.SetValue( iUP, 0, 0 );
						pSurface.AddPrimitiveVariable( pNewUP );
					}
				}
				
				// If the shaders need u/v or s/t and they are not specified, then we need to put them in as defaults.
				if ( USES( MyUses, EnvVars_u ) && !bHasVar(EnvVars_u) )
				{
					pSurface.AddPrimitiveVariable( new CqParameterTypedVarying<p_float, p_float>( "u",new EqVariableType( type_float ), p_float.class, p_float.class ) );
					pSurface.u().SetSize( 4 );
					((p_float)pSurface.u().pValue_get( 0 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(1.0f), new p_float(0.0f), new p_float(1.0f), u0, v0, p_float.class ).value;
					((p_float)pSurface.u().pValue_get( 1 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(1.0f), new p_float(0.0f), new p_float(1.0f), u1, v0, p_float.class ).value;
					((p_float)pSurface.u().pValue_get( 2 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(1.0f), new p_float(0.0f), new p_float(1.0f), u0, v1, p_float.class ).value;
					((p_float)pSurface.u().pValue_get( 3 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(1.0f), new p_float(0.0f), new p_float(1.0f), u1, v1, p_float.class ).value;
				}
				
				if ( USES( MyUses, EnvVars_v ) && !bHasVar(EnvVars_v) )
				{
					pSurface.AddPrimitiveVariable( new CqParameterTypedVarying<p_float, p_float>( "v", new EqVariableType( type_float ), p_float.class, p_float.class ) );
					pSurface.v().SetSize( 4 );
					((p_float)pSurface.v().pValue_get( 0 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(0.0f), new p_float(1.0f), new p_float(1.0f), u0, v0, p_float.class ).value;
					((p_float)pSurface.v().pValue_get( 1 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(0.0f), new p_float(1.0f), new p_float(1.0f), u1, v0, p_float.class ).value;
					((p_float)pSurface.v().pValue_get( 2 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(0.0f), new p_float(1.0f), new p_float(1.0f), u0, v1, p_float.class ).value;
					((p_float)pSurface.v().pValue_get( 3 , 0 )).value = BilinearEvaluate( new p_float(0.0f), new p_float(0.0f), new p_float(1.0f), new p_float(1.0f), u1, v1, p_float.class ).value;
				}
				
				if ( USES( MyUses, EnvVars_s ) && !bHasVar(EnvVars_s) )
				{
					pSurface.AddPrimitiveVariable( new CqParameterTypedVarying<p_float, p_float>( "s", new EqVariableType( type_float ), p_float.class, p_float.class ) );
					pSurface.s().SetSize( 4 );
					((p_float)pSurface.s().pValue_get( 0 , 0 )).value = BilinearEvaluate( new p_float(st1.x), new p_float(st2.x), new p_float(st3.x), new p_float(st4.x), u0, v0, p_float.class ).value;
					((p_float)pSurface.s().pValue_get( 1 , 0 )).value = BilinearEvaluate( new p_float(st1.x), new p_float(st2.x), new p_float(st3.x), new p_float(st4.x), u1, v0, p_float.class ).value;
					((p_float)pSurface.s().pValue_get( 2 , 0 )).value = BilinearEvaluate( new p_float(st1.x), new p_float(st2.x), new p_float(st3.x), new p_float(st4.x), u0, v1, p_float.class ).value;
					((p_float)pSurface.s().pValue_get( 3 , 0 )).value = BilinearEvaluate( new p_float(st1.x), new p_float(st2.x), new p_float(st3.x), new p_float(st4.x), u1, v1, p_float.class ).value;
				}
				
				if ( USES( MyUses, EnvVars_t ) && !bHasVar(EnvVars_t) )
				{
					pSurface.AddPrimitiveVariable( new CqParameterTypedVarying<p_float, p_float>( "t", new EqVariableType( type_float ), p_float.class, p_float.class ) );
					pSurface.t().SetSize( 4 );
					((p_float)pSurface.t().pValue_get( 0 , 0 )).value = BilinearEvaluate( new p_float(st1.y), new p_float(st2.y), new p_float(st3.y), new p_float(st4.y), u0, v0, p_float.class ).value;
					((p_float)pSurface.t().pValue_get( 1 , 0 )).value = BilinearEvaluate( new p_float(st1.y), new p_float(st2.y), new p_float(st3.y), new p_float(st4.y), u1, v0, p_float.class ).value;
					((p_float)pSurface.t().pValue_get( 2 , 0 )).value = BilinearEvaluate( new p_float(st1.y), new p_float(st2.y), new p_float(st3.y), new p_float(st4.y), u0, v1, p_float.class ).value;
					((p_float)pSurface.t().pValue_get( 3 , 0 )).value = BilinearEvaluate( new p_float(st1.y), new p_float(st2.y), new p_float(st3.y), new p_float(st4.y), u1, v1, p_float.class ).value;
				}
				
				aSplits.add( pSurface );
				cSplits++;
			}
		}
		return ( cSplits );
	}
	
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#Diceable()
	 */
	public boolean	Diceable()
	{
		return ( false );
	}
	
	/**
	 * Determine whether the passed surface is valid to be used as a
	 * frame in motion blur for this surface.
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#IsMotionBlurMatch(net.cellcomputing.himawari.library.CqBasicSurface)
	 */
	public boolean	IsMotionBlurMatch( CqBasicSurface pSurf )
	{
		return( false );
	}
	
	public	int	cUniform()
	{
		return ( m_uPatches * m_vPatches );
	}
	public	int	cVarying()
	{
		return ( ( ( m_uPeriodic ) ? m_uPatches : m_uPatches + 1 ) * ( ( m_vPeriodic ) ? m_vPatches : m_vPatches + 1 ) );
	}
	public	int	cVertex()
	{
		return ( m_nu * m_nv );
	}
	public	int	cFaceVarying() 
	{
		//TODO Must work out what this value should be.
		return ( 1 );
	}
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#SurfaceParametersAtVertex(int)
	 */
	public CqVector3D	SurfaceParametersAtVertex( int index )
	{
		CqVector3D	vec = new CqVector3D( 0, 0, 0 );
		
		float u = index % m_nu ;
		u /= ( m_nu - 1 );
		float v = index / (float)m_nu ;
		v /= ( m_nv - 1 );
		
		vec.x( u );
		vec.y( v );
		return ( vec );
	}
	
	/**
	 * 代入演算子のオーバーロード
	 * 
	 * @param From	代入元
	 * @return	自分自身
	 */
	public CqSurfacePatchMeshBilinear assignment( final CqSurfacePatchMeshBilinear From )
	{
		// Perform per surface copy function
		super.assignment( From );
		
		m_uPatches = From.m_uPatches;
		m_vPatches = From.m_vPatches;
		m_nu = From.m_nu;
		m_nv = From.m_nv;
		m_uPeriodic = From.m_uPeriodic;
		m_vPeriodic = From.m_vPeriodic;
		
		return ( this );
	}
	
	
}
