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

#include "../../eshared.hpp"
#include "ephysics.hpp"

#ifdef eDEBUG
#include <windows.h>
#include <stdio.h>
#endif


#if defined(HAVE_OP_MISC_PHYSICS) || defined(eEDITOR)
OP_DEFINE(ePhysicsOp, ePhysicsOp_ID, eIGenericOp, "Physics", Misc, Misc_CID, eColor(170, 170, 170), ' ', 0, 0, "")
    OP_INIT()
    {
        this->m_genData = &m_physics;
        eOP_PARAM_ADD_FLOAT("Calculation Frequency", 1.0f, 1000.0f, 100.0f);
        eOP_PARAM_ADD_INT("Collision Iterations", 0, 100, 5);
        eOP_PARAM_ADD_INT("Contact Iterations", 0, 100, 10);
        eOP_PARAM_ADD_INT("Penetration Iterations", 0, 100, 10);
        eOP_PARAM_ADD_FLOAT("Penetration Ressolve Fraction", 0.001f, 0.100f, 0.008f);
		eOP_PARAM_ADD_BOOL("Reuse Collision", eFALSE);
		eOP_PARAM_ADD_BOOL("Allow smaller Timesteps", eTRUE);
		eOP_PARAM_ADD_BOOL("Enable Freezing", eTRUE);
		eOP_PARAM_ADD_BOOL("Interpolate Collide Mesh", eFALSE);
    }

    OP_EXEC(eGraphicsApiDx9 *gfx, eF32 freq, eU32 collIter, eU32 conIter, eU32 penIter, eF32 penRes, eBool reuse, eBool allowSmaller, eBool freezing, eBool interp)
    {
        m_physics = ePhysics();
        m_physics.setProperties(freq, collIter, conIter, penIter, penRes, reuse, allowSmaller, freezing, interp);
    }

    ePhysics          m_physics;
OP_END(ePhysicsOp);
#endif

#if defined(HAVE_OP_MISC_PHYSICSMODEL) || defined(eEDITOR)
OP_DEFINE(ePhysicsModelOp, ePhysicsModelOp_ID, eIGenericOp, "PhysicsModel", Misc, Misc_CID, eColor(170, 170, 170), ' ', 1, 1, "Misc")
    OP_INIT()
    {
		eOP_PARAM_ADD_BOOL("Is Static", eFALSE);
        eOP_PARAM_ADD_LINK("Model", "Model");
        eOP_PARAM_ADD_FLOAT("Elasticity", 0.0f, 1.0f, 0.0f);
        eOP_PARAM_ADD_FLOAT("Static Friction", 0.0f, 1.0f, 1.0f);
        eOP_PARAM_ADD_FLOAT("Dynamic Friction", 0.0f, 1.0f, 1.0f);
	    eOP_PARAM_ADD_ENUM("Type", "BOX|ELLIPSOID", 0);
		eOP_PARAM_ADD_BOOL("Use Hierarchy", eFALSE);
    }

    typedef struct ENTRY {
        void* handle;
        eSceneData* sd;
    } ENTRY;

    eU32 createPhysNodes(eU32 parent, ePhysics& physics, eSceneData* current, eArray<ENTRY>& handles, const eMatrix4x4& mtx, eF32 elasticity, eF32 static_friction, eF32 dynamic_friction, eU32 type, eBool immovable) {
        ENTRY entry;
        eU32 entryIdx = handles.size();

        entry.sd = new eSceneData();
        entry.sd->transform(mtx);
        // gather objects
        for(eU32 i = 0; i < current->getEntryCount(); i++) {
            eSceneData::Entry& e = (eSceneData::Entry&)current->getEntry(i);
            eASSERT(e.renderableObject != eNULL);
            if(e.renderableList->isStatic() && (e.renderableCount != 0)) {
                // a static object
                entry.sd->merge(*e.renderableList, e.matrix);
            }
        }
        
        eU32 childParent = entryIdx;
        if(entry.sd->getRenderableTotal() == eNULL) {
            delete entry.sd;
            childParent = parent;
        } else {
            eAABB bbox = entry.sd->getBoundingBox();
            eVector3 center = bbox.getCenter();
            eVector3 size = bbox.getSize() * 0.5f * 1.00f;
            eAABB bbox2;
            bbox2.updateExtent(center - size);
            bbox2.updateExtent(center + size);
            entry.handle = physics.addCollide(entry.sd, (ePhysics::TYPE)type, elasticity, static_friction, dynamic_friction, bbox2.getCenter(), bbox2.getSize(), eMatrix4x4(), immovable, 1.0f);
            handles.append(entry);
            if(parent != -1) {
                eVector3 midPos = 0.5f * (bbox2.getCenter() - 
                                          handles[parent].sd->getBoundingBox().getCenter());
                midPos = eVector3(-0.05f, 0.0f, 0.25f);
//                physics.createJoint(handles[parent].handle, handles[entryIdx].handle,eVector3(1,0,1), midPos, 0.8f, 0.3f, 50.0f, 20.0f, 25.0f); 
//                physics.disableCollisions(handles[parent].handle, handles[entryIdx].handle);
            }
        }

        // create sub nodes
        for(eU32 i = 0; i < current->getEntryCount(); i++) {
            eSceneData::Entry& e = (eSceneData::Entry&)current->getEntry(i);
            eASSERT(e.renderableObject != eNULL);
            if(!e.renderableList->isStatic()) 
                createPhysNodes(childParent, physics, e.renderableList, handles, mtx * e.matrix, elasticity, static_friction, dynamic_friction, type, immovable);
        }

        return entryIdx;
    }

    OP_EXEC(eGraphicsApiDx9 *gfx, eBool immovable, const eIModelOp *modelOp, eF32 elasticity, eF32 static_friction, eF32 dynamic_friction, eU32 type, eBool useHierarchy)
    {
        this->m_genData = ((eIGenericOp *)getInputOperator(0))->getResult().genericDataPtr;
        ePhysics& physics = *((ePhysics*)m_genData);

        eSceneData* model = (modelOp == eNULL) ? eNULL : &modelOp->getResult().sceneData;
        for(eU32 i = 0; i < this->m_entries.size(); i++) {
            physics.remove(m_entries[i].handle);
            if(m_entries[i].sd != eNULL)
                delete m_entries[i].sd;
        }
        m_entries.clear();

        if(model != eNULL) {
            if(useHierarchy && (!model->isStatic())) {
                createPhysNodes(-1, physics, model, m_entries, eMatrix4x4(), elasticity, static_friction, dynamic_friction, (ePhysics::TYPE)type, immovable);
            } else {
                ENTRY e;
                e.handle = physics.addCollide(model, (ePhysics::TYPE)type, elasticity, static_friction, dynamic_friction, model->getBoundingBox().getCenter(), model->getBoundingBox().getSize(), eMatrix4x4(), immovable, 1.0f);
                e.sd = eNULL;
                m_entries.append(e);
            }
        }
    }

    eArray<ENTRY> m_entries;
OP_END(ePhysicsModelOp);
#endif

#if defined(HAVE_OP_MODEL_PHYSICSRENDERER) || defined(eEDITOR)
OP_DEFINE_MODEL(eModelPhysicsOp, eModelPhysicsOp_ID, "PhysicsRenderer", 'p', 1, 1, "Misc")
    OP_INIT()
    {
        eOP_PARAM_ADD_FLOAT("Time", -eF32_MAX, eF32_MAX, 0.0f);
        eOP_PARAM_ADD_LINK("VisModel", "Model");
        eOP_PARAM_ADD_FXYZ("Gravity", -eF32_MAX, eF32_MAX, 0, -9.81f, 0);
    }

    OP_EXEC(eGraphicsApiDx9 *gfx, eF32 time, const eIModelOp *visModelOp, const eVector3& gravity)
    {
        ePhysics& physics = *((ePhysics*)((eIGenericOp *)getInputOperator(0))->getResult().genericDataPtr);
        eSceneData* visModel = (visModelOp == eNULL) ? eNULL : &visModelOp->getResult().sceneData;

        physics.setGravity(gravity);
        {
    	    ePROFILER_ZONE("Physics");
            physics.update(time);
        }

		// visualize
		if(visModelOp) {
			eSceneData &visSd = visModelOp->getResult().sceneData;
            for(eU32 i = 0; i < physics.m_components.size(); i++) {
                Object* obj = physics.m_components[i];
                eSceneData* model = (eSceneData*)obj->getObjectData();
                eVector3 opos = ePhysics::convert(obj->get_position());
                eMatrix4x4 rotation = ePhysics::convert(obj->get_orientation());
                eMatrix4x4 mtx;
//                mtx.translate(-model->getBoundingBox().getCenter());
                mtx = rotation.inverse() * mtx;
                mtx.translate(opos);
                m_sceneData.merge(*model, mtx);
            }
		}

    }
OP_END(eModelPhysicsOp);
#endif

