/*************************************************************************
 *                                                                       *
 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.       *
 * All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
 *                                                                       *
 * This library is free software; you can redistribute it and/or         *
 * modify it under the terms of EITHER:                                  *
 *   (1) The GNU Lesser General Public License as published by the Free  *
 *       Software Foundation; either version 2.1 of the License, or (at  *
 *       your option) any later version. The text of the GNU Lesser      *
 *       General Public License is included with this library in the     *
 *       file LICENSE.TXT.                                               *
 *   (2) The BSD-style license that is included with this library in     *
 *       the file LICENSE-BSD.TXT.                                       *
 *                                                                       *
 * 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 files    *
 * LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
 *                                                                       *
 *************************************************************************/

#ifdef _MSC_VER
#pragma warning(disable:4291)  // for VC++, no complaints about "no matching operator delete found"
#endif

// this source file is mostly concerned with the data structures, not the
// numerics.
#include "ode_common.hpp"
#include "ode_math.hpp"
#include "ode_math_legacy.hpp"
#include "ode_misc.hpp"
#include "ode_rotation.hpp"
#include "ode_collision.hpp"
//#include <ode/ode.h>
#include "ode_memory.hpp"
//#include <ode/error.h>
//#include "config.h"
#include "matrix.h"
#include "odemath.h"
#include "objects.h"
#include "joints/joints.h"
#include "step.h"
#include "quickstep.h"
#include "util.h"
#include "odetls.h"


// misc defines
#define dDEBUGMSG(e)
#define dAVERIFY(e)
#define dMessage(a,b)

#define ALLOCA dALLOCA16
#define dMAX(x, y) ((x) > (y) ? (x) : (y))

//****************************************************************************
// utility


// add an object `obj' to the list who's head pointer is pointed to by `first'.

void addObjectToList (dObject *obj, dObject **first)
{
    obj->next = *first;
    obj->tome = first;
    if (*first) (*first)->tome = &obj->next;
    (*first) = obj;
}


// remove the object from the linked list

static inline void removeObjectFromList (dObject *obj)
{
    if (obj->next) obj->next->tome = obj->tome;
    *(obj->tome) = obj->next;
    // safeguard
    obj->next = NULL;
    obj->tome = NULL;
}


// remove the joint from neighbour lists of all connected bodies

static void removeJointReferencesFromAttachedBodies (dxJoint *j)
{
    for (int i=0; i<2; i++) {
        dxBody *body = j->node[i].body;
        if (body) {
            dxJointNode *n = body->firstjoint;
            dxJointNode *last = NULL;
            while (n) {
                if (n->joint == j) {
                    if (last) last->next = n->next;
                    else body->firstjoint = n->next;
                    break;
                }
                last = n;
                n = n->next;
            }
        }
    }
    j->node[0].body = NULL;
    j->node[0].next = NULL;
    j->node[1].body = NULL;
    j->node[1].next = NULL;
}

//****************************************************************************
// debugging

// see if an object list loops on itself (if so, it's bad).

static int listHasLoops (dObject *first)
{
    if (first==0 || first->next==0) return 0;
    dObject *a=first,*b=first->next;
    int skip=0;
    while (b) {
        if (a==b) return 1;
        b = b->next;
        if (skip) a = a->next;
        skip ^= 1;
    }
    return 0;
}


// check the validity of the world data structures

static int g_world_check_tag_generator = 0;

static inline int generateWorldCheckTag()
{
    // Atomicity is not necessary here
    return ++g_world_check_tag_generator;
}

static void checkWorld (dxWorld *w)
{
    dxBody *b;
    dxJoint *j;

    // check there are no loops
    if (listHasLoops (w->firstbody)) dDebug (0,"body list has loops");
    if (listHasLoops (w->firstjoint)) dDebug (0,"joint list has loops");

    // check lists are well formed (check `tome' pointers)
    for (b=w->firstbody; b; b=(dxBody*)b->next) {
        if (b->next && b->next->tome != &b->next)
            dDebug (0,"bad tome pointer in body list");
    }
    for (j=w->firstjoint; j; j=(dxJoint*)j->next) {
        if (j->next && j->next->tome != &j->next)
            dDebug (0,"bad tome pointer in joint list");
    }

    // check counts
    int n = 0;
    for (b=w->firstbody; b; b=(dxBody*)b->next) n++;
    if (w->nb != n) dDebug (0,"body count incorrect");
    n = 0;
    for (j=w->firstjoint; j; j=(dxJoint*)j->next) n++;
    if (w->nj != n) dDebug (0,"joint count incorrect");

    // set all tag values to a known value
    int count = generateWorldCheckTag();
    for (b=w->firstbody; b; b=(dxBody*)b->next) b->tag = count;
    for (j=w->firstjoint; j; j=(dxJoint*)j->next) j->tag = count;

    // check all body/joint world pointers are ok
    for (b=w->firstbody; b; b=(dxBody*)b->next) if (b->world != w)
        dDebug (0,"bad world pointer in body list");
    for (j=w->firstjoint; j; j=(dxJoint*)j->next) if (j->world != w)
        dDebug (0,"bad world pointer in joint list");

    /*
    // check for half-connected joints - actually now these are valid
    for (j=w->firstjoint; j; j=(dxJoint*)j->next) {
    if (j->node[0].body || j->node[1].body) {
    if (!(j->node[0].body && j->node[1].body))
    dDebug (0,"half connected joint found");
    }
    }
    */

    // check that every joint node appears in the joint lists of both bodies it
    // attaches
    for (j=w->firstjoint; j; j=(dxJoint*)j->next) {
        for (int i=0; i<2; i++) {
            if (j->node[i].body) {
                int ok = 0;
                for (dxJointNode *n=j->node[i].body->firstjoint; n; n=n->next) {
                    if (n->joint == j) ok = 1;
                }
                if (ok==0) dDebug (0,"joint not in joint list of attached body");
            }
        }
    }

    // check all body joint lists (correct body ptrs)
    for (b=w->firstbody; b; b=(dxBody*)b->next) {
        for (dxJointNode *n=b->firstjoint; n; n=n->next) {
            if (&n->joint->node[0] == n) {
                if (n->joint->node[1].body != b)
                    dDebug (0,"bad body pointer in joint node of body list (1)");
            }
            else {
                if (n->joint->node[0].body != b)
                    dDebug (0,"bad body pointer in joint node of body list (2)");
            }
            if (n->joint->tag != count) dDebug (0,"bad joint node pointer in body");
        }
    }

    // check all body pointers in joints, check they are distinct
    for (j=w->firstjoint; j; j=(dxJoint*)j->next) {
        if (j->node[0].body && (j->node[0].body == j->node[1].body))
            dDebug (0,"non-distinct body pointers in joint");
        if ((j->node[0].body && j->node[0].body->tag != count) ||
            (j->node[1].body && j->node[1].body->tag != count))
            dDebug (0,"bad body pointer in joint");
    }
}


void dWorldCheck (dxWorld *w)
{
    checkWorld (w);
}

//****************************************************************************
// body

dxBody::dxBody(dxWorld *w) :
dObject(w)
{

}


dxWorld* dBodyGetWorld (dxBody * b)
{
    dAASSERT (b);
    return b->world;
}

dxBody *dBodyCreate (dxWorld *w)
{
    dAASSERT (w);
    dxBody *b = new dxBody(w);
    b->firstjoint = NULL;
    b->flags = 0;
    b->geom = NULL;
    b->average_lvel_buffer = NULL;
    b->average_avel_buffer = NULL;
    dMassSetParameters (&b->mass,1,0,0,0,1,1,1,0,0,0);
    dSetZero (b->invI,4*3);
    b->invI[0] = 1;
    b->invI[5] = 1;
    b->invI[10] = 1;
    b->invMass = 1;
    dSetZero (b->posr.pos,4);
    dSetZero (b->q,4);
    b->q[0] = 1;
    dRSetIdentity (b->posr.R);
    dSetZero (b->lvel,4);
    dSetZero (b->avel,4);
    dSetZero (b->facc,4);
    dSetZero (b->tacc,4);
    dSetZero (b->finite_rot_axis,4);
    addObjectToList (b,(dObject **) &w->firstbody);
    w->nb++;

    // set auto-disable parameters
    b->average_avel_buffer = b->average_lvel_buffer = NULL; // no buffer at beginning
    dBodySetAutoDisableDefaults (b);	// must do this after adding to world
    b->adis_stepsleft = b->adis.idle_steps;
    b->adis_timeleft = b->adis.idle_time;
    b->average_counter = 0;
    b->average_ready = 0; // average buffer not filled on the beginning
    dBodySetAutoDisableAverageSamplesCount(b, b->adis.average_samples);

    b->moved_callback = NULL;

    dBodySetDampingDefaults(b);	// must do this after adding to world

    b->flags |= w->body_flags & dxBodyMaxAngularSpeed;
    b->max_angular_speed = w->max_angular_speed;

    b->flags |= dxBodyGyroscopic;

    return b;
}


void dBodyDestroy (dxBody *b)
{
    dAASSERT (b);

    // all geoms that link to this body must be notified that the body is about
    // to disappear. note that the call to dGeomSetBody(geom,0) will result in
    // dGeomGetBodyNext() returning 0 for the body, so we must get the next body
    // before setting the body to 0.
    dxGeom *next_geom = NULL;
    for (dxGeom *geom = b->geom; geom; geom = next_geom) {
        next_geom = dGeomGetBodyNext (geom);
        dGeomSetBody (geom,0);
    }

    // detach all neighbouring joints, then delete this body.
    dxJointNode *n = b->firstjoint;
    while (n) {
        // sneaky trick to speed up removal of joint references (black magic)
        n->joint->node[(n == n->joint->node)].body = NULL;

        dxJointNode *next = n->next;
        n->next = NULL;
        removeJointReferencesFromAttachedBodies (n->joint);
        n = next;
    }
    removeObjectFromList (b);
    b->world->nb--;

    // delete the average buffers
    if(b->average_lvel_buffer)
    {
        delete[] (b->average_lvel_buffer);
        b->average_lvel_buffer = NULL;
    }
    if(b->average_avel_buffer)
    {
        delete[] (b->average_avel_buffer);
        b->average_avel_buffer = NULL;
    }

    delete b;
}


void dBodySetData (dBodyID b, void *data)
{
    dAASSERT (b);
    b->userdata = data;
}


void *dBodyGetData (dBodyID b)
{
    dAASSERT (b);
    return b->userdata;
}


void dBodySetPosition (dBodyID b, dReal x, dReal y, dReal z)
{
    dAASSERT (b);
    b->posr.pos[0] = x;
    b->posr.pos[1] = y;
    b->posr.pos[2] = z;

    // notify all attached geoms that this body has moved
    for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom))
        dGeomMoved (geom);
}


void dBodySetRotation (dBodyID b, const dMatrix3 R)
{
    dAASSERT (b && R);

    memcpy(b->posr.R, R, sizeof(dMatrix3));
    
    bool bOrthogonalizeResult = dxOrthogonalizeR(b->posr.R);
    dAVERIFY(bOrthogonalizeResult);

    dRtoQ (R, b->q);
    dNormalize4 (b->q);

    // notify all attached geoms that this body has moved
    for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom)) {
        dGeomMoved (geom);
    }
}


void dBodySetQuaternion (dBodyID b, const dQuaternion q)
{
    dAASSERT (b && q);
    b->q[0] = q[0];
    b->q[1] = q[1];
    b->q[2] = q[2];
    b->q[3] = q[3];
    dNormalize4 (b->q);
    dQtoR (b->q,b->posr.R);

    // notify all attached geoms that this body has moved
    for (dxGeom *geom = b->geom; geom; geom = dGeomGetBodyNext (geom))
        dGeomMoved (geom);
}


void dBodySetLinearVel  (dBodyID b, dReal x, dReal y, dReal z)
{
    dAASSERT (b);
    b->lvel[0] = x;
    b->lvel[1] = y;
    b->lvel[2] = z;
}


void dBodySetAngularVel (dBodyID b, dReal x, dReal y, dReal z)
{
    dAASSERT (b);
    b->avel[0] = x;
    b->avel[1] = y;
    b->avel[2] = z;
}


const dReal * dBodyGetPosition (dBodyID b)
{
    dAASSERT (b);
    return b->posr.pos;
}


void dBodyCopyPosition (dBodyID b, dVector3 pos)
{
    dAASSERT (b);
    dReal* src = b->posr.pos;
    pos[0] = src[0];
    pos[1] = src[1];
    pos[2] = src[2];
}


const dReal * dBodyGetRotation (dBodyID b)
{
    dAASSERT (b);
    return b->posr.R;
}


void dBodyCopyRotation (dBodyID b, dMatrix3 R)
{
    dAASSERT (b);
    const dReal* src = b->posr.R;
    R[0] = src[0];
    R[1] = src[1];
    R[2] = src[2];
    R[3] = src[3];
    R[4] = src[4];
    R[5] = src[5];
    R[6] = src[6];
    R[7] = src[7];
    R[8] = src[8];
    R[9] = src[9];
    R[10] = src[10];
    R[11] = src[11];
}


const dReal * dBodyGetQuaternion (dBodyID b)
{
    dAASSERT (b);
    return b->q;
}


void dBodyCopyQuaternion (dBodyID b, dQuaternion quat)
{
    dAASSERT (b);
    dReal* src = b->q;
    quat[0] = src[0];
    quat[1] = src[1];
    quat[2] = src[2];
    quat[3] = src[3];
}


const dReal * dBodyGetLinearVel (dBodyID b)
{
    dAASSERT (b);
    return b->lvel;
}


const dReal * dBodyGetAngularVel (dBodyID b)
{
    dAASSERT (b);
    return b->avel;
}


void dBodySetMass (dBodyID b, const dMass *mass)
{
    dAASSERT (b && mass );
    dIASSERT(dMassCheck(mass));

    // The centre of mass must be at the origin.
    // Use dMassTranslate( mass, -mass->c[0], -mass->c[1], -mass->c[2] ) to correct it.
    dUASSERT( fabs( mass->c[0] ) <= dEpsilon &&
        fabs( mass->c[1] ) <= dEpsilon &&
        fabs( mass->c[2] ) <= dEpsilon, "The centre of mass must be at the origin." );

    b->mass = *mass;
    if (dInvertPDMatrix (b->mass.I,b->invI,3,NULL)==0) {
        dDEBUGMSG ("inertia must be positive definite!");
        dRSetIdentity (b->invI);
    }
    b->invMass = dRecip(b->mass.mass);
}


void dBodyGetMass (dBodyID b, dMass *mass)
{
    dAASSERT (b && mass);
    *mass = b->mass;
}


void dBodyAddForce (dBodyID b, dReal fx, dReal fy, dReal fz)
{
    dAASSERT (b);
    b->facc[0] += fx;
    b->facc[1] += fy;
    b->facc[2] += fz;
}


void dBodyAddTorque (dBodyID b, dReal fx, dReal fy, dReal fz)
{
    dAASSERT (b);
    b->tacc[0] += fx;
    b->tacc[1] += fy;
    b->tacc[2] += fz;
}


void dBodyAddRelForce (dBodyID b, dReal fx, dReal fy, dReal fz)
{
    dAASSERT (b);
    dVector3 t1,t2;
    t1[0] = fx;
    t1[1] = fy;
    t1[2] = fz;
    t1[3] = 0;
    dMultiply0_331 (t2,b->posr.R,t1);
    b->facc[0] += t2[0];
    b->facc[1] += t2[1];
    b->facc[2] += t2[2];
}


void dBodyAddRelTorque (dBodyID b, dReal fx, dReal fy, dReal fz)
{
    dAASSERT (b);
    dVector3 t1,t2;
    t1[0] = fx;
    t1[1] = fy;
    t1[2] = fz;
    t1[3] = 0;
    dMultiply0_331 (t2,b->posr.R,t1);
    b->tacc[0] += t2[0];
    b->tacc[1] += t2[1];
    b->tacc[2] += t2[2];
}


void dBodyAddForceAtPos (dBodyID b, dReal fx, dReal fy, dReal fz,
                         dReal px, dReal py, dReal pz)
{
    dAASSERT (b);
    b->facc[0] += fx;
    b->facc[1] += fy;
    b->facc[2] += fz;
    dVector3 f,q;
    f[0] = fx;
    f[1] = fy;
    f[2] = fz;
    q[0] = px - b->posr.pos[0];
    q[1] = py - b->posr.pos[1];
    q[2] = pz - b->posr.pos[2];
    dAddVectorCross3(b->tacc,q,f);
}


void dBodyAddForceAtRelPos (dBodyID b, dReal fx, dReal fy, dReal fz,
                            dReal px, dReal py, dReal pz)
{
    dAASSERT (b);
    dVector3 prel,f,p;
    f[0] = fx;
    f[1] = fy;
    f[2] = fz;
    f[3] = 0;
    prel[0] = px;
    prel[1] = py;
    prel[2] = pz;
    prel[3] = 0;
    dMultiply0_331 (p,b->posr.R,prel);
    b->facc[0] += f[0];
    b->facc[1] += f[1];
    b->facc[2] += f[2];
    dAddVectorCross3(b->tacc,p,f);
}


void dBodyAddRelForceAtPos (dBodyID b, dReal fx, dReal fy, dReal fz,
                            dReal px, dReal py, dReal pz)
{
    dAASSERT (b);
    dVector3 frel,f;
    frel[0] = fx;
    frel[1] = fy;
    frel[2] = fz;
    frel[3] = 0;
    dMultiply0_331 (f,b->posr.R,frel);
    b->facc[0] += f[0];
    b->facc[1] += f[1];
    b->facc[2] += f[2];
    dVector3 q;
    q[0] = px - b->posr.pos[0];
    q[1] = py - b->posr.pos[1];
    q[2] = pz - b->posr.pos[2];
    dAddVectorCross3(b->tacc,q,f);
}


void dBodyAddRelForceAtRelPos (dBodyID b, dReal fx, dReal fy, dReal fz,
                               dReal px, dReal py, dReal pz)
{
    dAASSERT (b);
    dVector3 frel,prel,f,p;
    frel[0] = fx;
    frel[1] = fy;
    frel[2] = fz;
    frel[3] = 0;
    prel[0] = px;
    prel[1] = py;
    prel[2] = pz;
    prel[3] = 0;
    dMultiply0_331 (f,b->posr.R,frel);
    dMultiply0_331 (p,b->posr.R,prel);
    b->facc[0] += f[0];
    b->facc[1] += f[1];
    b->facc[2] += f[2];
    dAddVectorCross3(b->tacc,p,f);
}


const dReal * dBodyGetForce (dBodyID b)
{
    dAASSERT (b);
    return b->facc;
}


const dReal * dBodyGetTorque (dBodyID b)
{
    dAASSERT (b);
    return b->tacc;
}


void dBodySetForce (dBodyID b, dReal x, dReal y, dReal z)
{
    dAASSERT (b);
    b->facc[0] = x;
    b->facc[1] = y;
    b->facc[2] = z;
}


void dBodySetTorque (dBodyID b, dReal x, dReal y, dReal z)
{
    dAASSERT (b);
    b->tacc[0] = x;
    b->tacc[1] = y;
    b->tacc[2] = z;
}


void dBodyGetRelPointPos (dBodyID b, dReal px, dReal py, dReal pz,
                          dVector3 result)
{
    dAASSERT (b);
    dVector3 prel,p;
    prel[0] = px;
    prel[1] = py;
    prel[2] = pz;
    prel[3] = 0;
    dMultiply0_331 (p,b->posr.R,prel);
    result[0] = p[0] + b->posr.pos[0];
    result[1] = p[1] + b->posr.pos[1];
    result[2] = p[2] + b->posr.pos[2];
}


void dBodyGetRelPointVel (dBodyID b, dReal px, dReal py, dReal pz,
                          dVector3 result)
{
    dAASSERT (b);
    dVector3 prel,p;
    prel[0] = px;
    prel[1] = py;
    prel[2] = pz;
    prel[3] = 0;
    dMultiply0_331 (p,b->posr.R,prel);
    result[0] = b->lvel[0];
    result[1] = b->lvel[1];
    result[2] = b->lvel[2];
    dAddVectorCross3(result,b->avel,p);
}


void dBodyGetPointVel (dBodyID b, dReal px, dReal py, dReal pz,
                       dVector3 result)
{
    dAASSERT (b);
    dVector3 p;
    p[0] = px - b->posr.pos[0];
    p[1] = py - b->posr.pos[1];
    p[2] = pz - b->posr.pos[2];
    p[3] = 0;
    result[0] = b->lvel[0];
    result[1] = b->lvel[1];
    result[2] = b->lvel[2];
    dAddVectorCross3(result,b->avel,p);
}


void dBodyGetPosRelPoint (dBodyID b, dReal px, dReal py, dReal pz,
                          dVector3 result)
{
    dAASSERT (b);
    dVector3 prel;
    prel[0] = px - b->posr.pos[0];
    prel[1] = py - b->posr.pos[1];
    prel[2] = pz - b->posr.pos[2];
    prel[3] = 0;
    dMultiply1_331 (result,b->posr.R,prel);
}


void dBodyVectorToWorld (dBodyID b, dReal px, dReal py, dReal pz,
                         dVector3 result)
{
    dAASSERT (b);
    dVector3 p;
    p[0] = px;
    p[1] = py;
    p[2] = pz;
    p[3] = 0;
    dMultiply0_331 (result,b->posr.R,p);
}


void dBodyVectorFromWorld (dBodyID b, dReal px, dReal py, dReal pz,
                           dVector3 result)
{
    dAASSERT (b);
    dVector3 p;
    p[0] = px;
    p[1] = py;
    p[2] = pz;
    p[3] = 0;
    dMultiply1_331 (result,b->posr.R,p);
}


void dBodySetFiniteRotationMode (dBodyID b, int mode)
{
    dAASSERT (b);
    b->flags &= ~(dxBodyFlagFiniteRotation | dxBodyFlagFiniteRotationAxis);
    if (mode) {
        b->flags |= dxBodyFlagFiniteRotation;
        if (b->finite_rot_axis[0] != 0 || b->finite_rot_axis[1] != 0 ||
            b->finite_rot_axis[2] != 0) {
                b->flags |= dxBodyFlagFiniteRotationAxis;
        }
    }
}


void dBodySetFiniteRotationAxis (dBodyID b, dReal x, dReal y, dReal z)
{
    dAASSERT (b);
    b->finite_rot_axis[0] = x;
    b->finite_rot_axis[1] = y;
    b->finite_rot_axis[2] = z;
    if (x != 0 || y != 0 || z != 0) {
        dNormalize3 (b->finite_rot_axis);
        b->flags |= dxBodyFlagFiniteRotationAxis;
    }
    else {
        b->flags &= ~dxBodyFlagFiniteRotationAxis;
    }
}


int dBodyGetFiniteRotationMode (dBodyID b)
{
    dAASSERT (b);
    return ((b->flags & dxBodyFlagFiniteRotation) != 0);
}


void dBodyGetFiniteRotationAxis (dBodyID b, dVector3 result)
{
    dAASSERT (b);
    result[0] = b->finite_rot_axis[0];
    result[1] = b->finite_rot_axis[1];
    result[2] = b->finite_rot_axis[2];
}


int dBodyGetNumJoints (dBodyID b)
{
    dAASSERT (b);
    int count=0;
    for (dxJointNode *n=b->firstjoint; n; n=n->next, count++);
    return count;
}


dJointID dBodyGetJoint (dBodyID b, int index)
{
    dAASSERT (b);
    int i=0;
    for (dxJointNode *n=b->firstjoint; n; n=n->next, i++) {
        if (i == index) return n->joint;
    }
    return 0;
}

void dBodySetDynamic (dBodyID b)
{
    dAASSERT (b);

    dBodySetMass(b,&b->mass);
}

void dBodySetKinematic (dBodyID b)
{
    dAASSERT (b);
    dSetZero (b->invI,4*3);
    b->invMass = 0; 
}

int dBodyIsKinematic (dBodyID b)
{
    dAASSERT (b);
    return b->invMass == 0;
}

void dBodyEnable (dBodyID b)
{
    dAASSERT (b);
    b->flags &= ~dxBodyDisabled;
    b->adis_stepsleft = b->adis.idle_steps;
    b->adis_timeleft = b->adis.idle_time;
    // no code for average-processing needed here
}


void dBodyDisable (dBodyID b)
{
    dAASSERT (b);
    b->flags |= dxBodyDisabled;
}


int dBodyIsEnabled (dBodyID b)
{
    dAASSERT (b);
    return ((b->flags & dxBodyDisabled) == 0);
}


void dBodySetGravityMode (dBodyID b, int mode)
{
    dAASSERT (b);
    if (mode) b->flags &= ~dxBodyNoGravity;
    else b->flags |= dxBodyNoGravity;
}


int dBodyGetGravityMode (dBodyID b)
{
    dAASSERT (b);
    return ((b->flags & dxBodyNoGravity) == 0);
}


// body auto-disable functions

dReal dBodyGetAutoDisableLinearThreshold (dBodyID b)
{
    dAASSERT(b);
    return dSqrt (b->adis.linear_average_threshold);
}


void dBodySetAutoDisableLinearThreshold (dBodyID b, dReal linear_average_threshold)
{
    dAASSERT(b);
    b->adis.linear_average_threshold = linear_average_threshold * linear_average_threshold;
}


dReal dBodyGetAutoDisableAngularThreshold (dBodyID b)
{
    dAASSERT(b);
    return dSqrt (b->adis.angular_average_threshold);
}


void dBodySetAutoDisableAngularThreshold (dBodyID b, dReal angular_average_threshold)
{
    dAASSERT(b);
    b->adis.angular_average_threshold = angular_average_threshold * angular_average_threshold;
}


int dBodyGetAutoDisableAverageSamplesCount (dBodyID b)
{
    dAASSERT(b);
    return b->adis.average_samples;
}


void dBodySetAutoDisableAverageSamplesCount (dBodyID b, unsigned int average_samples_count)
{
    dAASSERT(b);
    b->adis.average_samples = average_samples_count;
    // update the average buffers
    if(b->average_lvel_buffer)
    {
        delete[] b->average_lvel_buffer;
        b->average_lvel_buffer = NULL;
    }
    if(b->average_avel_buffer)
    {
        delete[] b->average_avel_buffer;
        b->average_avel_buffer = NULL;
    }
    if(b->adis.average_samples > 0)
    {
        b->average_lvel_buffer = new dVector3[b->adis.average_samples];
        b->average_avel_buffer = new dVector3[b->adis.average_samples];
    }
    else
    {
        b->average_lvel_buffer = NULL;
        b->average_avel_buffer = NULL;
    }
    // new buffer is empty
    b->average_counter = 0;
    b->average_ready = 0;
}


int dBodyGetAutoDisableSteps (dBodyID b)
{
    dAASSERT(b);
    return b->adis.idle_steps;
}


void dBodySetAutoDisableSteps (dBodyID b, int steps)
{
    dAASSERT(b);
    b->adis.idle_steps = steps;
}


dReal dBodyGetAutoDisableTime (dBodyID b)
{
    dAASSERT(b);
    return b->adis.idle_time;
}


void dBodySetAutoDisableTime (dBodyID b, dReal time)
{
    dAASSERT(b);
    b->adis.idle_time = time;
}


int dBodyGetAutoDisableFlag (dBodyID b)
{
    dAASSERT(b);
    return ((b->flags & dxBodyAutoDisable) != 0);
}


void dBodySetAutoDisableFlag (dBodyID b, int do_auto_disable)
{
    dAASSERT(b);
    if (!do_auto_disable)
    {
        b->flags &= ~dxBodyAutoDisable;
        // (mg) we should also reset the IsDisabled state to correspond to the DoDisabling flag
        b->flags &= ~dxBodyDisabled;
        b->adis.idle_steps = dWorldGetAutoDisableSteps(b->world);
        b->adis.idle_time = dWorldGetAutoDisableTime(b->world);
        // resetting the average calculations too
        dBodySetAutoDisableAverageSamplesCount(b, dWorldGetAutoDisableAverageSamplesCount(b->world) );
    }
    else
    {
        b->flags |= dxBodyAutoDisable;
    }
}


void dBodySetAutoDisableDefaults (dBodyID b)
{
    dAASSERT(b);
    dWorldID w = b->world;
    dAASSERT(w);
    b->adis = w->adis;
    dBodySetAutoDisableFlag (b, w->body_flags & dxBodyAutoDisable);
}


// body damping functions

dReal dBodyGetLinearDamping(dBodyID b)
{
    dAASSERT(b);
    return b->dampingp.linear_scale;
}

void dBodySetLinearDamping(dBodyID b, dReal scale)
{
    dAASSERT(b);
    if (scale)
        b->flags |= dxBodyLinearDamping;
    else
        b->flags &= ~dxBodyLinearDamping;
    b->dampingp.linear_scale = scale;
}

dReal dBodyGetAngularDamping(dBodyID b)
{
    dAASSERT(b);
    return b->dampingp.angular_scale;
}

void dBodySetAngularDamping(dBodyID b, dReal scale)
{
    dAASSERT(b);
    if (scale)
        b->flags |= dxBodyAngularDamping;
    else
        b->flags &= ~dxBodyAngularDamping;
    b->dampingp.angular_scale = scale;
}

void dBodySetDamping(dBodyID b, dReal linear_scale, dReal angular_scale)
{
    dAASSERT(b);
    dBodySetLinearDamping(b, linear_scale);
    dBodySetAngularDamping(b, angular_scale);
}

dReal dBodyGetLinearDampingThreshold(dBodyID b)
{
    dAASSERT(b);
    return dSqrt(b->dampingp.linear_threshold);
}

void dBodySetLinearDampingThreshold(dBodyID b, dReal threshold)
{
    dAASSERT(b);
    b->dampingp.linear_threshold = threshold*threshold;
}


dReal dBodyGetAngularDampingThreshold(dBodyID b)
{
    dAASSERT(b);
    return dSqrt(b->dampingp.angular_threshold);
}

void dBodySetAngularDampingThreshold(dBodyID b, dReal threshold)
{
    dAASSERT(b);
    b->dampingp.angular_threshold = threshold*threshold;
}

void dBodySetDampingDefaults(dBodyID b)
{
    dAASSERT(b);
    dWorldID w = b->world;
    dAASSERT(w);
    b->dampingp = w->dampingp;
    const unsigned mask = dxBodyLinearDamping | dxBodyAngularDamping;
    b->flags &= ~mask; // zero them
    b->flags |= w->body_flags & mask;
}

dReal dBodyGetMaxAngularSpeed(dBodyID b)
{
    dAASSERT(b);
    return b->max_angular_speed;
}

void dBodySetMaxAngularSpeed(dBodyID b, dReal max_speed)
{
    dAASSERT(b);
    if (max_speed < dInfinity)
        b->flags |= dxBodyMaxAngularSpeed;
    else
        b->flags &= ~dxBodyMaxAngularSpeed;
    b->max_angular_speed = max_speed;
}

void dBodySetMovedCallback(dBodyID b, void (*callback)(dBodyID))
{
    dAASSERT(b);
    b->moved_callback = callback;
}


dGeomID dBodyGetFirstGeom(dBodyID b)
{
    dAASSERT(b);
    return b->geom;
}


dGeomID dBodyGetNextGeom(dGeomID geom)
{
    dAASSERT(geom);
    return dGeomGetBodyNext(geom);
}


int dBodyGetGyroscopicMode(dBodyID b)
{
    dAASSERT(b);
    return b->flags & dxBodyGyroscopic;
}

void dBodySetGyroscopicMode(dBodyID b, int enabled)
{
    dAASSERT(b);
    if (enabled)
        b->flags |= dxBodyGyroscopic;
    else
        b->flags &= ~dxBodyGyroscopic;
}



//****************************************************************************
// joints



template<class T>
dxJoint* createJoint(dWorldID w, dJointGroupID group)
{
    dxJoint *j;
    if (group) {
        j = group->alloc<T>(w);
    } else {
        j = new T(w);
    }
    return j;
}


dxJoint * dJointCreateBall (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointBall>(w,group);
}


dxJoint * dJointCreateHinge (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointHinge>(w,group);
}


dxJoint * dJointCreateSlider (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointSlider>(w,group);
}


dxJoint * dJointCreateContact (dWorldID w, dJointGroupID group,
                               const dContact *c)
{
    dAASSERT (w && c);
    dxJointContact *j = (dxJointContact *)
        createJoint<dxJointContact> (w,group);
    j->contact = *c;
    return j;
}


dxJoint * dJointCreateHinge2 (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointHinge2> (w,group);
}


dxJoint * dJointCreateUniversal (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointUniversal> (w,group);
}

dxJoint * dJointCreatePR (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointPR> (w,group);
}

dxJoint * dJointCreatePU (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointPU> (w,group);
}

dxJoint * dJointCreatePiston (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointPiston> (w,group);
}

dxJoint * dJointCreateFixed (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointFixed> (w,group);
}


dxJoint * dJointCreateNull (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointNull> (w,group);
}


dxJoint * dJointCreateAMotor (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointAMotor> (w,group);
}

dxJoint * dJointCreateLMotor (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointLMotor> (w,group);
}

dxJoint * dJointCreatePlane2D (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointPlane2D> (w,group);
}

dxJoint * dJointCreateDBall (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointDBall> (w,group);
}

dxJoint * dJointCreateDHinge (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointDHinge> (w,group);
}


dxJoint * dJointCreateTransmission (dWorldID w, dJointGroupID group)
{
    dAASSERT (w);
    return createJoint<dxJointTransmission> (w,group);
}

static void FinalizeAndDestroyJointInstance(dxJoint *j, bool delete_it)
{
    // if any group joints have their world pointer set to 0, their world was
    // previously destroyed. no special handling is required for these joints.
    if (j->world != NULL) {
        removeJointReferencesFromAttachedBodies (j);
        removeObjectFromList (j);
        j->world->nj--;
    }
    if (delete_it) { 
        delete j;
    } else {
        j->~dxJoint();
    }
}

void dJointDestroy (dxJoint *j)
{
    dAASSERT (j);
    if (!(j->flags & dJOINT_INGROUP)) {
        FinalizeAndDestroyJointInstance(j, true);
    }
}


dJointGroupID dJointGroupCreate (int /*max_size*/)
{
    // not any more ... dUASSERT (max_size > 0,"max size must be > 0");
    dxJointGroup *group = new dxJointGroup();
    return group;
}


void dJointGroupDestroy (dJointGroupID group)
{
    dAASSERT (group);
    dJointGroupEmpty (group);
    delete group;
}

void dJointGroupEmpty (dJointGroupID group)
{
    dAASSERT (group);

    const sizeint num_joints = group->getJointCount();
    if (num_joints != 0) {
        // Local array is used since ALLOCA leads to mysterious NULL values in first array element and crashes under VS2005 :)
        const sizeint max_stack_jlist_size = 1024;
        dxJoint *stack_jlist[max_stack_jlist_size];

        const sizeint jlist_size = num_joints * sizeof(dxJoint*);
        dxJoint **jlist = num_joints <= max_stack_jlist_size ? stack_jlist : (dxJoint **)dAlloc(jlist_size);

        if (jlist != NULL) {
            // the joints in this group are detached starting from the most recently
            // added (at the top of the stack). this helps ensure that the various
            // linked lists are not traversed too much, as the joints will hopefully
            // be at the start of those lists.
            sizeint num_exported = group->exportJoints(jlist);
            dIVERIFY(num_exported == num_joints);

            for (sizeint i = num_joints; i != 0; ) {
                --i;
                dxJoint *j = jlist[i];
                FinalizeAndDestroyJointInstance(j, false);
            }
        } else {
            // ...else if there is no memory, go on detaching the way it is possible
            sizeint joint_bytes;
            for (dxJoint *j = (dxJoint *)group->beginEnum(); j != NULL; j = (dxJoint *)group->continueEnum(joint_bytes)) {
                joint_bytes = j->size(); // Get size before object is destroyed!
                FinalizeAndDestroyJointInstance(j, false);
            }
        }

        group->freeAll();

        if (jlist != stack_jlist && jlist != NULL) {
            dFree(jlist, jlist_size);
        }
    }
}


int dJointGetNumBodies(dxJoint *joint)
{
    // check arguments
    dUASSERT (joint,"bad joint argument");

    if ( !joint->node[0].body )
        return 0;
    else if ( !joint->node[1].body )
        return 1;
    else
        return 2;
}


void dJointAttach (dxJoint *joint, dxBody *body1, dxBody *body2)
{
    // check arguments
    dUASSERT (joint,"bad joint argument");
    dUASSERT (body1 == NULL || body1 != body2, "can't have body1==body2");
    dxWorld *world = joint->world;
    dUASSERT ( (body1 == NULL || body1->world == world) &&
        (body2 == NULL || body2->world == world),
        "joint and bodies must be in same world");

    // check if the joint can not be attached to just one body
    dUASSERT (!((joint->flags & dJOINT_TWOBODIES) &&
        ((body1 != NULL) != (body2 != NULL))),
        "joint can not be attached to just one body");

    // remove any existing body attachments
    if (joint->node[0].body != NULL || joint->node[1].body != NULL) {
        removeJointReferencesFromAttachedBodies (joint);
    }

    // if a body is zero, make sure that it is body2, so 0 --> node[1].body
    if (body1 == NULL) {
        body1 = body2;
        body2 = NULL;
        joint->flags |= dJOINT_REVERSE;
    }
    else {
        joint->flags &= (~dJOINT_REVERSE);
    }

    // attach to new bodies
    joint->node[0].body = body1;
    joint->node[1].body = body2;
    
    if (body1 != NULL) {
        joint->node[1].next = body1->firstjoint;
        body1->firstjoint = &joint->node[1];
    }
    else {
        joint->node[1].next = NULL;
    }
    
    if (body2 != NULL) {
        joint->node[0].next = body2->firstjoint;
        body2->firstjoint = &joint->node[0];
    }
    else {
        joint->node[0].next = NULL;
    }

    // Since the bodies are now set.
    // Calculate the values depending on the bodies.
    // Only need to calculate relative value if a body exist
    if (body1 != NULL || body2 != NULL) {
        joint->setRelativeValues();
    }
}

void dJointEnable (dxJoint *joint)
{
    dAASSERT (joint);
    joint->flags &= ~dJOINT_DISABLED;
}

void dJointDisable (dxJoint *joint)
{
    dAASSERT (joint);
    joint->flags |= dJOINT_DISABLED;
}

int dJointIsEnabled (dxJoint *joint)
{
    dAASSERT (joint);
    return (joint->flags & dJOINT_DISABLED) == 0;
}

void dJointSetData (dxJoint *joint, void *data)
{
    dAASSERT (joint);
    joint->userdata = data;
}


void *dJointGetData (dxJoint *joint)
{
    dAASSERT (joint);
    return joint->userdata;
}


dJointType dJointGetType (dxJoint *joint)
{
    dAASSERT (joint);
    return joint->type();
}


dBodyID dJointGetBody (dxJoint *joint, int index)
{
    dAASSERT (joint);
    if (index == 0 || index == 1) {
        if (joint->flags & dJOINT_REVERSE) return joint->node[1-index].body;
        else return joint->node[index].body;
    }
    else return 0;
}


void dJointSetFeedback (dxJoint *joint, dJointFeedback *f)
{
    dAASSERT (joint);
    joint->feedback = f;
}


dJointFeedback *dJointGetFeedback (dxJoint *joint)
{
    dAASSERT (joint);
    return joint->feedback;
}



dJointID dConnectingJoint (dBodyID in_b1, dBodyID in_b2)
{
    dAASSERT (in_b1 || in_b2);

    dBodyID b1, b2;

    if (in_b1 == 0) {
        b1 = in_b2;
        b2 = in_b1;
    }
    else {
        b1 = in_b1;
        b2 = in_b2;
    }

    // look through b1's neighbour list for b2
    for (dxJointNode *n=b1->firstjoint; n; n=n->next) {
        if (n->body == b2) return n->joint;
    }

    return 0;
}



int dConnectingJointList (dBodyID in_b1, dBodyID in_b2, dJointID* out_list)
{
    dAASSERT (in_b1 || in_b2);


    dBodyID b1, b2;

    if (in_b1 == 0) {
        b1 = in_b2;
        b2 = in_b1;
    }
    else {
        b1 = in_b1;
        b2 = in_b2;
    }

    // look through b1's neighbour list for b2
    int numConnectingJoints = 0;
    for (dxJointNode *n=b1->firstjoint; n; n=n->next) {
        if (n->body == b2)
            out_list[numConnectingJoints++] = n->joint;
    }

    return numConnectingJoints;
}


int dAreConnected (dBodyID b1, dBodyID b2)
{
    dAASSERT (b1/* && b2*/); // b2 can be NULL to test for connection to environment
    // look through b1's neighbour list for b2
    for (dxJointNode *n=b1->firstjoint; n; n=n->next) {
        if (n->body == b2) return 1;
    }
    return 0;
}


int dAreConnectedExcluding (dBodyID b1, dBodyID b2, int joint_type)
{
    dAASSERT (b1/* && b2*/); // b2 can be NULL to test for connection to environment
    // look through b1's neighbour list for b2
    for (dxJointNode *n=b1->firstjoint; n; n=n->next) {
        if (dJointGetType (n->joint) != joint_type && n->body == b2) return 1;
    }
    return 0;
}

//****************************************************************************
// world

dxWorld * dWorldCreate()
{
    dxWorld *w = new dxWorld();

    return w;
}


void dWorldDestroy (dxWorld *w)
{
    // delete all bodies and joints
    dAASSERT (w);
    dxBody *nextb, *b = w->firstbody;
    while (b) {
        nextb = (dxBody*) b->next;
        dBodyDestroy(b); // calling here dBodyDestroy for correct destroying! (i.e. the average buffers)
        b = nextb;
    }

    dxJoint *nextj, *j = w->firstjoint;
    while (j) {
        nextj = (dxJoint*)j->next;
        if (j->flags & dJOINT_INGROUP) {
            // the joint is part of a group, so "deactivate" it instead
            j->world = NULL;
            j->node[0].body = NULL;
            j->node[0].next = NULL;
            j->node[1].body = NULL;
            j->node[1].next = NULL;
            dMessage (0,"warning: destroying world containing grouped joints");
        }
        else {
            // TODO: shouldn't we call dJointDestroy()?
            sizeint sz = j->size();
            j->~dxJoint();
            dFree (j,sz);
        }
        j = nextj;
    }

    delete w;
}


void dWorldSetData (dWorldID w, void *data)
{
    dAASSERT (w);
    w->userdata = data;
}


void* dWorldGetData (dWorldID w)
{
    dAASSERT (w);
    return w->userdata;
}


void dWorldSetGravity (dWorldID w, dReal x, dReal y, dReal z)
{
    dAASSERT (w);
    w->gravity[0] = x;
    w->gravity[1] = y;
    w->gravity[2] = z;
}


void dWorldGetGravity (dWorldID w, dVector3 g)
{
    dAASSERT (w);
    g[0] = w->gravity[0];
    g[1] = w->gravity[1];
    g[2] = w->gravity[2];
}


void dWorldSetERP (dWorldID w, dReal erp)
{
    dAASSERT (w);
    w->global_erp = erp;
}


dReal dWorldGetERP (dWorldID w)
{
    dAASSERT (w);
    return w->global_erp;
}


void dWorldSetCFM (dWorldID w, dReal cfm)
{
    dAASSERT (w);
    w->global_cfm = cfm;
}


dReal dWorldGetCFM (dWorldID w)
{
    dAASSERT (w);
    return w->global_cfm;
}


void dWorldSetStepIslandsProcessingMaxThreadCount(dWorldID w, unsigned count)
{
    dAASSERT (w);
    w->islands_max_threads = count;
}

unsigned dWorldGetStepIslandsProcessingMaxThreadCount(dWorldID w)
{
    dAASSERT (w);
    return w->islands_max_threads;
}

int dWorldUseSharedWorkingMemory(dWorldID w, dWorldID from_world)
{
    dUASSERT (w,"bad world argument");

    bool result = false;

    if (from_world)
    {
        dUASSERT (!w->wmem, "world does already have working memory allocated"); // Prevent replacement of one memory object with another to avoid cases when smaller buffer replaces a larger one or memory manager changes.

        dxStepWorkingMemory *wmem = AllocateOnDemand(from_world->wmem);

        if (wmem)
        {
            // Even though there is an assertion check on entry still release existing
            // memory object for extra safety.
            if (w->wmem)
            {
                w->wmem->Release();
                w->wmem = NULL;
            }

            wmem->Addref();
            w->wmem = wmem;

            result = true;
        }
    }
    else
    {
        dxStepWorkingMemory *wmem = w->wmem;

        if (wmem)
        {
            wmem->Release();
            w->wmem = NULL;
        }

        result = true;
    }

    return result;
}

void dWorldCleanupWorkingMemory(dWorldID w)
{
    dUASSERT (w,"bad world argument");

    dxStepWorkingMemory *wmem = w->wmem;

    if (wmem)
    {
        wmem->CleanupMemory();
    }
}

int dWorldSetStepMemoryReservationPolicy(dWorldID w, const dWorldStepReserveInfo *policyinfo)
{
    dUASSERT (w,"bad world argument");
    dUASSERT (!policyinfo || (policyinfo->struct_size >= sizeof(*policyinfo) && policyinfo->reserve_factor >= 1.0f), "Bad policy info");

    bool result = false;

    dxStepWorkingMemory *wmem = policyinfo ? AllocateOnDemand(w->wmem) : w->wmem;

    if (wmem)
    {
        if (policyinfo)
        {
            wmem->SetMemoryReserveInfo(policyinfo->reserve_factor, policyinfo->reserve_minimum);
            result = wmem->GetMemoryReserveInfo() != NULL;
        }
        else
        {
            wmem->ResetMemoryReserveInfoToDefault();
            result = true;
        }
    }
    else if (!policyinfo)
    {
        result = true;
    }

    return result;
}

int dWorldSetStepMemoryManager(dWorldID w, const dWorldStepMemoryFunctionsInfo *memfuncs)
{
    dUASSERT (w,"bad world argument");
    dUASSERT (!memfuncs || memfuncs->struct_size >= sizeof(*memfuncs), "Bad memory functions info");

    bool result = false;

    dxStepWorkingMemory *wmem = memfuncs ? AllocateOnDemand(w->wmem) : w->wmem;

    if (wmem)
    {
        if (memfuncs)
        {
            wmem->SetMemoryManager(memfuncs->alloc_block, memfuncs->shrink_block, memfuncs->free_block);
            result = wmem->GetMemoryManager() != NULL;
        }
        else
        {
            wmem->ResetMemoryManagerToDefault();
            result = true;
        }
    }
    else if (!memfuncs)
    {
        result = true;
    }

    return result;
}

void dWorldSetStepThreadingImplementation(dWorldID w, 
    const dxThreadingFunctionsInfo *functions_info, dThreadingImplementationID threading_impl)
{
    dUASSERT (w,"bad world argument");
    dUASSERT (!functions_info || functions_info->struct_size >= sizeof(*functions_info), "Bad threading functions info");

#if dTHREADING_INTF_DISABLED
    dUASSERT(functions_info == NULL && threading_impl == NULL, "Threading interface is not available");
#else
    w->assignThreadingImpl(functions_info, threading_impl);
#endif
}


int dWorldStep (dWorldID w, dReal stepsize)
{
    dUASSERT (w,"bad world argument");
    dUASSERT (stepsize > 0,"stepsize must be > 0");

    bool result = false;

    dxWorldProcessIslandsInfo islandsinfo;
    if (dxReallocateWorldProcessContext (w, islandsinfo, stepsize, &dxEstimateStepMemoryRequirements))
    {
        if (dxProcessIslands (w, islandsinfo, stepsize, &dxStepIsland, &dxEstimateStepMaxCallCount))
        {
            result = true;
        }
    }

    return result;
}

int dWorldQuickStep (dWorldID w, dReal stepsize)
{
    dUASSERT (w,"bad world argument");
    dUASSERT (stepsize > 0,"stepsize must be > 0");

    bool result = false;

    dxWorldProcessIslandsInfo islandsinfo;
    if (dxReallocateWorldProcessContext (w, islandsinfo, stepsize, &dxEstimateQuickStepMemoryRequirements))
    {
        if (dxProcessIslands (w, islandsinfo, stepsize, &dxQuickStepIsland, &dxEstimateQuickStepMaxCallCount))
        {
            result = true;
        }
    }

    return result;
}


void dWorldImpulseToForce (dWorldID w, dReal stepsize,
                           dReal ix, dReal iy, dReal iz,
                           dVector3 force)
{
    dAASSERT (w);
    stepsize = dRecip(stepsize);
    force[0] = stepsize * ix;
    force[1] = stepsize * iy;
    force[2] = stepsize * iz;
    // @@@ force[3] = 0;
}


// world auto-disable functions

dReal dWorldGetAutoDisableLinearThreshold (dWorldID w)
{
    dAASSERT(w);
    return dSqrt (w->adis.linear_average_threshold);
}


void dWorldSetAutoDisableLinearThreshold (dWorldID w, dReal linear_average_threshold)
{
    dAASSERT(w);
    w->adis.linear_average_threshold = linear_average_threshold * linear_average_threshold;
}


dReal dWorldGetAutoDisableAngularThreshold (dWorldID w)
{
    dAASSERT(w);
    return dSqrt (w->adis.angular_average_threshold);
}


void dWorldSetAutoDisableAngularThreshold (dWorldID w, dReal angular_average_threshold)
{
    dAASSERT(w);
    w->adis.angular_average_threshold = angular_average_threshold * angular_average_threshold;
}


int dWorldGetAutoDisableAverageSamplesCount (dWorldID w)
{
    dAASSERT(w);
    return w->adis.average_samples;
}


void dWorldSetAutoDisableAverageSamplesCount (dWorldID w, unsigned int average_samples_count)
{
    dAASSERT(w);
    w->adis.average_samples = average_samples_count;
}


int dWorldGetAutoDisableSteps (dWorldID w)
{
    dAASSERT(w);
    return w->adis.idle_steps;
}


void dWorldSetAutoDisableSteps (dWorldID w, int steps)
{
    dAASSERT(w);
    w->adis.idle_steps = steps;
}


dReal dWorldGetAutoDisableTime (dWorldID w)
{
    dAASSERT(w);
    return w->adis.idle_time;
}


void dWorldSetAutoDisableTime (dWorldID w, dReal time)
{
    dAASSERT(w);
    w->adis.idle_time = time;
}


int dWorldGetAutoDisableFlag (dWorldID w)
{
    dAASSERT(w);
    return w->body_flags & dxBodyAutoDisable;
}


void dWorldSetAutoDisableFlag (dWorldID w, int do_auto_disable)
{
    dAASSERT(w);
    if (do_auto_disable)
        w->body_flags |= dxBodyAutoDisable;
    else
        w->body_flags &= ~dxBodyAutoDisable;
}


// world damping functions

dReal dWorldGetLinearDampingThreshold(dWorldID w)
{
    dAASSERT(w);
    return dSqrt(w->dampingp.linear_threshold);
}

void dWorldSetLinearDampingThreshold(dWorldID w, dReal threshold)
{
    dAASSERT(w);
    w->dampingp.linear_threshold = threshold*threshold;
}

dReal dWorldGetAngularDampingThreshold(dWorldID w)
{
    dAASSERT(w);
    return dSqrt(w->dampingp.angular_threshold);
}

void dWorldSetAngularDampingThreshold(dWorldID w, dReal threshold)
{
    dAASSERT(w);
    w->dampingp.angular_threshold = threshold*threshold;
}

dReal dWorldGetLinearDamping(dWorldID w)
{
    dAASSERT(w);
    return w->dampingp.linear_scale;
}

void dWorldSetLinearDamping(dWorldID w, dReal scale)
{
    dAASSERT(w);
    if (scale)
        w->body_flags |= dxBodyLinearDamping;
    else
        w->body_flags &= ~dxBodyLinearDamping;
    w->dampingp.linear_scale = scale;
}

dReal dWorldGetAngularDamping(dWorldID w)
{
    dAASSERT(w);
    return w->dampingp.angular_scale;
}

void dWorldSetAngularDamping(dWorldID w, dReal scale)
{
    dAASSERT(w);
    if (scale)
        w->body_flags |= dxBodyAngularDamping;
    else
        w->body_flags &= ~dxBodyAngularDamping;
    w->dampingp.angular_scale = scale;
}

void dWorldSetDamping(dWorldID w, dReal linear_scale, dReal angular_scale)
{
    dAASSERT(w);
    dWorldSetLinearDamping(w, linear_scale);
    dWorldSetAngularDamping(w, angular_scale);
}

dReal dWorldGetMaxAngularSpeed(dWorldID w)
{
    dAASSERT(w);
    return w->max_angular_speed;
}

void dWorldSetMaxAngularSpeed(dWorldID w, dReal max_speed)
{
    dAASSERT(w);
    if (max_speed < dInfinity)
        w->body_flags |= dxBodyMaxAngularSpeed;
    else
        w->body_flags &= ~dxBodyMaxAngularSpeed;
    w->max_angular_speed = max_speed;
}


void dWorldSetQuickStepNumIterations (dWorldID w, int num)
{
    dAASSERT(w);
    dAASSERT(num > 0);

    w->qs.AssignNumIterations(dMAX(num, 1)); // QuickStep implementation relies of number of iteration not being zero
}

int dWorldGetQuickStepNumIterations (dWorldID w)
{
    dAASSERT(w);
    
    return w->qs.GetNumIterations();
}


/*extern */
void dWorldSetQuickStepDynamicIterationParameters(dWorldID w, const dReal *ptr_iteration_premature_exit_delta/*=NULL*/,
    const dReal *ptr_max_num_extra_factor/*=NULL*/, const dReal *ptr_extra_iteration_requirement_delta/*=NULL*/)
{
    dAASSERT(w);
    dAASSERT(ptr_iteration_premature_exit_delta != NULL || ptr_max_num_extra_factor != NULL || ptr_extra_iteration_requirement_delta != NULL);
    dAASSERT(ptr_iteration_premature_exit_delta == NULL || *ptr_iteration_premature_exit_delta >= 0);
    dAASSERT(ptr_max_num_extra_factor == NULL || *ptr_max_num_extra_factor >= 0);
    dAASSERT(ptr_extra_iteration_requirement_delta == NULL || *ptr_extra_iteration_requirement_delta >= 0);

    if (ptr_iteration_premature_exit_delta != NULL) {
        w->qs.AssignPrematureExitDelta(dMAX(*ptr_iteration_premature_exit_delta, REAL(0.0)));
    }

    if (ptr_extra_iteration_requirement_delta != NULL) {
        w->qs.AssignExtraIterationsRequirementDelta(dMAX(*ptr_extra_iteration_requirement_delta, REAL(0.0)));
    }

    if (ptr_max_num_extra_factor != NULL) {
        w->qs.AssignMaxNumExtraFactor(dMAX(*ptr_max_num_extra_factor, REAL(0.0)));
    }
}

/*extern */
void dWorldGetQuickStepDynamicIterationParameters(dWorldID w, dReal *out_iteration_premature_exit_delta/*=NULL*/,
    dReal *out_max_num_extra_factor/*=NULL*/, dReal *out_extra_iteration_requirement_delta/*=NULL*/)
{
    dAASSERT(w);
    dAASSERT(out_iteration_premature_exit_delta != NULL || out_max_num_extra_factor != NULL || out_extra_iteration_requirement_delta != NULL);

    if (out_iteration_premature_exit_delta != NULL) {
        *out_iteration_premature_exit_delta = w->qs.GetPrematureExitDelta();
    }

    if (out_extra_iteration_requirement_delta != NULL) {
        *out_extra_iteration_requirement_delta = w->qs.GetExtraIterationsRequirementDelta();
    }

    if (out_max_num_extra_factor != NULL) {
        *out_max_num_extra_factor = w->qs.GetMaxNumExtraFactor();
    }
}

/*extern */
int dWorldAttachQuickStepDynamicIterationStatisticsSink(dWorldID w, dWorldQuickStepIterationCount_DynamicAdjustmentStatistics *var_stats/*=NULL*/)
{
    dAASSERT(w);
    dAASSERT(var_stats == NULL || (var_stats->struct_size >= sizeof(*var_stats) && var_stats->struct_size % sizeof(duint32) == 0));
    dSASSERT(sizeof(dWorldQuickStepIterationCount_DynamicAdjustmentStatistics) % sizeof(duint32) == 0);

    bool result = false;

    if (var_stats != NULL) {
        if (var_stats->struct_size >= sizeof(*var_stats) && var_stats->struct_size % sizeof(duint32) == 0) {

            w->qs.AssignStatisticsSink(var_stats);
            result = true;
        }
    }
    else {
        w->qs.ClearStatisticsSink();
        result = true;
    }

    return result;
}


void dWorldSetQuickStepW (dWorldID w, dReal param)
{
    dAASSERT(w);
    w->qs.w = param;
}


dReal dWorldGetQuickStepW (dWorldID w)
{
    dAASSERT(w);
    return w->qs.w;
}


void dWorldSetContactMaxCorrectingVel (dWorldID w, dReal vel)
{
    dAASSERT(w);
    w->contactp.max_vel = vel;
}


dReal dWorldGetContactMaxCorrectingVel (dWorldID w)
{
    dAASSERT(w);
    return w->contactp.max_vel;
}


void dWorldSetContactSurfaceLayer (dWorldID w, dReal depth)
{
    dAASSERT(w);
    w->contactp.min_depth = depth;
}


dReal dWorldGetContactSurfaceLayer (dWorldID w)
{
    dAASSERT(w);
    return w->contactp.min_depth;
}

//****************************************************************************
// testing

#define NUM 100

#define DO(x)


extern "C" void dTestDataStructures()
{
    int i;
    DO(printf ("testDynamicsStuff()\n"));

    dBodyID body [NUM];
    int nb = 0;
    dJointID joint [NUM];
    int nj = 0;

    for (i=0; i<NUM; i++) body[i] = NULL;
    for (i=0; i<NUM; i++) joint[i] = NULL;

    DO(printf ("creating world\n"));
    dWorldID w = dWorldCreate();
    checkWorld (w);

    for (;;) {
        if (nb < NUM && dRandReal() > 0.5) {
            DO(printf ("creating body\n"));
            body[nb] = dBodyCreate (w);
            DO(printf ("\t--> %p\n",body[nb]));
            nb++;
            checkWorld (w);
            DO(printf ("%d BODIES, %d JOINTS\n",nb,nj));
        }
        if (nj < NUM && nb > 2 && dRandReal() > 0.5) {
            dBodyID b1 = body [dRand() % nb];
            dBodyID b2 = body [dRand() % nb];
            if (b1 != b2) {
                DO(printf ("creating joint, attaching to %p,%p\n",b1,b2));
                joint[nj] = dJointCreateBall (w,0);
                DO(printf ("\t-->%p\n",joint[nj]));
                checkWorld (w);
                dJointAttach (joint[nj],b1,b2);
                nj++;
                checkWorld (w);
                DO(printf ("%d BODIES, %d JOINTS\n",nb,nj));
            }
        }
        if (nj > 0 && nb > 2 && dRandReal() > 0.5) {
            dBodyID b1 = body [dRand() % nb];
            dBodyID b2 = body [dRand() % nb];
            if (b1 != b2) {
                int k = dRand() % nj;
                DO(printf ("reattaching joint %p\n",joint[k]));
                dJointAttach (joint[k],b1,b2);
                checkWorld (w);
                DO(printf ("%d BODIES, %d JOINTS\n",nb,nj));
            }
        }
        if (nb > 0 && dRandReal() > 0.5) {
            int k = dRand() % nb;
            DO(printf ("destroying body %p\n",body[k]));
            dBodyDestroy (body[k]);
            checkWorld (w);
            for (; k < (NUM-1); k++) body[k] = body[k+1];
            nb--;
            DO(printf ("%d BODIES, %d JOINTS\n",nb,nj));
        }
        if (nj > 0 && dRandReal() > 0.5) {
            int k = dRand() % nj;
            DO(printf ("destroying joint %p\n",joint[k]));
            dJointDestroy (joint[k]);
            checkWorld (w);
            for (; k < (NUM-1); k++) joint[k] = joint[k+1];
            nj--;
            DO(printf ("%d BODIES, %d JOINTS\n",nb,nj));
        }
    }

    /*
    printf ("creating world\n");
    dWorldID w = dWorldCreate();
    checkWorld (w);
    printf ("creating body\n");
    dBodyID b1 = dBodyCreate (w);
    checkWorld (w);
    printf ("creating body\n");
    dBodyID b2 = dBodyCreate (w);
    checkWorld (w);
    printf ("creating joint\n");
    dJointID j = dJointCreateBall (w);
    checkWorld (w);
    printf ("attaching joint\n");
    dJointAttach (j,b1,b2);
    checkWorld (w);
    printf ("destroying joint\n");
    dJointDestroy (j);
    checkWorld (w);
    printf ("destroying body\n");
    dBodyDestroy (b1);
    checkWorld (w);
    printf ("destroying body\n");
    dBodyDestroy (b2);
    checkWorld (w);
    printf ("destroying world\n");
    dWorldDestroy (w);
    */
}

//****************************************************************************
// configuration
#if 1
#define REGISTER_EXTENSION( __a )  #__a " "
#else
#define REGISTER_EXTENSION( __a )  "__a "
#endif
static const char ode_configuration[] = "ODE "

// EXTENSION LIST BEGIN
//**********************************

#ifdef dNODEBUG
REGISTER_EXTENSION( ODE_EXT_no_debug )
#endif // dNODEBUG

#if dTRIMESH_ENABLED
REGISTER_EXTENSION( ODE_EXT_trimesh )

// tri-mesh extensions
#if dTRIMESH_OPCODE
REGISTER_EXTENSION( ODE_EXT_opcode )

// opcode extensions
#if dTRIMESH_16BIT_INDICES
REGISTER_EXTENSION( ODE_OPC_16bit_indices )
#endif

#if !dTRIMESH_OPCODE_USE_OLD_TRIMESH_TRIMESH_COLLIDER
REGISTER_EXTENSION( ODE_OPC_new_collider )
#endif

#endif // dTRIMESH_OPCODE

#if dTRIMESH_GIMPACT
REGISTER_EXTENSION( ODE_EXT_gimpact )

// gimpact extensions
#endif

#endif // dTRIMESH_ENABLED

#if dTLS_ENABLED
REGISTER_EXTENSION( ODE_EXT_mt_collisions )
#endif // dTLS_ENABLED

#if !dTHREADING_INTF_DISABLED
REGISTER_EXTENSION( ODE_EXT_threading )

#if dBUILTIN_THREADING_IMPL_ENABLED
REGISTER_EXTENSION( ODE_THR_builtin_impl )
#endif // #if dBUILTIN_THREADING_IMPL_ENABLED
#endif // #if !dTHREADING_INTF_DISABLED

//**********************************
// EXTENSION LIST END

// These tokens are mutually exclusive, and always present
#ifdef dSINGLE
"ODE_single_precision"
#else
"ODE_double_precision"
#endif // dDOUBLE

; // END

const char* dGetConfiguration (void)
{
    return ode_configuration;
}


// Helper to check for a feature of ODE
int dCheckConfiguration( const char* extension )
{
    const char *start;
    char *where, *terminator;

    /* Feature names should not have spaces. */
    where = (char*)strchr(extension, ' ');
    if ( where || *extension == '\0')
        return 1;

    const char* config = dGetConfiguration();

    const sizeint ext_length = strlen(extension);

    /* It takes a bit of care to be fool-proof. Don't be fooled by sub-strings, etc. */
    start = config;
    for (  ; ;  )
    {
        where = (char*)strstr((const char *) start, extension);
        if (!where)
            break;

        terminator = where + ext_length;

        if ( (where == start || *(where - 1) == ' ') && 
            (*terminator == ' ' || *terminator == '\0') )
        {
            return 1;
        }

        start = terminator;
    }

    return 0;
}


// Local Variables:
// c-basic-offset:4
// End:
