// Copyright (C) 2004-2009 Christoph Kubisch & Eike Decker
// This file is part of the "Luxinia Engine".
// For conditions of distribution and use, see copyright notice in luxiniapublicsdk.txt

#include "exmodel_local.h"

//////////////////////////////////////////////////////////////////////////
// CLASS

EXAnim::~EXAnim()
{
	for (size_t i = 0; i < tracks.size(); i++){
		delete tracks[i];
	}
}

EXPRSInfo::EXPRSInfo()
{
	Matrix44Identity(matrix);
	Vector3Clear(pos);
	Vector3Set(scale,1.0f,1.0f,1.0f);
	QuatIdentity(rot);
}

EXPRSKey::EXPRSKey()
{
	time = 0.0f;
	usedFlags = 0;

	Vector3Clear(pos);
	Vector3Set(scale,1.0f,1.0f,1.0f);
	QuatIdentity(rot);
}


EXMesh::EXMesh(void* host, const char *name, uint id) : EXParam(host,name,id)
{
	parent		= NULL;
	instance	= NULL;
	material	= NULL;
	skin		= NULL;

	maxUVcount	= 0;

	meshtype		= EXMESH_UNSET; 
	primitivetype	= EXPRIM_UNSET;
}

EXModel::~EXModel()
{
	for (size_t i = 0; i < meshes.size(); i++){
		delete meshes[i];
	}
	for (size_t i = 0; i < skins.size(); i++){
		delete skins[i];
	}
	for (size_t i = 0; i < materials.size(); i++){
		delete materials[i];
	}
}

//////////////////////////////////////////////////////////////////////////
// EXAnimPTR

EXAnimPTR		EXAnim_new		(const char *name){
	return new EXAnim(name);
}
void			EXAnim_delete		(EXAnimPTR imodel){
	delete imodel; 
}

EXTrackPTR	EXAnim_provideTrack	(EXAnimPTR model, const char *name, uint id){
	return SearchAndInsert<EXTrack>(model->tracks,model,name,id);
}
EXTrackPTR	EXAnim_getTrack			(EXAnimPTR imodel, uint index){
	return GetAt<EXTrack>(imodel->tracks,index);
}
uint		EXAnim_getTrackCount	(EXAnimPTR imodel)
{
	return (uint)imodel->tracks.size();
}

// EXTrackPTR
void	EXTrack_setName			(EXTrack *imesh, const char* name){
	imesh->param.name = std::string(name);
}
const char*		EXTrack_getName		(EXTrackPTR imesh){
	return imesh->param.name.c_str();
}
uint			EXTrack_getID		(EXTrackPTR imesh){
	return imesh->param.id;
}
void			EXTrack_setLength	(EXTrackPTR imesh, float time){
	imesh->length = time;
}
float			EXTrack_getLength	(EXTrackPTR imesh){
	return imesh->length;
}
uint			EXTrack_getKeyCount	(EXTrackPTR imesh){
	return (uint)imesh->keys.size();
}
EXPRSKeyPTR		EXTrack_getKey		(EXTrackPTR imesh, uint index){
	return GetAt<EXPRSKey>(imesh->keys,index);
}

EXPRSKeyPTR		EXTrack_provideKey	(EXTrackPTR imesh, uint i){
	return ProvideAt<EXPRSKey>(imesh->keys,i);
}

EXPRSKeyPTR		EXTrack_addKey		(EXTrackPTR imesh){
	return AddTo<EXPRSKey>(imesh->keys);
}

// EXPRSKey
void	EXPRSKey_setTime(EXPRSKeyPTR imesh, float x){
	imesh->time=x;
}
float	EXPRSKey_getTime		(EXPRSKeyPTR imesh){
	return imesh->time;
}
void	EXPRSKey_setPosition(EXPRSKeyPTR imesh, float x, float y, float z){
	imesh->pos[0]=x;
	imesh->pos[1]=y;
	imesh->pos[2]=z;
}
float*	EXPRSKey_getPosition(EXPRSKeyPTR imesh){
	return imesh->pos;
}
void	EXPRSKey_setRotation(EXPRSKeyPTR imesh, float x, float y, float z, float w){
	imesh->rot[0]=x;
	imesh->rot[1]=y;
	imesh->rot[2]=z;
	imesh->rot[3]=w;
}
float*	EXPRSKey_getRotation(EXPRSKeyPTR imesh){
	return imesh->rot;
}
void	EXPRSKey_setScale(EXPRSKeyPTR imesh, float x, float y, float z){
	imesh->scale[0]=x;
	imesh->scale[1]=y;
	imesh->scale[2]=z;
}
float*	EXPRSKey_getScale(EXPRSKeyPTR imesh){
	return imesh->scale;
}
void	EXPRSKey_setUsedFlags(EXPRSKey *imesh, uint states){
	imesh->usedFlags = states;
}
uint	EXPRSKey_getUsedFlags(EXPRSKeyPTR imesh){
	return imesh->usedFlags;
}

// EXModelPTR

EXModelPTR		EXModel_new		(const char *name){
	return new EXModel(name);
}
void			EXModel_delete		(EXModelPTR imodel){
	delete imodel; 
}
EXMeshPTR	EXModel_getMesh			(EXModelPTR imodel, uint index){
	return GetAt<EXMesh>(imodel->meshes,index);
}
uint		EXModel_getMeshCount	(EXModelPTR imodel)
{
	return (uint)imodel->meshes.size();
}
uint		EXModel_getMeshTypeCount	(EXModelPTR imodel, EXMeshType type)
{
	uint cnt = 0;
	for (size_t m = 0; m < imodel->meshes.size(); m++){
		if (imodel->meshes[m]->meshtype == type)
			cnt++;
	}	

	return cnt;
}
EXMaterialPTR	EXModel_getMaterial		(EXModelPTR imodel, uint index){
	return GetAt<EXMaterial>(imodel->materials,index);
}
uint		EXModel_getMaterialCount	(EXModelPTR imodel)
{
	return (uint)imodel->materials.size();
}
EXSkinPTR	EXModel_getSkin	(EXModelPTR imodel, uint index){
	return GetAt<EXSkin>(imodel->skins,index);
}
uint		EXModel_getSkinCount	(EXModelPTR imodel)
{
	return (uint)imodel->skins.size();
}

EXMaterialPTR	EXModel_provideMaterial 	(EXModelPTR model, const char *name, uint id) {
	return SearchAndInsert<EXMaterial>(model->materials,model,name,id);
}

EXMeshPTR	EXModel_provideMesh 	(EXModelPTR model, const char *name, uint id) {
	return SearchAndInsert<EXMesh>(model->meshes,model,name,id);
}
EXSkinPTR	EXModel_provideSkin 	(EXModelPTR model, const char *name, uint id) {
	return SearchAndInsert<EXSkin>(model->skins,model,name,id);
}

EXMeshPTR		EXModel_provideMeshParent		(EXModelPTR model, EXMeshPTR imesh, const char *name, uint id){
	EXMeshPTR imesh2;
	imesh2 = EXModel_provideMesh(model,name,id);
	imesh->parent = imesh2;
	return imesh2;
}
EXMaterialPTR	EXModel_provideMeshMaterial	(EXModelPTR model, EXMeshPTR imesh, const char *name, uint id){
	EXMaterial *imat;
	imat = EXModel_provideMaterial(model,name,id);
	imesh->material = imat;
	return imat;
}
EXMeshPTR		EXModel_provideMeshInstance	(EXModelPTR model, EXMeshPTR imesh, const char *name, uint id){
	EXMeshPTR imesh2;
	imesh2 = EXModel_provideMesh(model,name,id);
	imesh->instance = imesh2;
	return imesh2;
}
EXMeshPTR		EXModel_provideSkinMesh		(EXModelPTR model, EXSkinPTR iskin, const char *name, uint id){
	EXMeshPTR imesh2;
	imesh2 = EXModel_provideMesh(model,name,id);
	iskin->mesh = imesh2;
	imesh2->skin = iskin;
	return imesh2;
}
EXMeshPTR		EXModel_provideSkinMeshParent	(EXModelPTR model, EXSkinPTR iskin, uint i, const char *name, uint id){
	EXMeshPTR mesh = EXModel_provideMesh(model,name,id);

	if (iskin->skinParents.size() <= i){
		iskin->skinParents.resize(i+1,NULL);
	}
	iskin->skinParents[i] = mesh;

	return mesh;
}
EXMaterialPTR	EXModel_provideMaterialSub	(EXModelPTR model, EXMaterialPTR imaterial, uint i, const char *name, uint id){
	EXMaterialPTR mesh = EXModel_provideMaterial(model,name,id);

	if (imaterial->submats.size() <= i){
		imaterial->submats.resize(i+1,NULL);
	}
	imaterial->submats[i] = mesh;

	return mesh;
}



// EXMeshPTR
void	EXMesh_setName(EXMesh *imesh, const char* name){
	imesh->param.name = std::string(name);
}
const char* EXMesh_getName(EXMeshPTR imesh){
	return imesh->param.name.c_str();
}
EXMeshPTR EXMesh_getParent(EXMeshPTR imesh){
	return imesh->parent;
}
uint	EXMesh_getID(EXMeshPTR imesh){
	return imesh->param.id;
}
void	EXMesh_setType(EXMeshPTR imesh, EXMeshType type){
	imesh->meshtype = type;
}
EXMeshType EXMesh_getType(EXMeshPTR imesh){
	return imesh->meshtype;
}
void	EXMesh_setInstance(EXMesh *imesh, EXMeshPTR instanceof){
	imesh->instance = instanceof;
}
EXMeshPTR EXMesh_getInstance(EXMeshPTR imesh){
	return imesh->instance;
}
void	EXMesh_setMaterial(EXMesh *imesh, EXMaterialPTR imat){
	imesh->material = imat;
}
EXMaterialPTR EXMesh_getMaterial(EXMeshPTR imesh){
	return imesh->material;
}
void	EXMesh_setSkin(EXMesh *imesh, EXSkinPTR iskin){
	imesh->skin = iskin;
}
EXSkinPTR EXMesh_getSkin(EXMeshPTR imesh){
	return imesh->skin;
}
void	EXMesh_setParent(EXMesh *imesh, EXMeshPTR parent){
	imesh->parent = parent;
}
EXMeshPTR ExMesh_getParent(EXMeshPTR imesh){
	return imesh->parent;
}
void	EXMesh_setNoTangentSpace(EXMesh *imesh, booln state){
	imesh->noTangentSpace = state;
}
booln		EXMesh_getNoTangentSpace	(EXMesh *imesh){
	return imesh->noTangentSpace;
}
void	EXMesh_setNoLocalMirrors(EXMesh *imesh, booln state){
	imesh->noLocalMirrors = state;
}
booln		EXMesh_getNoLocalMirrors(EXMesh *imesh){
	return imesh->noLocalMirrors;
}

void	EXMesh_setPosition(EXMeshPTR imesh, float x, float y, float z){
	imesh->location.pos[0]=x;
	imesh->location.pos[1]=y;
	imesh->location.pos[2]=z;
}
float* 	EXMesh_getPosition(EXMeshPTR imesh){
	return imesh->location.pos;
}
void	EXMesh_setRotation(EXMeshPTR imesh, float x, float y, float z, float r){
	imesh->location.rot[0]=x;
	imesh->location.rot[1]=y;
	imesh->location.rot[2]=z;
	imesh->location.rot[3]=r;
}
float* 	EXMesh_getRotation(EXMeshPTR imesh){
	return imesh->location.rot;
}
void	EXMesh_setScale(EXMeshPTR imesh, float x, float y, float z){
	imesh->location.scale[0]=x;
	imesh->location.scale[1]=y;
	imesh->location.scale[2]=z;
}
float* 	EXMesh_getScale(EXMeshPTR imesh){
	return imesh->location.scale;
}
void	EXMesh_setMatrix(EXMeshPTR imesh, const Matrix44 matrix){
	Matrix44Copy(imesh->location.matrix,matrix);
}
float*	EXMesh_getMatrix(EXMeshPTR imesh){
	return imesh->location.matrix;
}
int	EXMeshIndex_getVertexOrderIDX	(EXMeshPTR imesh, EXIndexPTR iprim){
	EXMeshPTR vertmesh = imesh->instance ? imesh->instance : imesh;
	return 	vertmesh->validVertexIndex ? vertmesh->vertices[iprim->vertexIDX].orderIDX : -1;
}

void	EXMesh_setPrimitiveType(EXMeshPTR imesh, EXPrimitiveType meshType){
	imesh->primitivetype = meshType;
}
EXPrimitiveType		EXMesh_getPrimitiveType(EXMeshPTR imesh){
	return imesh->primitivetype;
}
uint EXMesh_getPrimitiveGroupCount(EXMeshPTR imesh){
	return (uint)imesh->primitivegroups.size();
}
uint			EXMesh_getIndexTotalCount		(EXMeshPTR imesh){
	size_t pcnt = imesh->primitivegroups.size();
	size_t icnt = 0;
	for (size_t i = 0; i < pcnt; i++){
		icnt += imesh->primitivegroups[i].indices.size();
	}
	return (uint)icnt;
}
EXPrimitiveGroup *EXMesh_providePrimitiveGroup(EXMesh *imesh, uint grp)
{
	return ProvideAt<EXPrimitiveGroup>(imesh->primitivegroups,grp);
}
EXPrimitiveGroup *EXMesh_getPrimitiveGroup(EXMesh *imesh, uint grp){
	return GetAt<EXPrimitiveGroup>(imesh->primitivegroups,grp);
}
EXPrimitiveGroupPTR	EXMesh_addPrimitiveGroup	(EXMeshPTR imesh){
	return AddTo<EXPrimitiveGroup>(imesh->primitivegroups);
}

// EXPrimitiveGroup
uint			EXPrimitiveGroup_getIndicesCount(EXPrimitiveGroupPTR iprimgrp){
	return (uint)iprimgrp->indices.size();
}
EXIndexPTR		EXPrimitiveGroup_getIndex(EXPrimitiveGroupPTR iprimgrp, uint atindex){
	return GetAt<EXIndex>(iprimgrp->indices,atindex);
}
EXIndexPTR		EXPrimitiveGroup_provideIndex(EXPrimitiveGroupPTR iprimgrp, uint atindex){
	return ProvideAt<EXIndex>(iprimgrp->indices,atindex);
}
EXIndexPTR		EXPrimitiveGroup_addIndex(EXPrimitiveGroupPTR iprimgrp){
	return AddTo<EXIndex>(iprimgrp->indices);
}


//EXIndexPTR
void	EXIndex_setIndexAttribs(EXIndexPTR iprim, uint primID, uint vertexIDX){
	iprim->primID = primID;
	iprim->vertexIDX = vertexIDX;
}
uint	EXIndex_getID(EXIndexPTR iprim){
	return iprim->primID;
}
uint	EXIndex_getVertexIDX(EXIndexPTR iprim){
	return iprim->vertexIDX;
}
EXVertexPTR		EXIndex_getVertex(EXIndexPTR iprim){
	return iprim->vertex;
}
int			EXIndex_getVertexOrderIDX(EXIndexPTR iprim){
	return iprim->vertex ? iprim->vertex->orderIDX : -1;
}

//EXMeshPTR (vertex)
uint EXMesh_getVertexCount(EXMeshPTR imesh){
	return (uint)imesh->vertices.size();
}
booln EXMesh_hasValidVertexOrder(EXMeshPTR imesh){
	return imesh->validVertexIndex;
}
uint EXMesh_getVertexOrderCount	(EXMeshPTR imesh){
	return imesh->validVertexIndex ? imesh->vertexOrder.size() : 0;
}
void EXMesh_setUsedUVWCount(EXMeshPTR imesh, uint uvcount){
	imesh->maxUVcount = uvcount;
}
uint EXMesh_getUsedUVWCount(EXMeshPTR imesh){
	return imesh->maxUVcount;
}
EXVertexPTR		EXMesh_getVertexOrdered(EXMeshPTR imesh, uint index){
	if (!imesh->validVertexIndex || index >= imesh->vertexOrder.size())
		return NULL;

	return GetAt<EXVertex>(imesh->vertices,imesh->vertexOrder[index]);
}
EXVertexPTR		EXMesh_getVertex(EXMeshPTR imesh, uint index){
	return GetAt<EXVertex>(imesh->vertices,index);
}
EXVertexPTR		EXMesh_provideVertex(EXMeshPTR imesh, uint atindex){
	return ProvideAt<EXVertex>(imesh->vertices,atindex);
}
EXVertexPTR		EXMesh_addVertex(EXMeshPTR imesh){
	return AddTo<EXVertex>(imesh->vertices);
}


//EXVertexPTR
int		EXVertex_getOrder		(EXVertexPTR ivert){
	return ivert->orderIDX;
}
void	EXVertex_setPosition	(EXVertex *vertex, float x, float y, float z){
	vertex->position[0]=x;
	vertex->position[1]=y;
	vertex->position[2]=z;
}
float*	EXVertex_getPosition		(EXVertexPTR ivert){
	return ivert->position;
}
void	EXVertex_setNormal	(EXVertex *vertex, float x, float y, float z){
	vertex->normal[0]=x;
	vertex->normal[1]=y;
	vertex->normal[2]=z;
}
float*	EXVertex_getNormal		(EXVertexPTR ivert){
	return ivert->normal;
}
void	EXVertex_setColor	(EXVertex *vertex, float r, float g, float b, float a){
	vertex->color[0]=r;
	vertex->color[1]=g;
	vertex->color[2]=b;
	vertex->color[3]=a;
}
float*	EXVertex_getColor		(EXVertexPTR ivert){
	return ivert->color;
}
void	EXVertex_setTangent	(EXVertex *vertex, float x, float y, float z){
	vertex->tangent[0]=x;
	vertex->tangent[1]=y;
	vertex->tangent[2]=z;
}
float*	EXVertex_getTangent		(EXVertexPTR ivert){
	return ivert->tangent;
}
void	EXVertex_setBinormal	(EXVertex *vertex, float x, float y, float z){
	vertex->binormal[0]=x;
	vertex->binormal[1]=y;
	vertex->binormal[2]=z;
}
float*	EXVertex_getBinormal	(EXVertexPTR ivert){
	return ivert->binormal;
}
void	EXVertex_setID	(EXVertex *vertex, int vertexID){
	vertex->vertexID = vertexID;
}
int	EXVertex_getID		(EXVertexPTR ivert){
	return ivert->vertexID;
}


void	EXVertex_setUVW(EXVertex *vertex, uint uvIndex, float u, float v, float w, float r){
	float *vector = vertex->uvws[uvIndex % EXMODEL_MAXUVWS];
	vector[0]=u;
	vector[1]=v;
	vector[2]=w;
	vector[3]=r;
}
float*	EXVertex_getUVW(EXVertex *vertex, uint uvIndex){
	return vertex->uvws[uvIndex % EXMODEL_MAXUVWS];
}

// EXMaterialPTR
uint EXMaterial_getID (EXMaterialPTR imat){
	return imat->param.id;
}
void	EXMaterial_setName(EXMaterialPTR imat, const char *name){
	imat->param.name = std::string(name);
}
const char*	EXMaterial_getName(EXMaterialPTR imat){
	return imat->param.name.c_str();
}
uint EXMaterial_getSubCount(EXMaterialPTR imat){
	return (uint)imat->submats.size();
}
void EXMaterial_setSubMaterial(EXMaterialPTR imat, uint atindex, EXMaterialPTR submat){
	SetAt<EXMaterialPTR>(imat->submats,atindex,submat);
}
EXMaterialPTR	EXMaterial_getSubMaterial(EXMaterialPTR imat, uint index){
	return GetAt<EXMaterial>(imat->submats,index);
}

void EXMaterial_setShaderName(EXMaterialPTR imat,const char* name){
	imat->shaderName = std::string(name);
}
const char*	 EXMaterial_getShaderName(EXMaterialPTR imat){
	return imat->shaderName.c_str();
}


// EXSkinPTR
uint		EXSkin_getID(EXSkinPTR iskin){
	return iskin->param.id;
}
const char*	EXSkin_getName(EXSkinPTR iskin){
	return iskin->param.name.c_str();
}
EXMeshPTR	EXSkin_getMesh(EXSkinPTR iskin){
	return iskin->mesh;
}
void		EXSkin_setMesh(EXSkinPTR iskin, EXMeshPTR imesh){
	iskin->mesh = imesh;
}

void		EXSkin_setUsedWeightCount(EXSkinPTR iskin, uint max){
	iskin->maxWeights = MIN(max,EXMODEL_MAXWEIGHTS);
}
uint		EXSkin_getUsedWeightCount(EXSkinPTR iskin){
	return iskin->maxWeights;
}

uint		EXSkin_getParentsCount(EXSkinPTR iskin){
	return (uint)iskin->skinParents.size();
}
void		EXSkin_setParentMesh(EXSkinPTR iskin, uint subIDX, EXMeshPTR mesh){
	SetAt<EXMeshPTR>(iskin->skinParents,subIDX,mesh);
}
EXMeshPTR	EXSkin_getParentMesh(EXSkinPTR iskin, uint subIDX){
	return 	GetAt<EXMesh>(iskin->skinParents,subIDX);
}
booln	EXSkin_hasValidVertexOrder(EXSkinPTR iskin){
	EXMeshPTR mesh = iskin->mesh;
	iskin->validVertexIndex = (mesh 
		&& mesh->vertices.size() == iskin->skinVerts.size()
		&& EXMesh_hasValidVertexOrder(mesh));

	return iskin->validVertexIndex;
}
uint		EXSkin_getVertexCount(EXSkinPTR iskin){
	return (uint)iskin->skinVerts.size();
}
uint		EXSkin_getVertexOrderCount(EXSkinPTR iskin){
	return iskin->validVertexIndex ? (uint)iskin->skinVerts.size() : 0;
}
EXSkinVertexPTR	EXSkin_getVertexOrdered	(EXSkinPTR iskin, uint index){
	if (!iskin->validVertexIndex || index >= iskin->skinVerts.size())
		return NULL;

	EXMeshPTR mesh = iskin->mesh;
	return &iskin->skinVerts[mesh->vertexOrder[index]];
}
EXSkinVertexPTR	EXSkin_getVertex(EXSkinPTR iskin, uint index){
	return GetAt<EXSkinVertex>(iskin->skinVerts,index);
}
EXSkinVertexPTR	EXSkin_provideSkinVertex(EXSkinPTR iskin, uint atindex){
	return ProvideAt<EXSkinVertex>(iskin->skinVerts,atindex);
}
EXSkinVertexPTR	EXSkin_addSkinVertex(EXSkinPTR iskin){
	return AddTo<EXSkinVertex>(iskin->skinVerts);
}

// EXSkinVertexPTR
void EXSkinVertex_setVertexID	(EXSkinVertexPTR isvert, int vertexID){
	isvert->vertexID = vertexID;
}
int	EXSkinVertex_getVertexID	(EXSkinVertexPTR isvert){
	return isvert->vertexID;
}
int	EXSkinVertex_getVertexIDX	(EXSkinVertexPTR isvert){
	return isvert->vertexIDX ;
}
uint EXSkinVertex_getWeightsCount(EXSkinVertexPTR isvert){
	return isvert->numWeights;
}

EXSkinWeightPTR	EXSkinVertex_addWeight
	(EXSkinVertexPTR isvert, float weight,int parentIDX, EXMeshPTR parent)
{
	EXSkinWeightPTR out = NULL;

	if (!parent && parentIDX < 0)
		return NULL;

	// insertion sort with highest weight first
	int insert = -1;
	for (uint i = 0; i < isvert->numWeights; i++){
		if (weight > isvert->weights[i].weight){
			insert = i;
		}
	}
	if (insert >= 0) {
		out = &isvert->weights[insert];
		// shift all others one up
		for (int i = MIN(isvert->numWeights,(EXMODEL_MAXWEIGHTS-1)); i > insert; i--){
			isvert->weights[i] = isvert->weights[i-1];
		}
	}
	else if (isvert->numWeights < EXMODEL_MAXWEIGHTS){
		out = &isvert->weights[isvert->numWeights];
	}
	isvert->numWeights = MIN(EXMODEL_MAXWEIGHTS,isvert->numWeights+1);
	if (out){
		out->parent = parent;
		out->parentIDX = parentIDX;
		out->weight = weight;
	}

	return out;
}
EXSkinWeightPTR	EXSkinVertex_getWeight(EXSkinVertexPTR isvert, uint index)
{
	if (index >= EXMODEL_MAXWEIGHTS) return NULL;
	return &isvert->weights[index];
}

// EXSkinWeightPTR
float EXSkinWeight_getWeight		(EXSkinWeightPTR isw){
	return isw->weight;
}
EXMeshPTR		EXSkinWeight_getParent		(EXSkinWeightPTR isw){
	return isw->parent;
}
int				EXSkinWeight_getParentIDX	(EXSkinWeightPTR isw){
	return isw->parentIDX;
}