/*
 * Copyright (c) 2009-2010 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "com_jme3_bullet_PhysicsSpace.h"
#include "jmePhysicsSpace.h"
#include "jmeBulletUtil.h"
#include "jmeUserPointer.h"
/**
 * Author: Normen Hansen
 */
#ifdef __cplusplus
extern "C" {
#endif

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    createPhysicsSpace
     * Signature: (FFFFFFI)J
     */
    JNIEXPORT jlong JNICALL Java_com_jme3_bullet_PhysicsSpace_createPhysicsSpace
    (JNIEnv * env, jobject object, jfloat minX, jfloat minY, jfloat minZ, jfloat maxX, jfloat maxY, jfloat maxZ, jint broadphase, jboolean threading) {
        jmeClasses::initJavaClasses(env);
        jmePhysicsSpace* space = new jmePhysicsSpace(env, object);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space has not been created.");
            return 0;
        }
        space->createPhysicsSpace(minX, minY, minZ, maxX, maxY, maxZ, broadphase, threading);
        return reinterpret_cast<jlong>(space);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    stepSimulation
     * Signature: (JFIF)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_stepSimulation
    (JNIEnv * env, jobject object, jlong spaceId, jfloat tpf, jint maxSteps, jfloat accuracy) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        space->stepSimulation(tpf, maxSteps, accuracy);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    addCollisionObject
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addCollisionObject
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (collisionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The collision object does not exist.");
            return;
        }
        jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
        userPointer -> space = space;

        space->getDynamicsWorld()->addCollisionObject(collisionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    removeCollisionObject
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeCollisionObject
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (collisionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The collision object does not exist.");
            return;
        }
        space->getDynamicsWorld()->removeCollisionObject(collisionObject);
        jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
        userPointer -> space = NULL;
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    addRigidBody
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addRigidBody
    (JNIEnv * env, jobject object, jlong spaceId, jlong rigidBodyId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btRigidBody* collisionObject = reinterpret_cast<btRigidBody*>(rigidBodyId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (collisionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The collision object does not exist.");
            return;
        }
        jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
        userPointer -> space = space;
        space->getDynamicsWorld()->addRigidBody(collisionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    removeRigidBody
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeRigidBody
    (JNIEnv * env, jobject object, jlong spaceId, jlong rigidBodyId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btRigidBody* collisionObject = reinterpret_cast<btRigidBody*>(rigidBodyId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (collisionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The collision object does not exist.");
            return;
        }
        jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
        userPointer -> space = NULL;
        space->getDynamicsWorld()->removeRigidBody(collisionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    addCharacterObject
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addCharacterObject
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (collisionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The collision object does not exist.");
            return;
        }
        jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
        userPointer -> space = space;
        space->getDynamicsWorld()->addCollisionObject(collisionObject, 
                btBroadphaseProxy::CharacterFilter, 
                btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter
        );
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    removeCharacterObject
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeCharacterObject
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btCollisionObject* collisionObject = reinterpret_cast<btCollisionObject*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (collisionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The collision object does not exist.");
            return;
        }
        jmeUserPointer *userPointer = (jmeUserPointer*)collisionObject->getUserPointer();
        userPointer -> space = NULL;
        space->getDynamicsWorld()->removeCollisionObject(collisionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    addAction
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addAction
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (actionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The action object does not exist.");
            return;
        }
        space->getDynamicsWorld()->addAction(actionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    removeAction
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeAction
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (actionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The action object does not exist.");
            return;
        }
        space->getDynamicsWorld()->removeAction(actionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    addVehicle
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addVehicle
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (actionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The vehicle object does not exist.");
            return;
        }
        space->getDynamicsWorld()->addAction(actionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    removeVehicle
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeVehicle
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btActionInterface* actionObject = reinterpret_cast<btActionInterface*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (actionObject == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The action object does not exist.");
            return;
        }
        space->getDynamicsWorld()->removeVehicle(actionObject);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    addConstraint
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_addConstraint
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btTypedConstraint* constraint = reinterpret_cast<btTypedConstraint*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (constraint == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The constraint object does not exist.");
            return;
        }
        space->getDynamicsWorld()->addConstraint(constraint);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    removeConstraint
     * Signature: (JJ)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_removeConstraint
    (JNIEnv * env, jobject object, jlong spaceId, jlong objectId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        btTypedConstraint* constraint = reinterpret_cast<btTypedConstraint*>(objectId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        if (constraint == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The constraint object does not exist.");
            return;
        }
        space->getDynamicsWorld()->removeConstraint(constraint);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    setGravity
     * Signature: (JLcom/jme3/math/Vector3f;)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_setGravity
    (JNIEnv * env, jobject object, jlong spaceId, jobject vector) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        if (space == NULL) {
            jclass newExc = env->FindClass("java/lang/NullPointerException");
            env->ThrowNew(newExc, "The physics space does not exist.");
            return;
        }
        btVector3 gravity = btVector3();
        jmeBulletUtil::convert(env, vector, &gravity);
        space->getDynamicsWorld()->setGravity(gravity);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    initNativePhysics
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_initNativePhysics
    (JNIEnv * env, jclass clazz) {
        jmeClasses::initJavaClasses(env);
    }

    /*
     * Class:     com_jme3_bullet_PhysicsSpace
     * Method:    finalizeNative
     * Signature: (J)V
     */
    JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_finalizeNative
    (JNIEnv * env, jobject object, jlong spaceId) {
        jmePhysicsSpace* space = reinterpret_cast<jmePhysicsSpace*>(spaceId);
        delete(space);
    }

#ifdef __cplusplus
}
#endif
