/* $Id: nurbsinterfac.cpp,v 1.1 2000-02-09 08:50:26 jeroen Exp $ */
/*
** License Applicability. Except to the extent portions of this file are
** made subject to an alternative license as permitted in the SGI Free
** Software License B, Version 1.0 (the "License"), the contents of this
** file are subject only to the provisions of the License. You may not use
** this file except in compliance with the License. You may obtain a copy
** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
**
** http://oss.sgi.com/projects/FreeB
**
** Note that, as provided in the License, the Software is distributed on an
** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
**
** Original Code. The Original Code is: OpenGL Sample Implementation,
** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
** Copyright in any portions created by third parties is as indicated
** elsewhere herein. All Rights Reserved.
**
** Additional Notice Provisions: The application programming interfaces
** established by SGI in conjunction with the Original Code are The
** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
** Window System(R) (Version 1.3), released October 19, 1998. This software
** was created using the OpenGL(R) version 1.2.1 Sample Implementation
** published by SGI, but has not been independently verified as being
** compliant with the OpenGL(R) version 1.2.1 Specification.
*/

/*
 * nurbsinterfac.c++
 *
 * $Date: 2000-02-09 08:50:26 $ $Revision: 1.1 $
 * $Header: /home/ktk/tmp/odin/2007/netlabs.cvs/odin32/src/opengl/glu/nurbs/internals/nurbsinterfac.cpp,v 1.1 2000-02-09 08:50:26 jeroen Exp $
 */

#include "glimports.h"
#include "mystdio.h"
#include "nurbsconsts.h"
#include "nurbstess.h"
#include "bufpool.h"
#include "quilt.h"
#include "displaylist.h"
#include "knotvector.h"
#include "mapdesc.h"

#define THREAD( work, arg, cleanup ) \
        if( dl ) {\
            arg->save = 1;\
            dl->append( (PFVS)&NurbsTessellator::work, (void *) arg, (PFVS)&NurbsTessellator::cleanup );\
        } else {\
            arg->save = 0;\
            work( arg );\
        }

#define THREAD2( work ) \
        if( dl ) {\
            dl->append( (PFVS)&NurbsTessellator::work, 0, 0 );\
        } else {\
            work( );\
        }

NurbsTessellator::NurbsTessellator( BasicCurveEvaluator &c, BasicSurfaceEvaluator& e)
        : subdivider( renderhints, backend ),
          backend( c, e ),
          maplist( backend ),
          o_pwlcurvePool( sizeof( O_pwlcurve ), 32, "o_pwlcurvePool" ),
          o_nurbscurvePool( sizeof( O_nurbscurve ), 32, "o_nurbscurvePool"),
          o_curvePool( sizeof( O_curve ), 32,  "o_curvePool" ),
          o_trimPool( sizeof( O_trim ), 32,  "o_trimPool" ),
          o_surfacePool( sizeof( O_surface ), 1, "o_surfacePool" ),
          o_nurbssurfacePool( sizeof( O_nurbssurface ), 4, "o_nurbssurfacePool" ),
          propertyPool( sizeof( Property ), 32, "propertyPool" ),
          quiltPool( sizeof( Quilt  ), 32, "quiltPool" )
{
    dl          = 0;
    inSurface   = 0;
    inCurve     = 0;
    inTrim      = 0;
    playBack    = 0;
    jumpbuffer  = newJumpbuffer();
    subdivider.setJumpbuffer( jumpbuffer );
}

NurbsTessellator::~NurbsTessellator( void )
{
    if( inTrim ) {
        do_nurbserror( 12 );
        endtrim();
    }

    if( inSurface ) {
        *nextNurbssurface = 0;
        do_freeall();
    }

    if (jumpbuffer) {
        deleteJumpbuffer(jumpbuffer);
        jumpbuffer= 0;
    }
}

/*-----------------------------------------------------------------------------
 * bgnsurface - allocate and initialize an o_surface structure
 *
 * Client: GL user
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::bgnsurface( long nuid )
{
    O_surface *o_surface = new(o_surfacePool) O_surface;
    o_surface->nuid = nuid;
    THREAD( do_bgnsurface, o_surface, do_freebgnsurface );
}

/*-----------------------------------------------------------------------------
 * bgncurve - allocate an initialize an o_curve structure
 *
 * Client: GL user
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::bgncurve( long nuid )
{
    O_curve *o_curve = new(o_curvePool) O_curve;
    o_curve->nuid = nuid;
    THREAD( do_bgncurve, o_curve, do_freebgncurve );
}
/*-----------------------------------------------------------------------------
 * endcurve -
 *
 * Client:
 *-----------------------------------------------------------------------------
 */

void
NurbsTessellator::endcurve( void )
{
    THREAD2( do_endcurve );
}

/*-----------------------------------------------------------------------------
 * endsurface - user level end of surface call
 *
 * Client: GL user
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::endsurface( void )
{
    THREAD2( do_endsurface );
}


/*-----------------------------------------------------------------------------
 * bgntrim - allocate and initialize a new trim loop structure (o_trim )
 *
 * Client: GL user
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::bgntrim( void )
{
    O_trim *o_trim = new(o_trimPool) O_trim;
    THREAD( do_bgntrim, o_trim, do_freebgntrim );
}

/*-----------------------------------------------------------------------------
 * endtrim -
 *
 * Client: GL user
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::endtrim( void )
{
    THREAD2( do_endtrim );
}


/*-----------------------------------------------------------------------------
 * pwlcurve -
 *
 *      count        - number of points on curve
 *      array        - array of points on curve
 *      byte_stride  - distance between points in bytes
 *      type         - valid data flag
 *
 * Client: Gl user
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::pwlcurve( long count, INREAL array[], long byte_stride, long type )
{
    Mapdesc *mapdesc = maplist.locate( type );

    if( mapdesc == 0 ) {
        do_nurbserror( 35 );
        isDataValid = 0;
        return;
    }

    if ( (type != N_P2D) && (type != N_P2DR) ) {
        do_nurbserror( 22 );
        isDataValid = 0;
        return;
    }
    if( count < 0 ) {
        do_nurbserror( 33 );
        isDataValid = 0;
        return;
    }
    if( byte_stride < 0 ) {
        do_nurbserror( 34 );
        isDataValid = 0;
        return;
    }

#ifdef NOTDEF
    if( mapdesc->isRational() ) {
        INREAL *p = array;
        INREAL x = p[0]; INREAL y = p[1]; INREAL w = p[2];
        p = (INREAL *) (((char *) p) + byte_stride);
        for( long i = 1; i != count; i++ ) {
            if( p[0] == x && p[1] == y && p[2] == w ) break;
            x = p[0]; y = p[1]; w = p[2];
            p = (INREAL *) (((char *) p) + byte_stride);
        }
        if( i != count ) {
            do_nurbserror( 37 );
            dprintf( "point %d (%f,%f)\n", i, x, y );
            isDataValid = 0;
            return;
        }
    } else {
        INREAL *p = array;
        INREAL x = p[0]; INREAL y = p[1];
        p = (INREAL *) (((char *) p) + byte_stride);
        for( long i = 1; i != count; i++ ) {
            if( p[0] == x && p[1] == y ) break;
            x = p[0]; y = p[1];
            p = (INREAL *) (((char *) p) + byte_stride);
        }
        if( i != count ) {
            do_nurbserror( 37 );
            dprintf( "point %d (%f,%f)\n", i, x, y );
            isDataValid = 0;
            return;
        }
    }
#endif

    O_pwlcurve  *o_pwlcurve = new(o_pwlcurvePool) O_pwlcurve( type, count, array, byte_stride, extTrimVertexPool.get((int)count) );
    THREAD( do_pwlcurve, o_pwlcurve, do_freepwlcurve );
}


/*-----------------------------------------------------------------------------
 * nurbscurve -
 *
 * Client: GL user
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::nurbscurve(
    long nknots,                /* number of p knots */
    INREAL knot[],              /* nondecreasing knot values in p */
    long byte_stride,           /* distance in bytes between control points */
    INREAL ctlarray[],          /* pointer to first control point */
    long order,                 /* order of spline */
    long type )                 /* description of range space */
{

    Mapdesc *mapdesc = maplist.locate( type );

    if( mapdesc == 0 ) {
        do_nurbserror( 35 );
        isDataValid = 0;
        return;
    }

    if( ctlarray == 0 ) {
        do_nurbserror( 36 );
        isDataValid = 0;
        return;
    }

    if( byte_stride < 0 ) {
        do_nurbserror( 34 );
        isDataValid = 0;
        return;
    }

    Knotvector knots;

    knots.init( nknots, byte_stride, order, knot );
    if( do_check_knots( &knots, "curve" ) ) return;

    O_nurbscurve *o_nurbscurve = new(o_nurbscurvePool) O_nurbscurve(type);
    o_nurbscurve->bezier_curves = new(quiltPool) Quilt(mapdesc);
    o_nurbscurve->bezier_curves->toBezier( knots,ctlarray, mapdesc->getNcoords() );

    THREAD( do_nurbscurve, o_nurbscurve, do_freenurbscurve );
}


/*-----------------------------------------------------------------------------
 * nurbssurface -
 *
 * Client: User routine
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::nurbssurface(
    long sknot_count,           /* number of s knots */
    INREAL sknot[],             /* nondecreasing knot values in s */
    long tknot_count,           /* number of t knots */
    INREAL tknot[],             /* nondecreasing knot values in t */
    long s_byte_stride,         /* s step size in memory bytes */
    long t_byte_stride,         /* t step size in memory bytes */
    INREAL ctlarray[],          /* pointer to first control point */
    long sorder,                /* order of the spline in s parameter */
    long torder,                /* order of the spline in t parameter */
    long type)                  /* description of range space */
{
    Mapdesc *mapdesc = maplist.locate( type );

    if( mapdesc == 0 ) {
        do_nurbserror( 35 );
        isDataValid = 0;
        return;
    }

    if( s_byte_stride < 0 ) {
        do_nurbserror( 34 );
        isDataValid = 0;
        return;
    }

    if( t_byte_stride < 0 ) {
        do_nurbserror( 34 );
        isDataValid = 0;
        return;
    }

    Knotvector sknotvector, tknotvector;

    sknotvector.init( sknot_count, s_byte_stride, sorder, sknot );
    if( do_check_knots( &sknotvector, "surface" ) ) return;

    tknotvector.init( tknot_count, t_byte_stride, torder, tknot );
    if( do_check_knots( &tknotvector, "surface" ) ) return;

    O_nurbssurface *o_nurbssurface = new(o_nurbssurfacePool) O_nurbssurface(type);
    o_nurbssurface->bezier_patches = new(quiltPool) Quilt(mapdesc);

    o_nurbssurface->bezier_patches->toBezier( sknotvector, tknotvector,
        ctlarray, mapdesc->getNcoords() );
    THREAD( do_nurbssurface, o_nurbssurface, do_freenurbssurface );
}


/*-----------------------------------------------------------------------------
 * setnurbsproperty -
 *
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::setnurbsproperty( long tag, INREAL value )
{
    if( ! renderhints.isProperty( tag ) ) {
        do_nurbserror( 26 );
    } else {
        Property *prop = new(propertyPool) Property( tag, value );
        THREAD( do_setnurbsproperty, prop, do_freenurbsproperty );
    }
}

/*-----------------------------------------------------------------------------
 * setnurbsproperty -
 *
 *-----------------------------------------------------------------------------
 */
void
NurbsTessellator::setnurbsproperty( long type, long tag, INREAL value )
{
    Mapdesc *mapdesc = maplist.locate( type );

    if( mapdesc == 0 ) {
        do_nurbserror( 35 );
        return;
    }

    if( ! mapdesc->isProperty( tag ) ) {
        do_nurbserror( 26 );
        return;
    }

    Property *prop = new(propertyPool) Property( type, tag, value );
    THREAD( do_setnurbsproperty2, prop, do_freenurbsproperty );
}


/*-----------------------------------------------------------------------------
 * getnurbsproperty -
 *
 *-----------------------------------------------------------------------------
 */

void
NurbsTessellator::getnurbsproperty( long tag, INREAL *value )
{
    if( renderhints.isProperty( tag ) ) {
        *value = renderhints.getProperty( tag );
    } else {
        do_nurbserror( 26 );
    }
}

/*-----------------------------------------------------------------------------
 * getnurbsproperty -
 *
 *-----------------------------------------------------------------------------
 */

void
NurbsTessellator::getnurbsproperty( long type, long tag, INREAL *value )
{
    Mapdesc *mapdesc = maplist.locate( type );

    if( mapdesc == 0 )
        do_nurbserror( 35 );

    if( mapdesc->isProperty( tag  ) ) {
        *value = mapdesc->getProperty( tag );
    } else {
        do_nurbserror( 26 );
    }
}

/*--------------------------------------------------------------------------
 * setnurbsproperty - accept a user supplied matrix as culling or sampling mat
 *--------------------------------------------------------------------------
 */

void
NurbsTessellator::setnurbsproperty( long type, long purpose, INREAL *mat )
{
    // XXX - cannot be put in display list
    Mapdesc *mapdesc = maplist.locate( type );

    if( mapdesc == 0 ) {
        do_nurbserror( 35 );
        isDataValid = 0;
    } else if( purpose == N_BBOXSIZE ) {
        mapdesc->setBboxsize( mat );
    } else {
#ifndef NDEBUG
        dprintf( "ERRORRORRORR!!!\n");
#endif
    }
}

/*--------------------------------------------------------------------------
 * setnurbsproperty - accept a user supplied matrix as culling or sampling mat
 *--------------------------------------------------------------------------
 */

void
NurbsTessellator::setnurbsproperty( long type, long purpose, INREAL *mat,
    long rstride, long cstride )
{
    // XXX - cannot be put in display list
    Mapdesc *mapdesc = maplist.locate( type );

    if( mapdesc == 0 ) {
        do_nurbserror( 35 );
        isDataValid = 0;
    } else if( purpose == N_CULLINGMATRIX ) {
        mapdesc->setCmat( mat, rstride, cstride );
    } else if( purpose == N_SAMPLINGMATRIX ) {
        mapdesc->setSmat( mat, rstride, cstride );
    } else if( purpose == N_BBOXMATRIX ) {
        mapdesc->setBmat( mat, rstride, cstride );
    } else {
#ifndef NDEBUG
        dprintf( "ERRORRORRORR!!!\n");
#endif
    }
}

void
NurbsTessellator::redefineMaps( void )
{
    maplist.initialize();
}

void
NurbsTessellator::defineMap( long type, long rational, long ncoords )
{
    maplist.define( type, (int) rational, (int) ncoords );
}

void
NurbsTessellator::discardRecording( void *_dl )
{
    delete (DisplayList *) _dl;
}

void *
NurbsTessellator::beginRecording( void )
{
    dl = new DisplayList( this );
    return (void *) dl;
}

void
NurbsTessellator::endRecording( void )
{
    dl->endList();
    dl = 0;
}

void
NurbsTessellator::playRecording( void *_dl )
{
    playBack = 1;
    bgnrender();
    ((DisplayList *)_dl)->play();
    endrender();
    playBack = 0;
}

