// 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.EnvVars_v;
import static net.cellcomputing.himawari.library.EqVariableClass.class_constant;
import static net.cellcomputing.himawari.library.EqVariableClass.class_uniform;
import static net.cellcomputing.himawari.library.EqVariableClass.class_varying;
import static net.cellcomputing.himawari.library.EqVariableClass.class_vertex;
import static net.cellcomputing.himawari.library.EqVariableType.type_float;
import static net.cellcomputing.himawari.library.RiGlobal.USES;
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;

/**
 * 
 * A (possibly disconnected) group of cubic curves, such as those specified in
 * a RIB call to Curves "cubic".
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqCubicCurvesGroup extends CqCurvesGroup {

	/**
	 * Constructor for a CqCubicCurvesGroup.
	 *
	 * @param ncurves       Number of curves in the group.
	 * @param nvertices     Number of vertices per curve.
	 * @param periodic      true if curves in the group are periodic.
	 */
	public CqCubicCurvesGroup( int ncurves, int[] nvertices, boolean periodic )
	{
		super();
		
	    m_ncurves = ncurves;
	    m_periodic = periodic;

	    // add up the total number of vertices
	    m_nTotalVerts = 0;
	    int i;
	    for ( i = 0; i < ncurves; i++ )
	    {
	        m_nTotalVerts += nvertices[ i ];
	    }

	    // copy the array of numbers of vertices
	    m_nvertices.clear();
//	    m_nvertices.reserve( m_ncurves );
	    for ( i = 0; i < m_ncurves; i++ )
	    {
	        m_nvertices.add( nvertices[ i ] );
	    }

	}

	/**
	 * CqCubicCurvesGroup copy finalructor.
	 */
	public CqCubicCurvesGroup( final CqCubicCurvesGroup from )
	{
		super();
		this.assignment(from);
	}
	
	/**
	 * Assignment operator.
	 *
	 * @param from  CqCubicCurvesGroup to make this one equal to.
	 */
	public CqCubicCurvesGroup assignment( final CqCubicCurvesGroup from )
	{
	    // base class assignment
	    super.assignment( (CqCurvesGroup)from );

	    return ( this );
	}



	/**
	 * Splits a CqCubicCurvesGroup object into a set of piecewise-cubic curve
	 * segments.
	 *
	 * @param aSplits       Vector to contain the cubic curve segments that are
	 *                              created.
	 *
	 * @return  The number of piecewise-cubic curve segments that have been
	 *              created.
	 */
	@Override
	public int Split( STLVector<CqBasicSurface> aSplits )
	{
		
		// number of points to skip between curves
		int vStep =
			pAttributes() .GetIntegerAttribute( "System", "BasisStep" ) [ 1 ];
		
		// information about which parameters are used
		int bUses = Uses();
		
		int vertexI = 0;     //< Current vertex class index.
		int varyingI = 0;     //< Current varying class index.
		int uniformI = 0;     //< Current uniform class index.
		int nsplits = 0;     //< Number of split objects we've created.
		
		// process each curve in the group.  at this level, a curve is a
		//  set of joined piecewise-cubic curve segments.  curveN is the
		//  index of the current curve.
		for ( int curveN = 0; curveN < m_ncurves; curveN++ )
		{
			// find the total number of piecewise cubic segments in the
			//  current curve, accounting for periodic curves
			int npcSegs;
			if ( m_periodic )
			{
				npcSegs = m_nvertices.get( curveN ) / vStep;
			}
			else
			{
				npcSegs = ( m_nvertices.get( curveN ) - 4 ) / vStep + 1;
			}
			
			// find the number of varying parameters in the current curve
			int nVarying;
			if ( m_periodic )
			{
				nVarying = npcSegs;
			}
			else
			{
				nVarying = npcSegs + 1;
			}
			
			int nextCurveVertexIndex = vertexI + m_nvertices.get( curveN );
			int nextCurveVaryingIndex = varyingI + nVarying;
			
			// process each piecewise cubic segment within the current
			//  curve.  pcN is the index of the current piecewise
			//  cubic curve segment within the current curve
			for ( int pcN = 0; pcN < npcSegs; pcN++ )
			{
				// the current vertex index within the current curve group
				int cvi = 0;
				
				// the current varying index within the current curve group
				int cva = 0;
				
				// each segment needs four vertex indexes, which we
				//  calculate here.  if the index goes beyond the
				//  number of vertices then we wrap it around,
				//  starting back at zero.
				int[] vi = new int[ 4 ];
				vi[ 0 ] = vertexI + cvi; ++cvi;
				if ( cvi >= m_nvertices.get( curveN ) ) cvi = 0;
				vi[ 1 ] = vertexI + cvi; ++cvi;
				if ( cvi >= m_nvertices.get( curveN ) ) cvi = 0;
				vi[ 2 ] = vertexI + cvi; ++cvi;
				if ( cvi >= m_nvertices.get( curveN ) ) cvi = 0;
				vi[ 3 ] = vertexI + cvi; ++cvi;
				
				//vi内部の値が、頂点数を超えていたら
				//頂点数分戻るように変更 2006/03/29
				for(int i=0;i < vi.length;i++){
					if(vi[i] >= nextCurveVertexIndex)
						vi[i] -=m_nvertices.get( curveN );
				}
				//--
				// we also need two varying indexes.  once again, we
				//  wrap around
				int[] vai = new int[ 2 ];
				vai[ 0 ] = varyingI + cva; ++cva;
				if ( cva >= nVarying ) cva = 0;
				vai[ 1 ] = varyingI + cva; ++cva;

				//vi内部の値が、頂点数を超えていたら
				//頂点数分戻るように変更 2006/03/29
				for(int i=0;i<2;i++){
					if(vai[i] >= nextCurveVaryingIndex)
						vai[i] -=nVarying;
				}
				//--
				// now, we need to find the value of v at the start
				//  and end of the current piecewise cubic curve
				//  segment
				float vstart = ( float ) pcN / ( float ) ( npcSegs );
				float vend = ( float ) ( pcN + 1 ) / ( float ) ( npcSegs );
				
				// create the new CqLinearCurveSegment for the current
				//  curve segment
				CqCubicCurveSegment pSeg = new CqCubicCurveSegment();
				pSeg.SetSurfaceParameters( this );
				
				// set the value of "v"
				if ( USES( bUses, EnvVars_v ) )
				{
					CqParameterTypedVarying <p_float, p_float> pVP = 
						new CqParameterTypedVarying <p_float, p_float> ( "v", 1, new EqVariableType(type_float), p_float.class, p_float.class );
						pVP.SetSize( pSeg.cVarying() );
						((p_float)pVP.pValue_get( 0 , 0)).value = vstart;
						((p_float)pVP.pValue_get( 1 , 0)).value = vend;
						pSeg.AddPrimitiveVariable( pVP );
				}
				
				// process user parameters
//				std::vector<CqParameter*>::iterator iUP;
				for ( CqParameter iUP : aUserParams() )
				{
					if ( ( iUP ) .Class().getValue() == class_vertex )
					{
						// copy "vertex" class variables
						CqParameter pNewUP =
							( iUP ) .CloneType(
									( iUP ) .strName(),
									( iUP ) .Count()
							);
						pNewUP.SetSize( pSeg.cVertex() );
						
						for ( int i = 0; i < 4; i++ )
						{
							pNewUP.SetValue(
									( iUP ), i, vi[ i ]
							);
						}
						pSeg.AddPrimitiveVariable( pNewUP );
						
					}
					else if ( ( iUP ) .Class().getValue() == class_varying )
					{
						// copy "varying" class variables
						CqParameter pNewUP =
							( iUP ) .CloneType(
									( iUP ) .strName(),
									( iUP ) .Count()
							);
						pNewUP.SetSize( pSeg.cVarying() );
						
						pNewUP.SetValue( ( iUP ), 0, vai[ 0 ] );
						pNewUP.SetValue( ( iUP ), 1, vai[ 1 ] );
						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 );
						//pNewUP.SetValue( ( iUP ), 0, 0 );//2006.02.16. nttdata
						pSeg.AddPrimitiveVariable( pNewUP );
					}
					else if ( ( iUP ) .Class().getValue() == class_constant )
					{
						// copy "finalant" class variables
						CqParameter pNewUP =
							( iUP ) .CloneType(
									( iUP ) .strName(),
									( iUP ) .Count()
							);
						pNewUP.SetSize( 1 );
						
						pNewUP.SetValue( ( iUP ), 0, 0 );
						pSeg.AddPrimitiveVariable( pNewUP );
					} // if
				} // for each user parameter
				
				
				
				vertexI += vStep;
				varyingI++;
				nsplits++;
				
				CqMatrix matBasis = pAttributes() .GetMatrixAttribute( "System", "Basis" ) [ 1 ];
				pSeg .ConvertToBezierBasis( matBasis );
				
				aSplits.add( pSeg );
			}
			
			vertexI = nextCurveVertexIndex;
			varyingI = nextCurveVaryingIndex;
			// we've finished our current curve, so we can get the next
			//  uniform parameter.  there's one uniform parameter per
			//  facet, so each curve corresponds to a facet.
			uniformI++;
		}
		
		return nsplits;
		
	}

	/**
	 * 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 apply the transformation.
	 */
	@Override
	public void Transform( CqMatrix matTx, CqMatrix matITTx, CqMatrix matRTx, int iTime )
	{
	    // make sure the "width" parameter is present
	    PopulateWidth();

	    // number of points to skip between curves
	    final int vStep =
	        pAttributes() .GetIntegerAttribute( "System", "BasisStep" ) [ 1 ];


	    // First, we want to transform the width array.  For cubic curve
	    //  groups, there is one width parameter at each parametric corner.

	    int widthI = 0;
	    int vertexI = 0;

	    // Process each curve in the group.  At this level, each single curve
	    //  is a set of piecewise-cubic curves.
	    for ( int curveN = 0; curveN < m_ncurves; curveN++ )
	    {

	        // now, for each curve in the group, we want to know how many
	        //  varying parameters there are, since this determines how
	        //  many widths will need to be transformed for this curve
	        int nsegments;
	        if ( m_periodic )
	        {
	            nsegments = m_nvertices.get( curveN ) / vStep;
	        }
	        else
	        {
	            nsegments = ( m_nvertices.get( curveN ) - 4 ) / vStep + 1;
	        }
	        int nvarying;
	        if ( m_periodic )
	        {
	            nvarying = nsegments;
	        }
	        else
	        {
	            nvarying = nsegments + 1;
	        }

			int nextCurveVertexIndex = vertexI + m_nvertices.get( curveN );

	        // now we process all the widths for the current curve
	        for ( int ccwidth = 0; ccwidth < nvarying; ccwidth++ )
	        {
	            // 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( widthI , 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( vertexI ,0) );
	            CqVector3D pt_delta = 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 = pt_delta.sub( pt );
	            ((p_float)width().pValue_get( widthI , 0)).value = widthVector.Magnitude();


	            // we've finished the current width, so we move on
	            //  to the next one.  this means incrementing the width
	            //  index by 1, and the vertex index by vStep
	            ++widthI;
//	            vertexI++; 修正前が正しいため修正箇所を元に戻した。
	            vertexI += vStep; 
	            
	            //AqsisではArray外へアクセスする可能性があるため
	            //配列長以上の数値の場合は開始点に戻るよう変更 2006/03/31
	            if(vertexI >= m_nvertices.get( curveN ))
	            	vertexI -= m_nvertices.get( curveN );
	        }
	        vertexI = nextCurveVertexIndex;
	    }

	    // finally, we want to call the base class transform
	    super.Transform( matTx, matITTx, matRTx, iTime );


	}
	
	/**
	 * Returns the number of parameters of varying storage class that this curve
	 * group has.
	 *
	 * @return      Number of varying parameters.
	 */
	public int cVarying()
	{

	    int vStep = pAttributes() .GetIntegerAttribute( "System", "BasisStep" ) [ 1 ];
	    int varying_count = 0;

	    for(int i = 0; i < m_ncurves; ++i)
	    {
	    	int segment_count = m_periodic ? (m_nvertices.get(i) / vStep) : ((m_nvertices.get(i) - 4) / vStep + 1);
	    	varying_count += m_periodic ? segment_count : segment_count + 1;
	    }

	    return varying_count;
	}
	
    /** 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 vertex class parameters. */
    public	int cVertex()
    {
        return m_nTotalVerts;
    };
	
}
