// 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.EqVariableType.*;
import static net.cellcomputing.himawari.library.EqVariableClass.*;
import static net.cellcomputing.himawari.library.EqEnvVars.*;
import static net.cellcomputing.himawari.library.RiGlobal.*;
import static net.cellcomputing.himawari.library.EqIntIndex.*;
import static net.cellcomputing.himawari.library.CqCurve.EssSplitDecision.*;
import static net.cellcomputing.himawari.library.CqStats.*;

import net.cellcomputing.himawari.accessory.STLVector;
import net.cellcomputing.himawari.accessory.primitive.p_String;
import net.cellcomputing.himawari.accessory.primitive.p_float;
import net.cellcomputing.himawari.accessory.primitive.p_int;
import net.cellcomputing.himawari.library.types.CqColor;
import net.cellcomputing.himawari.library.types.CqMatrix;
import net.cellcomputing.himawari.library.types.CqVector3D;
import net.cellcomputing.himawari.library.types.CqVector4D;



/**
 * A single segment or sub-segment from a linear curve.
 * 直線からの一つのセグメントかサブセグメント。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqLinearCurveSegment extends CqCurve {
	
	//------------------------------------------------------ Public Methods
	
	/**　デフォルトコンストラクタ　*/
	public CqLinearCurveSegment()
	{
		super();
	}
	
	/** コピーコンストラクタ　 @param from　コピー元　　*/
	public CqLinearCurveSegment( final CqLinearCurveSegment from )
	{
		super();
		this.assginment( from );
	}
	
	/** デストラクタ　*/
	public void destruct()
	{}
	
	/**
	 * 代入演算子のオーバーロード
	 * 
	 * @param from	代入元
	 * @return	自分自身
	 */
	public CqLinearCurveSegment assginment( final CqLinearCurveSegment from )
	{
		super.assignment( from );
		return ( this );
	}
	
	
	/**
	 * Implements natural subdivision for this curve segment.
	 *
	 * @param pParam        Original parameter.
	 * @param pParam1       First new parameter.
	 * @param pParam2       Second new parameter.
	 * @param u             true if the split is along u (should
	 *                              always be false!)
	 */
	@SuppressWarnings("unchecked")
	public void NaturalSubdivide( CqParameter pParam, CqParameter pParam1, CqParameter pParam2, boolean u )
	{
		assert( u == false );
		
		switch ( pParam.Type().getValue() )
		{
		case type_float:
		{
			CqParameterTyped<p_float, p_float> pTParam = ( CqParameterTyped<p_float, p_float> ) pParam  ;
			CqParameterTyped<p_float, p_float> pTResult1 = ( CqParameterTyped<p_float, p_float> ) pParam1  ;
			CqParameterTyped<p_float, p_float> pTResult2 = ( CqParameterTyped<p_float, p_float> ) pParam2  ;
			TypedNaturalSubdivide( pTParam, pTResult1, pTResult2, u ) ;
			break;
		}
		case type_integer:
		{
			CqParameterTyped<p_int, p_float> pTParam = ( CqParameterTyped<p_int, p_float> ) pParam ;
			CqParameterTyped<p_int, p_float> pTResult1 = ( CqParameterTyped<p_int, p_float> ) pParam1 ;
			CqParameterTyped<p_int, p_float> pTResult2 = ( CqParameterTyped<p_int, p_float> ) pParam2 ;
			TypedNaturalSubdivide( pTParam, pTResult1, pTResult2, u );
			break;
		}
		case type_point:
		case type_vector:
		case type_normal:
		{
			CqParameterTyped<CqVector3D, CqVector3D> pTParam = ( CqParameterTyped<CqVector3D, CqVector3D> ) pParam ;
			CqParameterTyped<CqVector3D, CqVector3D> pTResult1 = ( CqParameterTyped<CqVector3D, CqVector3D> ) pParam1 ;
			CqParameterTyped<CqVector3D, CqVector3D> pTResult2 = ( CqParameterTyped<CqVector3D, CqVector3D> ) pParam2 ;
			TypedNaturalSubdivide( pTParam, pTResult1, pTResult2, u );
			break;
		}
		case type_hpoint:
		{
			CqParameterTyped<CqVector4D, CqVector3D> pTParam = ( CqParameterTyped<CqVector4D, CqVector3D> ) pParam ;
			CqParameterTyped<CqVector4D, CqVector3D> pTResult1 = ( CqParameterTyped<CqVector4D, CqVector3D> ) pParam1 ;
			CqParameterTyped<CqVector4D, CqVector3D> pTResult2 = ( CqParameterTyped<CqVector4D, CqVector3D> ) pParam2 ;
			TypedNaturalSubdivide( pTParam, pTResult1, pTResult2, u );
			break;
		}
		case type_color:
		{
			CqParameterTyped<CqColor, CqColor> pTParam = ( CqParameterTyped<CqColor, CqColor> ) pParam ;
			CqParameterTyped<CqColor, CqColor> pTResult1 = ( CqParameterTyped<CqColor, CqColor> ) pParam1 ;
			CqParameterTyped<CqColor, CqColor> pTResult2 = ( CqParameterTyped<CqColor, CqColor> ) pParam2 ;
			TypedNaturalSubdivide( pTParam, pTResult1, pTResult2, u );
			break;
		}
		case type_string:
		{
			CqParameterTyped<p_String, p_String> pTParam = ( CqParameterTyped<p_String, p_String> ) pParam ;
			CqParameterTyped<p_String, p_String> pTResult1 = ( CqParameterTyped<p_String, p_String> ) pParam1 ;
			CqParameterTyped<p_String, p_String> pTResult2 = ( CqParameterTyped<p_String, p_String> ) pParam2 ;
			TypedNaturalSubdivide( pTParam, pTResult1, pTResult2, u );
			break;
		}
		case type_matrix:
		{
			//			CqParameterTyped<CqMatrix, CqMatrix> pTParam = ( CqParameterTyped<CqMatrix, CqMatrix> ) pParam ;
			//			CqParameterTyped<CqMatrix, CqMatrix> pTResult1 = ( CqParameterTyped<CqMatrix, CqMatrix> ) pParam1 ;
			//			CqParameterTyped<CqMatrix, CqMatrix> pTResult2 = ( CqParameterTyped<CqMatrix, CqMatrix> ) pParam2 ;
			//			TypedNaturalSubdivide( pTParam, pTResult1, pTResult2, u );
			//			break;
		}
		default:
		{
			break;
		}
		}
	}
	
	
	/**
	 * Splits a CqLinearCurveSegment into either two smaller segments or a
	 * patch.
	 *
	 * @param aSplits       Vector to store the split objects in.
	 * @return The number of objects we've created.
	 */
	public int Split( STLVector<CqBasicSurface> aSplits ) 
	{
		// Split based on the decision
		switch( m_splitDecision )
		{
		case Split_Patch:
		{
			// split into a patch
			int cPatches = SplitToPatch( aSplits );
			STATS_INC( GEO_crv_splits );
			STATS_INC( GEO_crv_patch );
			STATS_SETI( GEO_crv_patch_created, STATS_GETI( GEO_crv_patch_created ) + cPatches );
			
			return cPatches;
		}
		
		case Split_Curve:
		{
			// split into smaller curves
			int cCurves = SplitToCurves( aSplits );
			STATS_INC( GEO_crv_splits );
			STATS_INC( GEO_crv_crv );
			STATS_SETI( GEO_crv_crv_created, STATS_GETI( GEO_crv_crv_created ) + cCurves );
			
			return cCurves;
		}
		
		default:
			return 0;
//		throw; 
		}
	}
	
	
	/**
	 * Splits a linear curve segment into two smaller curves.
	 *
	 * @param aSplits       Vector of split surfaces to add the segment to.
	 * @return Number of created objects.
	 */
	public int SplitToCurves( STLVector<CqBasicSurface> aSplits )
	{
		// split into more curves
		//  This bit right here looks a lot like CqSurface::Split().
		//  The difference is that we *don't* want the default splitter
		//  to handle varying class variables because it inconveniently
		//  sets them up to have 4 elements.
		
		aSplits.add( new CqLinearCurveSegment() );
		aSplits.add( new CqLinearCurveSegment() );
		
		aSplits.get( 0 ).SetSurfaceParameters( this );
		aSplits.get( 0 ).SetEyeSplitCount( EyeSplitCount() );
		
		aSplits.get( 1 ).SetSurfaceParameters( this );
		aSplits.get( 1 ).SetEyeSplitCount( EyeSplitCount() );
		
		// Iterate through any user parameters, subdividing and storing
		//  the second value in the target surface.
		for ( CqParameter iUP : m_aUserParams )
		{
			// clone the parameters
			CqParameter pNewA = iUP.Clone();
			CqParameter pNewB = iUP.Clone();
			
			// let the standard system handle all but varying class
			//  primitive variables
			if ( iUP.Class().getValue() == class_varying )
			{
				// for varying class variables, we want to
				//  handle them the same way as vertex class
				//  variables for the simple case of a
				//  CqSingleCurveLinear
				NaturalSubdivide( iUP, pNewA, pNewB, false );
			}
			else{
				iUP.Subdivide( pNewA, pNewB, false, this );
			}
			
			((CqCurve) aSplits.get( 0 )). AddPrimitiveVariable( pNewA );
			((CqCurve) aSplits.get( 1 )). AddPrimitiveVariable( pNewB );
		}
		
		return 2;
	}
	
	
	/**
	 * Converts a linear curve segment into a patch for rendering.
	 *
	 * @param aSplits       Vector of split surfaces to add the segment to.
	 *
	 * @return Number of created objects.
	 */
	public int SplitToPatch( STLVector<CqBasicSurface> aSplits )
	{
		//-----------------------------------------------------------------------
		// first, we find the following vectors:
		//  direction     - from the first point to the second along the line
		//                      segment
		//  normal0       - normal at the first point
		//  normal1       - normal at the second point
		//  widthOffset0  - offset to account for the width of the patch at
		//                      the first point
		//  widthOffset1  - offset to account for the width of the patch at
		//                      the second point
		//-----------------------------------------------------------------------
		
		CqVector3D direction = new CqVector3D( ((CqVector4D) P().pValue_get( 1 ,0 )).sub((CqVector4D) P().pValue_get( 0 ,0 ) ));
		CqVector3D normal0 = new CqVector3D();
		CqVector3D normal1 = new CqVector3D();
		GetNormal( 0, normal0 ); 
		GetNormal( 1, normal1 );
		normal0.Unit();
		normal1.Unit();
		CqVector3D widthOffset0 = normal0.mod( direction );
		CqVector3D widthOffset1 = normal1.mod( direction );
//		widthOffset0 *= width().pValue_get( 0 ,0 ) / widthOffset0.Magnitude() / 2.0;
//		widthOffset1 *= width().pValue_get( 1 ,0 ) / widthOffset1.Magnitude() / 2.0;
		widthOffset0.assignMul( ((p_float)width().pValue_get( 0 ,0 )).value / widthOffset0.Magnitude() / 2.0f );
		widthOffset1.assignMul( ((p_float)width().pValue_get( 1 ,0 )).value / widthOffset1.Magnitude() / 2.0f );
		
		// next, we create the bilinear patch
		CqSurfacePatchBilinear pPatch = new CqSurfacePatchBilinear() ;
		pPatch.SetSurfaceParameters( this );
		pPatch.SetDefaultPrimitiveVariables();
		
		// set the points on the patch
		pPatch.AddPrimitiveVariable( new CqParameterTypedVertex <CqVector4D, CqVector3D> ( "P", new EqVariableType(type_hpoint), CqVector4D.class, CqVector3D.class ));
		pPatch.P().SetSize( 4 );
		((CqVector4D) pPatch.P().pValue_get( 0 ,0 ) ).assignment( new CqVector3D( ((CqVector4D) P().pValue_get( 0 ,0 ))).add( widthOffset0 )) ;
		((CqVector4D) pPatch.P().pValue_get( 1 ,0 ) ).assignment( new CqVector3D( ((CqVector4D) P().pValue_get( 0 ,0 ))).sub( widthOffset0 )) ;
		((CqVector4D) pPatch.P().pValue_get( 2 ,0 ) ).assignment( new CqVector3D( ((CqVector4D) P().pValue_get( 1 ,0 ))).add( widthOffset1 )) ;
		((CqVector4D) pPatch.P().pValue_get( 3 ,0 ) ).assignment( new CqVector3D( ((CqVector4D) P().pValue_get( 1 ,0 ))).sub( widthOffset1 )) ;
		
		// set the normals on the patch
		/* *******************************************   
		 * pPatch.AddPrimitiveVariable(
		 new CqParameterTypedVertex <
		 CqVector3D, type_normal, CqVector3D
		 > ( "N", 0 )
		 );
		 pPatch.N().SetSize( 4 );
		 pPatch.N().pValue_get( 0 ,0 ) = pPatch.N().pValue_get( 1 ,0 ) = normal0;
		 pPatch.N().pValue_get( 2 ,0 ) = pPatch.N().pValue_get( 3 ,0 ) = normal1;
		 ************************************************/
		
		int bUses = Uses();
		
		// set u, v coordinates of the patch
		if ( USES( bUses, EnvVars_u ) || USES( bUses, EnvVars_v ) )
		{
			((p_float) pPatch.u().pValue_get( 0 ,0 ) ).value = ((p_float) pPatch.u().pValue_get( 2 ,0 ) ).value = 0.0f;
			((p_float) pPatch.u().pValue_get( 1 ,0 ) ).value = ((p_float) pPatch.u().pValue_get( 3 ,0 ) ).value = 1.0f;
			((p_float) pPatch.v().pValue_get( 0 ,0 ) ).value = ((p_float) pPatch.v().pValue_get( 1 ,0 ) ).value = ((p_float) v().pValue_get( 0 ,0 ) ).value;
			((p_float) pPatch.v().pValue_get( 2 ,0 ) ).value = ((p_float) pPatch.v().pValue_get( 3 ,0 ) ).value = ((p_float) v().pValue_get( 1 ,0 ) ).value;
		}
		
		// helllllp!!! WHAT DO I DO WITH s,t!!!???
		//  for now, they're set equal to u and v
		if ( USES( bUses, EnvVars_s ) || USES( bUses, EnvVars_t ) )
		{
			((p_float) pPatch.s().pValue_get( 0 ,0 ) ).value = ((p_float) pPatch.s().pValue_get( 2 ,0 ) ).value = 0.0f;
			((p_float) pPatch.s().pValue_get( 1 ,0 ) ).value = ((p_float) pPatch.s().pValue_get( 3 ,0 ) ).value = 1.0f;
			((p_float) pPatch.t().pValue_get( 0 ,0 ) ).value = ((p_float) pPatch.t().pValue_get( 1 ,0 ) ).value = ((p_float) v().pValue_get( 0 ,0 ) ).value;
			((p_float) pPatch.t().pValue_get( 2 ,0 ) ).value = ((p_float) pPatch.t().pValue_get( 3 ,0 ) ).value = ((p_float) v().pValue_get( 1 ,0 ) ).value;
		}
		
		// set any remaining user parameters
		for ( CqParameter iUP : m_aUserParams )
		{
			if ( ( iUP.hash() != hp )&&( iUP.hash() != hn ) &&( iUP.hash() != hu )&&( iUP.hash() != hv ) )
			{
				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( pPatch.cVarying() == pPatch.cVertex());
					pNewUP.SetSize( pPatch.cVarying() );
					
					pNewUP.SetValue( iUP, 0, 0 );
					pNewUP.SetValue( iUP, 1, 0 );
					pNewUP.SetValue( iUP, 2, 1 );
					pNewUP.SetValue( iUP, 3, 1 );
					pPatch.AddPrimitiveVariable( pNewUP );
					
				}
				else if ( ( iUP.Class().getValue() == class_uniform ) || ( iUP.Class().getValue() == class_constant ) )
				{
					// copy "uniform" or "constant" class variables
					CqParameter pNewUP = iUP.CloneType( iUP.strName(), iUP.Count() );
					assert( pPatch.cUniform() == 1 );
					pNewUP.SetSize( pPatch.cUniform() );
					
					pNewUP.SetValue( iUP, 0, 0 );
					pPatch.AddPrimitiveVariable( pNewUP );
				}
			}
		}
		
		// add the patch to the split surfaces vector
		aSplits.add( pPatch );
		
		return 1;
	}
	
	//---------------------------------------------- Inlined Public Methods
	
	/** Returns the number of facevarying class parameters. */
	public	int	cFaceVarying() 
	{
		return 0;
	}
	/** Returns the number of uniform class parameters. */
	public	int cUniform() 
	{
		return 1;
	}
	/** Returns the number of varying class parameters. */
	public	int cVarying() 
	{
		return 2;
	}
	/** Returns the number of vertex class parameters. */
	public	int	cVertex() 
	{
		return 2;
	}
	/** Returns a string name of the class. */
	public String strName() 
	{
		return "CqLinearCurveSegment";
	}
	
	
	/** Typed natural subdivision for the surface. */
	@SuppressWarnings("unchecked")
	public < T, SLT > void TypedNaturalSubdivide( CqParameterTyped<T, SLT> pParam, CqParameterTyped<T, SLT> pResult1, CqParameterTyped<T, SLT> pResult2, boolean u )
	{
		// we can only split curves along v, so enforce this
		assert( u == false );
		
		CqParameterTyped<T, SLT> pTParam = ( CqParameterTyped<T, SLT> ) pParam ;
		CqParameterTyped<T, SLT> pTResult1 = ( CqParameterTyped<T, SLT> ) pResult1 ;
		CqParameterTyped<T, SLT> pTResult2 = ( CqParameterTyped<T, SLT> ) pResult2 ;
		
		T t = (T)pParam.pValue_get( 0 , 0);
		if( t instanceof p_float ) 
		{
			((p_float) pTResult1.pValue_get( 0 , 0 )).value = ((p_float) pTParam.pValue_get( 0 , 0 )).value;
			((p_float) pTResult1.pValue_get( 0 , 1 )).value = ((p_float) pTResult2.pValue_get( 0 , 0 )).value = ( ((p_float) pTParam.pValue_get( 0 , 0 )).value + ((p_float) pTParam.pValue_get( 0 , 1 ) ).value ) * 0.5f ;
			((p_float) pTResult2.pValue_get( 0 , 1 )).value = ((p_float) pTParam.pValue_get( 0 , 1 )).value;
		}
		else if( t instanceof CqVector3D ) 
		{
			((CqVector3D) pTResult1.pValue_get( 0 , 0 )).assignment( ((CqVector3D) pTParam.pValue_get( 0 , 0 ) ));
			((CqVector3D) pTResult1.pValue_get( 0 , 1 )).assignment( ((CqVector3D) pTResult2.pValue_get( 0 , 0 )).assignment( (((CqVector3D) pTParam.pValue_get( 0 , 0 )).add( ((CqVector3D) pTParam.pValue_get( 0 , 1 ) )).mul( 0.5f )) )) ;
			((CqVector3D) pTResult2.pValue_get( 0 , 1 )).assignment( ((CqVector3D) pTParam.pValue_get( 0 , 1 ) ));
		}
		else if( t instanceof CqVector4D ) 
		{
			((CqVector4D) pTResult1.pValue_get( 0 , 0 )).assignment( ((CqVector4D) pTParam.pValue_get( 0 , 0 ) ));
			((CqVector4D) pTResult1.pValue_get( 0 , 1 )).assignment( ((CqVector4D) pTResult2.pValue_get( 0 , 0 )).assignment( (((CqVector4D) pTParam.pValue_get( 0 , 0 )).add( ((CqVector4D) pTParam.pValue_get( 0 , 1 ) )).mul( 0.5f )) )) ;
			((CqVector4D) pTResult2.pValue_get( 0 , 1 )).assignment( ((CqVector4D) pTParam.pValue_get( 0 , 1 ) ));
		}
		else if( t instanceof CqColor ) 
		{
			((CqColor) pTResult1.pValue_get( 0 , 0 )).assignment( ((CqColor) pTParam.pValue_get( 0 , 0 ) ));
			((CqColor) pTResult1.pValue_get( 0 , 1 )).assignment( ((CqColor) pTResult2.pValue_get( 0 , 0 )).assignment( (((CqColor) pTParam.pValue_get( 0 , 0 )).add( ((CqColor) pTParam.pValue_get( 0 , 1 ) )).mul( 0.5f )) )) ;
			((CqColor) pTResult2.pValue_get( 0 , 1 )).assignment( ((CqColor) pTParam.pValue_get( 0 , 1 ) ));
		}
		else if( t instanceof CqMatrix )
		{
			((CqMatrix) pTResult1.pValue_get( 0 , 0 )).assignment( ((CqMatrix) pTParam.pValue_get( 0 , 0 ) ));
			((CqMatrix) pTResult1.pValue_get( 0 , 1 )).assignment( ((CqMatrix) pTResult2.pValue_get( 0 , 0 )).assignment( (((CqMatrix) pTParam.pValue_get( 0 , 0 )).add( ((CqMatrix) pTParam.pValue_get( 0 , 1 ))).multiply( 0.5f )) )) ;
			((CqMatrix) pTResult2.pValue_get( 0 , 1 )).assignment( ((CqMatrix) pTParam.pValue_get( 0 , 1 ) ));
		}
		else if( t instanceof p_int ) 
		{
			((p_int) pTResult1.pValue_get( 0 , 0 )).value = ((p_int) pTParam.pValue_get( 0 , 0 )).value;
			((p_int) pTResult1.pValue_get( 0 , 1 )).value = ((p_int) pTResult2.pValue_get( 0 , 0 )).value = (int)( (((p_int) pTParam.pValue_get( 0 , 0 )).value + ((p_int) pTParam.pValue_get( 0 , 1 ) ).value ) * 0.5f );
			((p_int) pTResult2.pValue_get( 0 , 1 )).value = ((p_int) pTParam.pValue_get( 0 , 1 )).value;
		}
		else if( t instanceof p_String ) 
		{
			((p_String) pTResult1.pValue_get( 0 , 0 )).value = ((p_String) pTParam.pValue_get( 0 , 0 )).value;
			((p_String) pTResult1.pValue_get( 0 , 1 )).value = ((p_String) pTResult2.pValue_get( 0 , 0 )).value = ((p_String) pTParam.pValue_get( 0 , 0 )).value + ((p_String) pTParam.pValue_get( 0 , 1 ) ).value;
			((p_String) pTResult2.pValue_get( 0 , 1 )).value = ((p_String) pTParam.pValue_get( 0 , 1 )).value;
		}
		
	}
	
	
	
}
