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

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

/**
 * 
 * モーションブラーされたマイクロポリゴンについて保存するクラス。
 * Class which stores info about motion blurred micropolygrids.
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqMotionMicroPolyGrid extends CqMicroPolyGridBaseMotionImple {
	
	/**
	 * デフォルトコンストラクタ
	 */
	public CqMotionMicroPolyGrid() {
		super();	//CqMicroPolyGridBase
		CqMotionSpec( (CqMicroPolyGridBase)null );
	}
	
	/**
	 * Split the micropolygrid into individual MPGs,
	 * @param pImage Pointer to image being rendered into.
	 * @param xmin Integer minimum extend of the image part being rendered, takes into account buckets and clipping.
	 * @param xmax Integer maximum extend of the image part being rendered, takes into account buckets and clipping.
	 * @param ymin Integer minimum extend of the image part being rendered, takes into account buckets and clipping.
	 * @param ymax Integer maximum extend of the image part being rendered, takes into account buckets and clipping.
	 */
	@Override
	public void Split(CqImageBuffer pImage, long xmin, long xmax, long ymin, long ymax) {

	    // Get the main object, the one that was shaded.
	    CqMicroPolyGrid pGridA = (CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) );
	    int cu = pGridA.uGridRes();
	    int cv = pGridA.vGridRes();
	    int iTime;

	    CqMatrix matCameraToRaster = QGetRenderContext() .matSpaceToSpace( "camera", "raster", new CqMatrix(), new CqMatrix(), QGetRenderContext().Time() );

	    pGridA.AddRef();

	    // Get an array of P's for all time positions.
	    STLVector< STLVector<CqVector3D> > aaPtimes = new STLVector<STLVector<CqVector3D>>( 2, CqVector3D.class );;
	    aaPtimes.resize( iTimes() );

	    int tTime = iTimes();
//	    CqMatrix matObjectToCameraT;	//読み取られていなかった
	    int i;
	    int gsmin1;
	    gsmin1 = pGridA.GridSize() - 1;

	    for ( iTime = 0; iTime < tTime; iTime++ )
	    {
//	        matObjectToCameraT = QGetRenderContext() .matSpaceToSpace( "object", "camera", new CqMatrix(), pSurface() .pTransform() .matObjectToWorld( pSurface() .pTransform() .Time( iTime ) ), QGetRenderContext().Time() );
	        aaPtimes.get( iTime ).resize( gsmin1 + 1 );

	        // Transform the whole grid to hybrid camera/raster space
	        CqMicroPolyGrid pg = (CqMicroPolyGrid)( GetMotionObject( Time( iTime ) ) );
	        //nttdata 高速化
//	        STLVector<CqVector3D> pP = new STLVector<CqVector3D>(CqVector3D.class);
//	        pg.pVar(EnvVars_P) .GetPointPtr( pP );
	        //STLVector<CqVector3D> pP ;
	        STLArray<CqVector3D> pP ;
	        pP = pg.pVar(EnvVars_P) .GetPointPtr_2( );
	        
	        for ( i = gsmin1; i >= 0; i-- )
	        {
	            CqVector3D Point = new CqVector3D( pP.get( i ) );

	            // Make sure to retain camera space 'z' coordinate.
	            float zdepth = Point.z;
	            aaPtimes.get( iTime ).get( i ).assignment( matCameraToRaster.multiply( Point ) );
	            aaPtimes.get( iTime ).get( i ).z( zdepth );
	            pP.get( i ).assignment( aaPtimes.get( iTime ).get( i ) );
	        }
			SqTriangleSplitLine sl = new SqTriangleSplitLine();
			CqVector3D v0, v1, v2;
			v0 = new CqVector3D( aaPtimes.get( iTime ).get( 0 ) );
			v1 = new CqVector3D( aaPtimes.get( iTime ).get( cu ) );
			v2 = new CqVector3D( aaPtimes.get( iTime ).get( cv * ( cu + 1 ) ) );
			// Check for clockwise, swap if not.
			if( ( ( v1.x - v0.x ) * ( v2.y - v0.y ) - ( v1.y - v0.y ) * ( v2.x - v0.x ) ) >= 0 )
			{
				sl.m_TriangleSplitPoint1.assignment( v1 );
				sl.m_TriangleSplitPoint2.assignment( v2 );
			}
			else
			{
				sl.m_TriangleSplitPoint1.assignment( v2 );
				sl.m_TriangleSplitPoint2.assignment( v1 );
			}
			m_TriangleSplitLine.AddTimeSlot(Time( iTime ), sl ); 
	    }

	    // Get the required trim curve sense, if specified, defaults to "inside".
	    final String[] pattrTrimSense = pAttributes() .GetStringAttribute( "trimcurve", "sense" );
	    String strTrimSense = "inside";
	    if ( pattrTrimSense != null ) strTrimSense = pattrTrimSense[ 0 ];
//	    boolean bOutside = strTrimSense == "outside";
	    boolean bOutside = strTrimSense.equals("outside");

	    // Determine whether we need to bother with trimming or not.
	    boolean bCanBeTrimmed = pSurface() .bCanBeTrimmed() && null != pGridA.pVar(EnvVars_u) && null != pGridA.pVar(EnvVars_v);

	    int iv;
	    for ( iv = 0; iv < cv; iv++ )
	    {
	        int iu;
	        for ( iu = 0; iu < cu; iu++ )
	        {
	            int iIndex = ( iv * ( cu + 1 ) ) + iu;

	            // If culled don't bother.
	            if ( pGridA.CulledPolys().Value( iIndex ) )
	            {
	                //theStats.IncCulledMPGs();
	                continue;
	            }

	            // If the MPG is trimmed then don't add it.
	            boolean fTrimmed = false;
	            if ( bCanBeTrimmed )
	            {
	                p_float u1, v1, u2, v2, u3, v3, u4, v4;

	                pGridA.pVar(EnvVars_u) .GetFloat( u1 = new p_float(), iIndex );
	                pGridA.pVar(EnvVars_v) .GetFloat( v1 = new p_float(), iIndex );
	                pGridA.pVar(EnvVars_u) .GetFloat( u2 = new p_float(), iIndex + 1 );
	                pGridA.pVar(EnvVars_v) .GetFloat( v2 = new p_float(), iIndex + 1 );
	                pGridA.pVar(EnvVars_u) .GetFloat( u3 = new p_float(), iIndex + cu + 2 );
	                pGridA.pVar(EnvVars_v) .GetFloat( v3 = new p_float(), iIndex + cu + 2 );
	                pGridA.pVar(EnvVars_u) .GetFloat( u4 = new p_float(), iIndex + cu + 1 );
	                pGridA.pVar(EnvVars_v) .GetFloat( v4 = new p_float(), iIndex + cu + 1 );
	                
					CqVector2D vecA = new CqVector2D(u1.value, v1.value);
					CqVector2D vecB = new CqVector2D(u2.value, v2.value);
					CqVector2D vecC = new CqVector2D(u3.value, v3.value);
					CqVector2D vecD = new CqVector2D(u4.value, v4.value);

					boolean fTrimA = pSurface() .bIsPointTrimmed( vecA );
	                boolean fTrimB = pSurface() .bIsPointTrimmed( vecB );
	                boolean fTrimC = pSurface() .bIsPointTrimmed( vecC );
	                boolean fTrimD = pSurface() .bIsPointTrimmed( vecD );

	                if ( bOutside )
	                {
	                    fTrimA = !fTrimA;
	                    fTrimB = !fTrimB;
	                    fTrimC = !fTrimC;
	                    fTrimD = !fTrimD;
	                }

	                // If all points are trimmed, need to check if the MP spans the trim curve at all, if not, then
					// we can discard it altogether.
	                if ( fTrimA && fTrimB && fTrimC && fTrimD )
					{
						if(!pSurface().bIsLineIntersecting(vecA, vecB) && 
						   !pSurface().bIsLineIntersecting(vecB, vecC) && 
						   !pSurface().bIsLineIntersecting(vecC, vecD) && 
						   !pSurface().bIsLineIntersecting(vecD, vecA) )
						{
							STATS_INC( MPG_trimmedout );
							continue;
						}
					}

	                // If any points are trimmed mark the MPG as needing to be trim checked.
	                //fTrimmed = fTrimA || fTrimB || fTrimC || fTrimD;
	                if ( fTrimA || fTrimB || fTrimC || fTrimD )
	                    fTrimmed = true;
	            }

	            CqMicroPolygonMotion pNew = new CqMicroPolygonMotion();
	            pNew.SetGrid( this );
	            pNew.SetIndex( iIndex );
	            for ( iTime = 0; iTime < iTimes(); iTime++ )
	                pNew.AppendKey( aaPtimes.get( iTime ).get( iIndex ), aaPtimes.get( iTime ).get( iIndex + 1 ), aaPtimes.get( iTime ).get( iIndex + cu + 2 ), aaPtimes.get( iTime ).get( iIndex + cu + 1 ), Time( iTime ) );
	            pImage.AddMPG( pNew );
	        }
	    }

	    pGridA.Release();

	    // Delete the donor motion grids, as their work is done.
	/*    for ( iTime = 1; iTime < iTimes(); iTime++ )
	    {
	        CqMicroPolyGrid* pg = (CqMicroPolyGrid)( GetMotionObject( Time( iTime ) ) );
	        if ( NULL != pg )
	            RELEASEREF( pg );
	    }
	    //		delete( GetMotionObject( Time( iTime ) ) );
	*/
	
	}

	/** 
	 * Shade the primary grid.
	 */
	@Override
	public void Shade() {
		CqMicroPolyGrid pGrid = (CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) );
	    pGrid.Shade();
	}

	/** 
	 * Transfer shader output variables for the primary grid.
	 */
	@Override
	public void TransferOutputVariables() {
		CqMicroPolyGrid pGrid = (CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) );
		pGrid.TransferOutputVariables();
	}

	@Override
	public void DeleteVariables(boolean all) {
	}

    // Redirect acces via IqShaderExecEnv
	@Override
    public int	uGridRes()
    {
        assert( GetMotionObject( Time( 0 ) )!=null ); 
		return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .uGridRes() );
    }
	@Override
    public int	vGridRes()
    {
        assert( GetMotionObject( Time( 0 ) )!=null ); 
		return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .vGridRes() );
    }
	@Override
    public int	GridSize()
    {
        assert( GetMotionObject( Time( 0 ) )!=null ); 
		return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .GridSize() );
    }
	@Override
    public IqShaderData pVar(int index)
    {
        assert( GetMotionObject( Time( 0 ) )!=null ); 
        return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .pVar(index) );
    }
	@Override
    public IqShaderData FindStandardVar( final String pname )
    {
        assert( GetMotionObject( Time( 0 ) )!=null ); 
        return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .FindStandardVar(pname) );
    }

    /**
     * Get a pointer to the surface which this grid belongs.
     * Actually returns the surface pointer from the first timeslot.
     * @return Surface pointer, only valid during shading.
     */
	@Override
    public CqSurface	pSurface()
    {
        return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .pSurface() );
    }

	@Override
    public IqAttributes pAttributes()
    {
        return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .pAttributes() );
    }

	@Override
    public CqCSGTreeNode pCSGNode()
    {
        return ( ((CqMicroPolyGrid)( GetMotionObject( Time( 0 ) ) )) .pCSGNode() );
    }
	
	/** 
	 * Overridden from CqMotionSpec, does nothing.
	 */
	public void ClearMotionObject(CqMicroPolyGridBase A) {
	}

	/** 
	 * Overridden from CqMotionSpec, does nothing.
	 */
	public CqMicroPolyGridBase ConcatMotionObjects(CqMicroPolyGridBase A, CqMicroPolyGridBase B) {
		return ( B );
	}

	/** 
	 * Overridden from CqMotionSpec, does nothing.
	 */
	public CqMicroPolyGridBase LinearInterpolateMotionObjects(float Fraction, CqMicroPolyGridBase A, CqMicroPolyGridBase B) {
		return ( A );
	}

	
}
