// 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.EqEnvVars.*;
import static net.cellcomputing.himawari.library.EqVariableType.*;
import static net.cellcomputing.himawari.library.EqVariableClass.*;
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.CqMatrix;
import net.cellcomputing.himawari.library.types.CqVector3D;
import net.cellcomputing.himawari.library.types.CqVector4D;
import net.cellcomputing.himawari.util.HimawariLogger;

/**
 * A (possibly disconnected) group of linear curves, such as those specified in
 * a RIB call to Curves "linear".
 * 直線のひとつのグループをRIBから呼び出されて指定されます。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqLinearCurvesGroup extends CqCurvesGroup {
	
	/**
	 * コンストラクタ<BR>初期値が二つ与えられた場合。
	 * 
	 * @param ncurves
	 * @param nvertices
	 */
	public CqLinearCurvesGroup( int ncurves, int nvertices[] )
	{
		this( ncurves, nvertices, false );
	}
	
	/**
	 * コンストラクタ<BR>初期値が三つ与えられた場合。
	 * 
	 * @param ncurves
	 * @param nvertices
	 * @param periodic
	 */
	public CqLinearCurvesGroup( int ncurves, int nvertices[], boolean periodic )
	{
		assert( nvertices != null );
		
		HimawariLogger logger = HimawariLogger.getLogger();
		m_ncurves = ncurves;
		m_periodic = periodic;
		
		// it makes no sense to have a periodic curve group with a segment
		//  that has only two vertices - check for this just in case
		//  because the cVarying equations don't work; also add up the total
		//  number of vertices
		m_nTotalVerts = 0;
		int i;
		for ( i = 0; i < m_ncurves; i++ ) {
			m_nTotalVerts += nvertices[ i ];
			
			if ( ( nvertices[ i ] <= 2 ) && m_periodic )
				logger.warning( "Periodic linear curves should have more than two vertices\n" );
		}
		
		// copy the array of numbers of vertices
		m_nvertices.clear();
		m_nvertices.ensureCapacity( m_ncurves );
//		m_nvertices.reserve( m_ncurves );
		
		for ( i = 0; i < m_ncurves; i++ )
			m_nvertices.add( nvertices[ i ] );
	}
	
	/**
	 * コピーコンストラクタ
	 * 
	 * @param from	コピー元
	 */
	public CqLinearCurvesGroup( final CqLinearCurvesGroup from )
	{
		super( from );
		this.assignment( from );
	}
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.CqBasicSurface#destruct()
	 */
	public void destruct()
	{
		m_nvertices.clear();
	}
	
	/**
	 * Assignment operator.
	 *
	 * @param from  CqCubicCurveSegment to set this one equal to.
	 * @return Reference to *this.
	 */
	public CqLinearCurvesGroup assignment( final CqLinearCurvesGroup from )
	{
		// base class assignment
		super.assignment( from );
		
		return ( this );
	}
	
	/**
	 * Splits a CqLinearCurvesGroup object.
	 *
	 * The initial, naiive implementation here is immediately to split the group of
	 * curves into CqLinearCurveSegment objects.  Perhaps a better way would be to
	 * manage splitting of curve groups into other curve groups until they're of a
	 * small enough size to become curve segments... ?
	 *
	 * @param aSplits       Vector of split objects.
	 */
	public	int Split( STLVector<CqBasicSurface> aSplits )
	{
		int nSplits = 0;      // number of splits we've done
		
		int bUses = Uses();
		
		// create each linear curve, filling in its variables as we go
		int vertexI = 0;      // keeps track of the current vertex index
		int uniformI = 0;     // keeps track of the uniform param index
		// we process all the curves in the group...
		for ( int curveI = 0; curveI < m_ncurves; curveI++ )
		{
			int lastSegment;
			if ( m_periodic )
				lastSegment = m_nvertices.get( curveI );
			else
				lastSegment = m_nvertices.get( curveI ) - 1;
			
			int firstVertex = vertexI;
			
			// for each curve, we then process all its segments
			for ( int segI = 0; segI < lastSegment; segI++ )
			{
				int nextVertex;
				if ( segI == ( m_nvertices.get( curveI ) - 1 ) )
					nextVertex = firstVertex;
				else
					nextVertex = vertexI + 1;
				
				// create the new CqLinearCurveSegment for the current
				//  curve segment
				CqLinearCurveSegment pSeg = new CqLinearCurveSegment();
				pSeg.SetSurfaceParameters( this );
				
				// set the value of "v"
				if ( USES( bUses, EnvVars_v ) )
				{
					float vv = ( float ) segI / ( float ) lastSegment;
					float vvnext = ( float )( segI + 1 ) / ( float ) lastSegment;
					CqParameterTypedVarying <p_float, p_float > pVP = new CqParameterTypedVarying< p_float, p_float> ( "v", new EqVariableType( type_float), p_float.class, p_float.class );
					pVP.SetSize( pSeg.cVarying() );
					((p_float) pVP.pValue_get( 0 ,0 ) ).value = vv;
					((p_float) pVP.pValue_get( 1 ,0 ) ).value = vvnext;
					pSeg.AddPrimitiveVariable( pVP );
				}
				
				for( CqParameter iUP : aUserParams() ) 
				{
					if ( iUP.Class().getValue() == class_varying || iUP.Class().getValue() == class_vertex )
					{
						// copy "varying" or "vertex" class
						//  variables
						CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
						
						assert( pSeg.cVarying() == pSeg.cVertex() );
						pNewUP.SetSize( pSeg.cVarying() );
						pNewUP.SetValue( iUP, 0, vertexI );
						pNewUP.SetValue( iUP, 1, nextVertex );
						pSeg.AddPrimitiveVariable( pNewUP );
						
					}
					else if ( iUP.Class().getValue() == class_uniform )
					{
						// copy "uniform" class variables
						CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
						
						pNewUP.SetSize( pSeg.cUniform() );
 						pNewUP.SetValue( iUP, 0, uniformI );
						pSeg.AddPrimitiveVariable( pNewUP );
					}
					else if ( iUP.Class().getValue() == class_constant )
					{
						// copy "constant" class variables
						CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
						
						pNewUP.SetSize( 1 );
						pNewUP.SetValue( iUP, 0, 0 );
						pSeg.AddPrimitiveVariable( pNewUP );
					} // if
				} // for each parameter
				
				++vertexI;
				aSplits.add( pSeg );
				++nSplits;
				
			} // for each curve segment
			
			if ( !m_periodic )
				++vertexI;
			
			++uniformI;
		} // for each curve
		
		return nSplits;
	}
	
	/* (non-Javadoc)
	 * @see net.cellcomputing.himawari.library.IqSurface#Transform(net.cellcomputing.himawari.library.types.CqMatrix, net.cellcomputing.himawari.library.types.CqMatrix, net.cellcomputing.himawari.library.types.CqMatrix)
	 */
	public void Transform( final CqMatrix matTx, final CqMatrix matITTx, final CqMatrix matRTx )
	{
		Transform( matTx, matITTx, matRTx, 0 );
	}
	
	
	/**
	 * Transforms this GPrim using the specified matrices.
	 *
	 * @param matTx         Reference to the transformation matrix.
	 * @param matITTx       Reference to the inverse transpose of the 
	 *                        transformation matrix, used to transform normals.
	 * @param matRTx        Reference to the rotation only transformation matrix, 
	 *                        used to transform vectors.
	 * @param iTime			The frame time at which to perform the transformation.
	 */
	public void Transform( final CqMatrix matTx, final CqMatrix matITTx, final CqMatrix matRTx, int iTime )
	{
		// First, we want to transform the width array.  For each curve in the
		//  group, there are as many width parameters as there are vertices,
		//  so each vertex matches exactly with a width; no stuffing around is
		//  required.
		PopulateWidth();
		assert( cVarying() == cVertex() );
		
		for ( int i = 0; i < cVarying(); i++ )
		{
			// first, create a horizontal vector in the new space which is
			//  the length of the current width in current space
			CqVector3D horiz = new CqVector3D( 1, 0, 0 );
			horiz = matITTx.multiply( horiz ) ;
			horiz.assignMul( ((p_float)width().pValue_get( i ,0 )).value / horiz.Magnitude() );
			
			// now, create two points; one at the vertex in current space
			//  and one which is offset horizontally in the new space by
			//  the width in the current space.  transform both points
			//  into the new space
			CqVector3D pt = new CqVector3D( ((CqVector4D)P().pValue_get( i ,0 )) );
			CqVector3D pt_delta = new CqVector3D( pt.add( horiz ));
			pt = matTx.multiply( pt );
			pt_delta = matTx.multiply( pt_delta );
			
			// finally, find the difference between the two points in
			//  the new space - this is the transformed width
			CqVector3D widthVector = new CqVector3D( pt_delta.sub( pt ));
			((p_float)width().pValue_get( i , 0 )).value = widthVector.Magnitude() ;
		}
		
		// finally, we want to call the base class transform
//		CqCurve::Transform( matTx, matITTx, matRTx, iTime );
		super.Transform( matTx, matITTx, matRTx, iTime );
	}
	

	/** Returns the number of facevarying class parameters. */
	public	int cFaceVarying() 
	{
		return 0;
	}
	
	/** Returns the number of uniform class parameters. */
	public	int cUniform() 
	{
		return m_ncurves;
	}
	
	/** Returns the number of varying class parameters. */
	public int cVarying() 
	{
		return m_nTotalVerts;
	}
	
	/** Returns the number of vertex class parameters. */
	public	int cVertex() 
	{
		return m_nTotalVerts;
	}
	
	/** Returns a string name of the class. */
	public String strName() 
	{
		return "CqLinearCurvesGroup";
	}
}
