#include <string>
#include "../headers/oglheaders.h"
#include "../headers/main.h"
#include "../headers/PhysXWrapper.h"
#include "../headers/FBXWrapper.h"
#include "../headers/FBXConverter.h"
#include "../headers/MeshManager.h"
#define DEBUG

bool FBXWrapper :: init(void) {
	bool ret = true;
	fbxman = KFbxSdkManager::CreateKFbxSdkManager();
	if (!fbxman) {
		fprintf(stderr, "Unable to create the FBX SDK manager\n");
		ret = false;
	}
	else {
		FBXConverter::geomConverter = KFbxGeometryConverter::Create(fbxman, "");
		importOptions = KFbxStreamOptionsFbxReader::Create(fbxman, "");
		// disable anything we can't handle
		/*importOptions->SetOption(KFBXSTREAMOPT_FBX_LINK, false);
		importOptions->SetOption(KFBXSTREAMOPT_FBX_SHAPE, false);
		importOptions->SetOption(KFBXSTREAMOPT_FBX_GOBO, false);
		importOptions->SetOption(KFBXSTREAMOPT_FBX_ANIMATION, false);
		importOptions->SetOption(KFBXSTREAMOPT_FBX_GLOBAL_SETTINGS, false);*/
	}
	return ret;
}

void FBXWrapper :: release(void) {
	if(importOptions)
		importOptions->Destroy();
	FBXConverter::geomConverter->Destroy();
	FBXConverter::geomConverter = NULL;
	fbxman->DestroyKFbxSdkManager();
}

ActorPrototype FBXWrapper :: importMesh(const char* file, bool staticActor) {
	KFbxScene *fbxscene = importScene(file);
	KFbxNode* root = fbxscene->GetRootNode();

	int cnum = root->GetChildCount();
	KFbxNode* cur = NULL;
	KFbxNodeAttribute* attr = NULL;
	ActorPrototype ret;
	ret.actor = NULL;
	ret.actorDesc = NULL;

#ifdef DEBUG
	printf("=====================\n");
	printf("Importing FBX Mesh...\n");
#endif
	for(int i=0; i<cnum; i++) {
		cur = root->GetChild(i);
		attr = cur->GetNodeAttribute();
		if ((attr != NULL) && (attr->GetAttributeType() == KFbxNodeAttribute::eMESH)) {
#ifdef DEBUG
			printf("Mesh found: %s\n", cur->GetName());
#endif
			FBXConverter::geomConverter->TriangulateInPlace(cur);
			ret = FBXConverter::mesh(cur, cur->GetGlobalFromDefaultTake(), staticActor);
			break;
		}
	}
	
#ifdef DEBUG
	printf("=====================\n");
#endif
	fbxscene->Destroy();
	return ret;
}

GeBranch* FBXWrapper :: importLevel(char* file) {
	KFbxScene *fbxscene = importScene(file);
	KFbxNode* root = fbxscene->GetRootNode();
	KFbxNode* cur = NULL;
	KFbxNodeAttribute* attr = NULL;
	KFbxXMatrix currentMatrix;
	std::list<float> toLoadAlpha;
	std::list<string> toLoadId;
	std::list<NxMat34> toLoadPose;
	GeActor* mesh;
	string name;
	GeBranch* ret = new GeBranch();
	int cnum = root->GetChildCount();

#ifdef DEBUG
	printf("======================\n");
	printf("Importing FBX Level...\n");
#endif
	for(int i=0; i<cnum; i++) {
		cur = root->GetChild(i);
		name = cur->GetName();
		attr = cur->GetNodeAttribute();
		if (attr != NULL) {
#ifdef DEBUG
			printf("\tImporting FBX Node: %s\n\t", name.data());
			switch(attr->GetAttributeType()) {
				case KFbxNodeAttribute::eUNIDENTIFIED: printf("Type: eUNIDENTIFIED"); break;
				case KFbxNodeAttribute::eNULL: printf("Type: eNULL"); break;
				case KFbxNodeAttribute::eMARKER: printf("Type: eMARKER"); break;
				case KFbxNodeAttribute::eMESH: printf("Type: eMESH"); break;
				case KFbxNodeAttribute::eLIGHT: printf("Type: eLIGHT"); break;
				case KFbxNodeAttribute::eOPTICAL_REFERENCE: printf("Type: eOPTICAL_REFERENCE"); break;
				case KFbxNodeAttribute::eOPTICAL_MARKER: printf("Type: eOPTICAL_MARKER"); break;
				case KFbxNodeAttribute::eCONSTRAINT: printf("Type: eCONSTRAINT"); break;
				case KFbxNodeAttribute::eBOUNDARY: printf("Type: eBOUNDARY"); break;
				default: printf("Type: unknown"); break;
			}
			
			printf("\n");
#endif

			if (name[0] == 'M') {// Marker
				KFbxXMatrix mat = cur->GetGlobalFromDefaultTake();
				KFbxVector4 t = mat.GetT();
				KFbxQuaternion q = mat.GetQ();
				NxQuat nxq;
				nxq.x = (NxReal)q.mData[0];
				nxq.y = (NxReal)q.mData[1];
				nxq.z = (NxReal)q.mData[2];
				nxq.w = (NxReal)q.mData[3];
				NxMat34 nxmat(NxMat33(nxq), NxVec3((NxReal)t[0], (NxReal)t[1], (NxReal)t[2]));

				if (name[1] == 'L') { // Load Model
					toLoadPose.push_back(nxmat);
					toLoadAlpha.push_back(FBXConverter::getMaterial((KFbxMesh*)(cur->GetNodeAttribute())).diffuse.a);
					toLoadId.push_back(name.substr(2));
				}
				else if (name[1] == 'S') { // Starting Point
					character->actor->setGlobalPosition(NxVec3((NxReal)t[0],(NxReal)t[1],(NxReal)t[2]));
				}
			}
			else if (attr->GetAttributeType() == KFbxNodeAttribute::eMESH) {
				KFbxXMatrix mat = cur->GetGlobalFromDefaultTake();
				KFbxVector4 t = mat.GetT();
				KFbxQuaternion q = mat.GetQ();
				NxQuat nxq;
				nxq.x = (NxReal)q.mData[0];
				nxq.y = (NxReal)q.mData[1];
				nxq.z = (NxReal)q.mData[2];
				nxq.w = (NxReal)q.mData[3];
				NxMat34 nxmat(NxMat33(nxq), NxVec3((NxReal)t[0], (NxReal)t[1], (NxReal)t[2]));
				mat.SetR(KFbxVector4());
				mat.SetT(KFbxVector4());
				
				FBXConverter::geomConverter->TriangulateInPlace(cur);
				if (name[0] == 'T') {// Trigger
					Trigger* trig = FBXConverter::trigger(cur, mat);
					if (trig != NULL) {
						trig->actor->setGlobalPose(nxmat);
						triggerList.push_back(trig);
					}
				}
				else {
					mesh = FBXConverter::mesh(cur, mat, (name[0] != 'I')).actor;
					if (mesh != NULL) {
						mesh->actor->setGlobalPose(nxmat);
						mesh->id = name.substr(name.length()-2);
						ret->push_back(mesh);
					}
				}
				/*
				printf("%.3f, %.3f, %.3f, %.3f\n", mat.mData[0][0], mat.mData[1][0], mat.mData[2][0], mat.mData[3][0]);
				printf("%.3f, %.3f, %.3f, %.3f\n", mat.mData[0][1], mat.mData[1][1], mat.mData[2][1], mat.mData[3][1]);
				printf("%.3f, %.3f, %.3f, %.3f\n", mat.mData[0][2], mat.mData[1][2], mat.mData[2][2], mat.mData[3][2]);
				printf("%.3f, %.3f, %.3f, %.3f\n\n", mat.mData[0][3], mat.mData[1][3], mat.mData[2][3], mat.mData[3][3]);
				printf("%.3f, %.3f, %.3f, %.3f\n", nxmat.M.getColumn(0).x, nxmat.M.getColumn(1).x, nxmat.M.getColumn(2).x, nxmat.t.x);
				printf("%.3f, %.3f, %.3f, %.3f\n", nxmat.M.getColumn(0).y, nxmat.M.getColumn(1).y, nxmat.M.getColumn(2).y, nxmat.t.y);
				printf("%.3f, %.3f, %.3f, %.3f\n", nxmat.M.getColumn(0).z, nxmat.M.getColumn(1).z, nxmat.M.getColumn(2).z, nxmat.t.z);
				printf("%.3f, %.3f, %.3f, %.3f\n", 0.0, 0.0, 0.0, 0.0);*/
			}
			else if (attr->GetAttributeType() == KFbxNodeAttribute::eLIGHT) {
				KFbxXMatrix mat = cur->GetGlobalFromDefaultTake();
				GeLight* l = FBXConverter::light(cur, mat);
				if (l != NULL) lights.push_back(l);
			}
		}
	}
	fbxscene->Destroy();

	std::list<string>::iterator toLoadIdIterator = toLoadId.begin();
	std::list<NxMat34>::iterator toLoadPoseIterator = toLoadPose.begin();
	std::list<float>::iterator toLoadAlphaIterator = toLoadAlpha.begin();

	for(; toLoadIdIterator != toLoadId.end(); toLoadIdIterator++, toLoadPoseIterator++, toLoadAlphaIterator++) {
		string name = (*toLoadIdIterator);
		if (name[0] == 'B') {
			mesh = meshMan->getBlendableActor(name.substr(3, name.length()-5), name[1] == 'S');
			mesh->material.diffuse.a = *toLoadAlphaIterator;
		}
		else { // name[ß] == 'N'
			mesh = meshMan->getActor(name.substr(3, name.length()-5), name[1] == 'S');
		}
		if (mesh != NULL) {
			mesh->actor->setGlobalPose(*toLoadPoseIterator);
			if((name[2] >= 'A') && (name[2] <= 'Z')) {
				mesh->actor->setMass(((name[2] - 'A')+0.01f)* 100.0f / 26.0f);
			}
			else {
				mesh->actor->raiseActorFlag(NX_AF_DISABLE_RESPONSE);
				mesh->actor->raiseBodyFlag(NX_BF_DISABLE_GRAVITY);
			}
			mesh->id = name.substr(name.length()-2);
			ret->push_back(mesh);
		}
	}
	
#ifdef DEBUG
	printf("======================\n");
#endif
	return ret;
}


KFbxScene* FBXWrapper :: importScene(const char* pFilename) {
	int lFileMajor, lFileMinor, lFileRevision;
	int lSDKMajor,  lSDKMinor,  lSDKRevision;
	int lFileFormat = -1;
	KString lCurrentTakeName;
	bool lStatus;
	KFbxScene* pScene = KFbxScene::Create(fbxman, "");

	// Get the file version number generate by the FBX SDK.
	KFbxIO::GetCurrentVersion(lSDKMajor, lSDKMinor, lSDKRevision);

	// Create an importer.
	KFbxImporter* importer = KFbxImporter::Create(fbxman, "");
	
	if (!KFbxIOPluginRegistryAccessor::Get()->DetectFileFormat(pFilename, lFileFormat))
	{
		// Unrecognizable file format. Try to fall back to native format.
		lFileFormat = KFbxIOPluginRegistryAccessor::Get()->GetNativeReaderFormat();
	}
	importer->SetFileFormat(lFileFormat);

    // Initialize the importer by providing a filename.
	const bool status = importer->Initialize(pFilename);
	importer->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);

	if( !status )
	{
		printf("Call to KFbxImporter::Initialize() failed.\n");
		printf("Error returned: %s\n\n", importer->GetLastErrorString());

		if (importer->GetLastErrorID() == KFbxIO::eFILE_VERSION_NOT_SUPPORTED_YET ||
			importer->GetLastErrorID() == KFbxIO::eFILE_VERSION_NOT_SUPPORTED_ANYMORE)
		{
			printf("FBX version number for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision);
			printf("FBX version number for file %s is %d.%d.%d\n\n", pFilename, lFileMajor, lFileMinor, lFileRevision);
		}
	}

	// Import the scene.
	lStatus = importer->Import(*pScene, importOptions);

	// Destroy the importer.
	importer->Destroy();

	return pScene;
}