/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *   This file is part of
 *       _______   ______________  ______     _____
 *      / ____/ | / /  _/ ____/  |/  /   |   |__  /
 *     / __/ /  |/ // // / __/ /|_/ / /| |    /_ <
 *    / /___/ /|  // // /_/ / /  / / ___ |  ___/ /
 *   /_____/_/ |_/___/\____/_/  /_/_/  |_| /____/.
 *
 *   Copyright © 2003-2010 Brain Control, all rights reserved.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef EPHYSICS_HPP
#define EPHYSICS_HPP

#include "../../eshared.hpp"
#include "jiggle/physics.hpp"
#include "jiggle/ellipsoid.hpp"
#include "jiggle/box.hpp"
#include "jiggle/hinge_joint.hpp"

class Object;

class ePhysics {
public:

    enum TYPE {
        BOX,
        ELLIPSOID,
    }; 

    ePhysics() {
        m_lastTime = 0.0f;
    };
	void setProperties(eF32 physics_freq, eU32 physics_num_collision_iterations, eU32 physics_num_contact_iterations, eU32 physics_num_penetration_iterations, 
             eF32 physics_penetration_resolve_fraction, eBool physics_reuse_collisions, eBool physics_allow_smaller_timesteps, eBool physics_enable_freezing, eBool interp_coll_mesh) {
            Scalar physics_time_scale = 1.0f;

            Collision_body_mesh::set_interpolate_mesh(interp_coll_mesh);
            m_physics.set_timestep(1.0f / physics_freq);
            m_physics.set_time_scale(physics_time_scale);
            m_physics.set_num_collision_iterations(physics_num_collision_iterations);
            m_physics.set_num_contact_iterations(physics_num_contact_iterations);
            m_physics.set_num_penetration_iterations(physics_num_penetration_iterations);
            m_physics.set_reuse_collisions(physics_reuse_collisions);
            m_physics.set_penetration_resolve_fraction(physics_penetration_resolve_fraction);
            m_physics.set_allow_smaller_timesteps(physics_allow_smaller_timesteps);
             m_physics.enable_freezing(physics_enable_freezing);
	}

    void disableCollisions(void* handle0, void* handle1) {
          Rigid_body * rb0 = (Rigid_body*)handle0;
          Rigid_body * rb1 = (Rigid_body*)handle1;
          if (!rb0->collision_body() || !rb1->collision_body())
            return;
          rb0->collision_body()->non_collidables().push_back(rb1->collision_body());
          rb1->collision_body()->non_collidables().push_back(rb0->collision_body());
    }

    static eVector3 convert(Position pos) {
        return eVector3(pos(0), pos(1), pos(2));
    }

    static Position convert(eVector3 pos) {
        return Position(pos.x, pos.y, pos.z);
    }

    static eMatrix4x4 convert(const Matrix3& rotation) {

        return eMatrix4x4(rotation(0,0), rotation(0,1), rotation(0,2), 0,
                        rotation(1,0), rotation(1,1), rotation(1,2), 0,
                        rotation(2,0), rotation(2,1), rotation(2,2), 0,
                        0,0,0,1);
    }

    static Matrix3 convert(const eMatrix4x4& rotation) {
        Matrix3 m;
        m(0,0) = rotation.m11;
        m(0,1) = rotation.m12;
        m(0,2) = rotation.m13;
        m(1,0) = rotation.m21;
        m(1,1) = rotation.m22;
        m(1,2) = rotation.m23;
        m(2,0) = rotation.m31;
        m(2,1) = rotation.m32;
        m(2,2) = rotation.m33;
        return m;
    }


    void update(eF32 time) {
          // kick the physics
        eF32 deltaTime = eMax(0.0f, time - this->m_lastTime);
        this->m_lastTime = time;
        m_physics.integrate(deltaTime);
    }

    void* createJoint(void* handle0, void* handle1, const eVector3& axis, const eVector3& absPosHinge, const eMatrix4x4& invMtx0, const eMatrix4x4& invMtx1, eF32 halfWidth, eF32 sideSlack, eF32 fwdAngle, eF32 bckAngle, eF32 damping) {
        Rigid_body * rb0 = (Rigid_body*)handle0;
        Rigid_body * rb1 = (Rigid_body*)handle1;
        Hinge_joint* h = new Hinge_joint();
        h->initialise(&m_physics, rb0, rb1, convert(axis), convert(absPosHinge), convert(invMtx0), convert(invMtx1), halfWidth, sideSlack, fwdAngle, bckAngle, damping);
        m_joints.append(h);
        return h;
    }

    void* addCollide(void* data, TYPE type, eF32 elasticity, eF32 static_friction, eF32 dynamic_friction, const eVector3& position, const eVector3& size, const eMatrix4x4& rotation, eBool isStatic, eF32 mass) {
        Object* obj = eNULL;
        switch(type) {
        case BOX:
            obj = new Box(&m_physics, size.x,  size.y, size.z, mass, true);
            break;
        case ELLIPSOID:
            int slices = 10;
            int stacks = 8;
            obj = new Ellipsoid(&m_physics, size.x,  size.y, size.z, slices, stacks, mass);
            break;
        };
        obj->set_position(convert(position));
        obj->elasticity() = elasticity;
        obj->static_friction() = static_friction;
        obj->dynamic_friction() = dynamic_friction;
        obj->set_orientation(convert(rotation));
//        obj->set_activity_threshold(0.7f, 30.0f);
//        obj->set_activation_factor(3);
        if(isStatic) {
            obj->set_immovable(true);
//            obj->set_velocity(Velocity(-5.0f, 0.0f, 0.0f));
        }
        obj->setObjectData(data);
        m_physics.add_body(obj);
        m_components.append(obj);
        return obj;
    }

    void remove(void* handle) {
        Object* obj = (Object*)handle;
        m_physics.remove_body(obj);
        eInt p = m_components.exists(obj);
        if(p != -1) {
            m_components.removeAt(p);
            delete obj;
        }

        for(eU32 i = 0; i < this->m_joints.size(); i++) {
            Hinge_joint* h = this->m_joints[i];
            if((h->m_body0 == handle) || (h->m_body1 == handle)) {
                h->deinit(&m_physics);
                this->m_joints.removeAt(i);
                i--;
            }
        }
    }

    void reset() {
    }

    void setGravity(const eVector3& gravity) {
        Physics::set_gravity(Vector3(gravity.x, gravity.y, gravity.z));
    }

    eArray<Object*> m_components;
    eArray<Hinge_joint*>  m_joints;
private:
    Physics         m_physics;
    eF32            m_lastTime;
};

#endif // LSYSTEM_HPP
