#include "PhysicsCore.h"
#include "NxCooking.h"
#include "stdio.h"

class UserOutputStream : public NxUserOutputStream
{
    void reportError (NxErrorCode code, const char *message, const char* file, int line)
    {
        if (code < NXE_DB_INFO)
        {
            printf("SDK Error: %s\n", message);
        }
    }
            
    NxAssertResponse reportAssertViolation (const char *message, const char *file,int line)
    {
        assert(0);
        return NX_AR_CONTINUE;
    }
            
    void print (const char *message)
    {
        printf("SDK says: %s\n", message);
    }

};

PhysicsScene::PhysicsScene(PhysicsCore* Core,const NxSceneDesc& Desc)
{
	m_PhysicsCore = Core;
	m_Scene = m_PhysicsCore->GetSDK()->createScene(Desc);
}

PhysicsScene::~PhysicsScene(void)
{
//	GetSimulationResults();  //Make sure to fetchResults() before shutting down
    m_PhysicsCore->GetSDK()->releaseScene(*m_Scene);
}

void PhysicsScene::StartSimulation(const float dTime)
{
    m_Scene->simulate(dTime);
    m_Scene->flushStream();
}

void PhysicsScene::GetSimulationResults(void)
{
	while(!m_Scene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}

void PhysicsScene::Release(void)
{
	m_PhysicsCore->Release(this);
}

void PhysicsScene::DrawDebug(const Camera& cam)
{
	const NxDebugRenderable *dbgRenderable = m_Scene->getDebugRenderable();

	PhysicsRenderer* renderer = m_PhysicsCore->GetDebugRenderer();
	if(renderer)
		renderer->RenderData(dbgRenderable, cam);
}

PhysicsCore::PhysicsCore(DeviceStateManager* const device)
{
	m_Allocator = NULL;//new UserAllocator();
	m_OutputStream = new UserOutputStream();
	m_PhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, m_Allocator, m_OutputStream);
	
	m_PhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01f);
	m_PhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1.f);
	//m_PhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_AXES, 1.0f);
	//m_PhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_AABBS, 1);
	m_PhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1.f);
	//m_PhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SPHERES, 1);
	//m_PhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_EDGES, 1.0f);
	//m_PhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS, 1.0f);
	//m_PhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_VNORMALS, 1.0f);
//	m_PhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_NORMAL, 1.0f);
//	m_PhysicsSDK->setParameter(NX_VISUALIZE_CONTACT_POINT, 1.0f);
	//m_PhysicsSDK->setParameter(NX_VISUALIZE_BODY_MASS_AXES, 1.0f);

	if(device)
		m_DebugRenderer = new PhysicsRenderer(device);
	else
		m_DebugRenderer = NULL;

	NxInitCooking();
}

PhysicsCore::~PhysicsCore(void)
{
	NxCloseCooking();

	m_Scenes.Clear();//calls destructors of scenes

	m_PhysicsSDK->release();

//	delete m_Allocator;
	delete m_OutputStream;

	SAFEDELETE(m_DebugRenderer);
}

void PhysicsCore::Release(PhysicsScene* const Scene)
{
	ListElement<PhysicsScene> *el = m_Scenes.GetElement((unsigned int)Scene);
	if(el)
		el->Delete();
}

PhysicsScene* PhysicsCore::CreateScene(const NxSceneDesc& SceneDesc)
{
	PhysicsScene* Scene = new PhysicsScene(this,SceneDesc);
	m_Scenes.AddTail(Scene,(unsigned int)Scene);
	return Scene;
}