/*************************************************************************
 *                                                                       *
 * 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.                     *
 *                                                                       *
 *************************************************************************/


#include <ode/odeconfig.h>
#include "contact.h"
#include "joint_internal.h"



 //****************************************************************************
 // contact

dxJointContact::dxJointContact(dxWorld *w) :
    dxJoint(w)
{
}


void
dxJointContact::getSureMaxInfo(SureMaxInfo* info)
{
    // ...as the actual m is very likely to hit the maximum
    info->max_m = (contact.surface.mode&dContactRolling) ? 6 : 3;
}


void
dxJointContact::getInfo1(dxJoint::Info1 *info)
{
    // make sure mu's >= 0, then calculate number of constraint rows and number
    // of unbounded rows.
    int m = 1, nub = 0;

    // Anisotropic sliding and rolling and spinning friction 
    if (contact.surface.mode & dContactAxisDep) {
        if (contact.surface.mu < 0) {
            contact.surface.mu = 0;
        }
        else if (contact.surface.mu > 0) {
            if (contact.surface.mu == dInfinity) { nub++; }
            m++;
        }

        if (contact.surface.mu2 < 0) {
            contact.surface.mu2 = 0;
        }
        else if (contact.surface.mu2 > 0) {
            if (contact.surface.mu2 == dInfinity) { nub++; }
            m++;
        }

        if ((contact.surface.mode & dContactRolling) != 0) {
            if (contact.surface.rho < 0) {
                contact.surface.rho = 0;
            }
            else {
                if (contact.surface.rho == dInfinity) { nub++; }
                m++;
            }

            if (contact.surface.rho2 < 0) {
                contact.surface.rho2 = 0;
            }
            else {
                if (contact.surface.rho2 == dInfinity) { nub++; }
                m++;
            }

            if (contact.surface.rhoN < 0) {
                contact.surface.rhoN = 0;
            }
            else {
                if (contact.surface.rhoN == dInfinity) { nub++; }
                m++;
            }
        }
    }
    else {
        if (contact.surface.mu < 0) {
            contact.surface.mu = 0;
        }
        else if (contact.surface.mu > 0) {
            if (contact.surface.mu == dInfinity) { nub += 2; }
            m += 2;
        }

        if ((contact.surface.mode & dContactRolling) != 0) {
            if (contact.surface.rho < 0) {
                contact.surface.rho = 0;
            }
            else {
                if (contact.surface.rho == dInfinity) { nub += 3; }
                m += 3;
            }
        }
    }

    the_m = m;
    info->m = m;
    info->nub = nub;
}


void
dxJointContact::getInfo2(dReal worldFPS, dReal worldERP,
    int rowskip, dReal *J1, dReal *J2,
    int pairskip, dReal *pairRhsCfm, dReal *pairLoHi,
    int *findex)
{
    enum 
    {
        ROW_NORMAL,

        ROW__OPTIONAL_MIN,
    };

    const int surface_mode = contact.surface.mode;

    // set right hand side and cfm value for normal
    dReal erp = (surface_mode & dContactSoftERP) != 0 ? contact.surface.soft_erp : worldERP;
    dReal k = worldFPS * erp;

    dReal depth = contact.geom.depth - world->contactp.min_depth;
    if (depth < 0) depth = 0;

    dReal motionN = (surface_mode & dContactMotionN) != 0 ? contact.surface.motionN : REAL(0.0);
    const dReal pushout = k * depth + motionN;

    bool apply_bounce = (surface_mode & dContactBounce) != 0 && contact.surface.bounce_vel >= 0;
    dReal outgoing = 0;

    // note: this cap should not limit bounce velocity
    const dReal maxvel = world->contactp.max_vel;
    dReal c = pushout > maxvel ? maxvel : pushout;

    // c1,c2 = contact points with respect to body PORs
    dVector3 c1, c2 = { 0, };

    // get normal, with sign adjusted for body1/body2 polarity
    dVector3 normal;
    if ((flags & dJOINT_REVERSE) != 0) {
        dCopyNegatedVector3(normal, contact.geom.normal);
    }
    else {
        dCopyVector3(normal, contact.geom.normal);
    }

    dxBody *b1 = node[1].body;
    if (b1) {
        dSubtractVectors3(c2, contact.geom.pos, b1->posr.pos);
        // set Jacobian for b1 normal
        dCopyNegatedVector3(J2 + ROW_NORMAL * rowskip + GI2__JL_MIN, normal);
        dCalcVectorCross3(J2 + ROW_NORMAL * rowskip + GI2__JA_MIN, normal, c2); //== dCalcVectorCross3( J2 + GI2__JA_MIN, c2, normal ); dNegateVector3( J2 + GI2__JA_MIN );
        if (apply_bounce) {
            outgoing /*+*/= dCalcVectorDot3(J2 + ROW_NORMAL * rowskip + GI2__JA_MIN, node[1].body->avel)
                - dCalcVectorDot3(normal, node[1].body->lvel);
        }
    }

    dxBody *b0 = node[0].body;
    dSubtractVectors3(c1, contact.geom.pos, b0->posr.pos);
    // set Jacobian for b0 normal
    dCopyVector3(J1 + ROW_NORMAL * rowskip + GI2__JL_MIN, normal);
    dCalcVectorCross3(J1 + ROW_NORMAL * rowskip + GI2__JA_MIN, c1, normal);
    if (apply_bounce) {
        // calculate outgoing velocity (-ve for incoming contact)
        outgoing += dCalcVectorDot3(J1 + ROW_NORMAL * rowskip + GI2__JA_MIN, node[0].body->avel)
            + dCalcVectorDot3(normal, node[0].body->lvel);
    }

    // deal with bounce
    if (apply_bounce) {
        dReal negated_outgoing = motionN - outgoing;
        // only apply bounce if the outgoing velocity is greater than the
        // threshold, and if the resulting c[rowNormal] exceeds what we already have.
        dIASSERT(contact.surface.bounce_vel >= 0);
        if (/*contact.surface.bounce_vel >= 0 &&*/
            negated_outgoing > contact.surface.bounce_vel) {
            const dReal newc = contact.surface.bounce * negated_outgoing + motionN;
            if (newc > c) { c = newc; }
        }
    }

    pairRhsCfm[ROW_NORMAL * pairskip + GI2_RHS] = c;

    if ((surface_mode & dContactSoftCFM) != 0) {
        pairRhsCfm[ROW_NORMAL * pairskip + GI2_CFM] = contact.surface.soft_cfm;
    }

    // set LCP limits for normal
    pairLoHi[ROW_NORMAL * pairskip + GI2_LO] = 0;
    pairLoHi[ROW_NORMAL * pairskip + GI2_HI] = dInfinity;


    if (the_m > 1) { // if no friction, there is nothing else to do
        // now do jacobian for tangential forces
        dVector3 t1, t2; // two vectors tangential to normal

        if ((surface_mode & dContactFDir1) != 0) {   // use fdir1 ?
            dCopyVector3(t1, contact.fdir1);
            dCalcVectorCross3(t2, normal, t1);
        }
        else {
            dPlaneSpace(normal, t1, t2);
        }

        int row = ROW__OPTIONAL_MIN;
        int currRowSkip = row * rowskip, currPairSkip = row * pairskip;

        // first friction direction
        const dReal mu = contact.surface.mu;

        if (mu > 0) {
            dCopyVector3(J1 + currRowSkip + GI2__JL_MIN, t1);
            dCalcVectorCross3(J1 + currRowSkip + GI2__JA_MIN, c1, t1);

            if (node[1].body) {
                dCopyNegatedVector3(J2 + currRowSkip + GI2__JL_MIN, t1);
                dCalcVectorCross3(J2 + currRowSkip + GI2__JA_MIN, t1, c2); //== dCalcVectorCross3( J2 + rowskip + GI2__JA_MIN, c2, t1 ); dNegateVector3( J2 + rowskip + GI2__JA_MIN );
            }

            // set right hand side
            if ((surface_mode & dContactMotion1) != 0) {
                pairRhsCfm[currPairSkip + GI2_RHS] = contact.surface.motion1;
            }
            // set slip (constraint force mixing)
            if ((surface_mode & dContactSlip1) != 0) {
                pairRhsCfm[currPairSkip + GI2_CFM] = contact.surface.slip1;
            }

            // set LCP bounds and friction index. this depends on the approximation
            // mode
            pairLoHi[currPairSkip + GI2_LO] = -mu;
            pairLoHi[currPairSkip + GI2_HI] = mu;

            if ((surface_mode & dContactApprox1_1) != 0) {
                findex[row] = 0;
            }

            ++row;
            currRowSkip += rowskip; currPairSkip += pairskip;
        }

        // second friction direction
        const dReal mu2 = (surface_mode & dContactMu2) != 0 ? contact.surface.mu2 : mu;

        if (mu2 > 0) {
            dCopyVector3(J1 + currRowSkip + GI2__JL_MIN, t2);
            dCalcVectorCross3(J1 + currRowSkip + GI2__JA_MIN, c1, t2);

            if (node[1].body) {
                dCopyNegatedVector3(J2 + currRowSkip + GI2__JL_MIN, t2);
                dCalcVectorCross3(J2 + currRowSkip + GI2__JA_MIN, t2, c2); //== dCalcVectorCross3( J2 + currRowSkip + GI2__JA_MIN, c2, t2 ); dNegateVector3( J2 + currRowSkip + GI2__JA_MIN );
            }

            // set right hand side
            if ((surface_mode & dContactMotion2) != 0) {
                pairRhsCfm[currPairSkip + GI2_RHS] = contact.surface.motion2;
            }
            // set slip (constraint force mixing)
            if ((surface_mode & dContactSlip2) != 0) {
                pairRhsCfm[currPairSkip + GI2_CFM] = contact.surface.slip2;
            }

            // set LCP bounds and friction index. this depends on the approximation
            // mode
            pairLoHi[currPairSkip + GI2_LO] = -mu2;
            pairLoHi[currPairSkip + GI2_HI] = mu2;

            if ((surface_mode & dContactApprox1_2) != 0) {
                findex[row] = 0;
            }

            ++row;
            currRowSkip += rowskip; currPairSkip += pairskip;
        }

        // Handle rolling/spinning friction
        if ((surface_mode & dContactRolling) != 0) {

            const dReal *const ax[3] = {
                t1, // Rolling around t1 creates movement parallel to t2
                t2,
                normal // Spinning axis
            };

            const int approx_bits[3] = { dContactApprox1_1, dContactApprox1_2, dContactApprox1_N };

            // Get the coefficients
            dReal rho[3];
            rho[0] = contact.surface.rho;
            if ((surface_mode & dContactAxisDep) != 0) {
                rho[1] = contact.surface.rho2;
                rho[2] = contact.surface.rhoN;
            }
            else {
                rho[1] = rho[0];
                rho[2] = rho[0];
            }

            for (int i = 0; i != 3; ++i) {
                if (rho[i] > 0) {
                    // Set the angular axis
                    dCopyVector3(J1 + currRowSkip + GI2__JA_MIN, ax[i]);

                    if (b1) {
                        dCopyNegatedVector3(J2 + currRowSkip + GI2__JA_MIN, ax[i]);
                    }

                    // Set the lcp limits
                    pairLoHi[currPairSkip + GI2_LO] = -rho[i];
                    pairLoHi[currPairSkip + GI2_HI] = rho[i];

                    // Should we use proportional force?
                    if ((surface_mode & approx_bits[i]) != 0) {
                        // Make limits proportional to normal force
                        findex[row] = 0;
                    }

                    ++row;
                    currRowSkip += rowskip; currPairSkip += pairskip;
                }
            }
        }
    }
}

dJointType
dxJointContact::type() const
{
    return dJointTypeContact;
}


sizeint
dxJointContact::size() const
{
    return sizeof(*this);
}

