// 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

#ifndef __EXMODEL_LOCAL_H__
#define __EXMODEL_LOCAL_H__

#include <luxexmodel/exmodel.h>
#include <luxmath/luxmath.h>

#include <stdlib.h>
#include <string.h>
#include <vector>
#include <string>
#include <algorithm>

#define ISKIN_MINWEIGHT 0.0001



//////////////////////////////////////////////////////////////////////////
// Common

typedef struct EXInfo_s{
	std::string		name;
	uint			id;
}EXInfo_t;

typedef union EXHost_u{
	EXModelPTR		model;
	EXAnimPTR		anim;
	void*			data;
}EXHost_t;

struct EXParam {
	EXInfo_t	param;
	EXHost_t	host;

	EXParam(void* data=NULL) {param.id = EXMODEL_NOID; param.name = EXMODEL_NONAME; host.data = data;}
	EXParam(void* data,const char *name, uint id) {param.id = id; param.name = name ? name : EXMODEL_NONAME; host.data = data;}
};



//////////////////////////////////////////////////////////////////////////
// Animation


struct EXPRSKey
{
	float		time;
	Vector3		pos;
	Quat		rot;
	Vector3		scale;

	enum32		usedFlags;

	EXPRSKey();
};


struct EXTrack : public EXParam
{
	std::vector<EXPRSKey>	keys;
	float					length;

	EXTrack(void* host, const char *name, uint id) : EXParam(host,name, id) {}
};

struct EXAnim 
{
	std::string				name;
	std::vector<EXTrackPTR>	tracks;

	const char*				errorstring;

	EXAnim() {};
	EXAnim(const char *name) : name(name),errorstring(NULL) {}
	~EXAnim();
};


//////////////////////////////////////////////////////////////////////////
// Model

struct EXPRSInfo {
	//size: 104b
	Matrix44	matrix;
	Vector3		pos;
	Quat		rot;
	Vector3		scale;

	EXPRSInfo();
};

struct EXVertex 
{
	int					clone;
	int					orderIDX;		// index in output storage, set after Model_fix
	int 				vertexID;	
	// EXVertex_identical depends on order
	Vector3 			position;
	Vector3 			normal;
	Vector4 			color;
	Vector3 			tangent;
	Vector3				binormal;
	Vector4				uvws[EXMODEL_MAXUVWS];

	EXVertex() : clone(-1),orderIDX(-1),vertexID(-1) {	}
};

struct EXIndex 
{
	int					vertexIDX;	// vector idx
	EXVertexPTR			vertex;		// vector ptr after Model_fix
	uint				primID;

	EXIndex() : vertexIDX(-1),primID(0),vertex(NULL) {}
};

struct EXPrimitiveGroup
{
	std::vector<EXIndex>	indices;
};

struct EXMesh : public EXParam
{
	EXPRSInfo			location;

	EXMeshType			meshtype;
	EXPrimitiveType		primitivetype;

	EXMeshPTR			parent;
	EXMeshPTR			instance;
	EXMaterialPTR 		material;
	EXSkinPTR			skin;

	booln				noTangentSpace;
	booln				noLocalMirrors;
	booln				validVertexIndex;	// if not set the vertex Indices must be rebuild
	uint				maxUVcount;			// just a help to make it easier to read out the maxuvcount
	
	std::vector<EXVertex>			vertices;
	std::vector<uint>				vertexOrder;
	std::vector<EXPrimitiveGroup>	primitivegroups;
	

	EXMesh(void*host, const char *name, uint id);
};


struct EXSkinWeight
{
	float					weight;
	EXMeshPTR				parent;			// set after FixModel
	int						parentIDX;

	EXSkinWeight() : weight(0.0f),parent(NULL),parentIDX(-1) {}
};

struct EXSkinVertex
{
	EXSkinWeight	weights[EXMODEL_MAXWEIGHTS];
	uint			numWeights;
	int				vertexIDX;			// if skincount==meshcount
	int				vertexID;			// if multiple destinations

	EXSkinVertex() : vertexIDX(-1),numWeights(0),vertexID(-1) {}
};

struct EXSkin : public EXParam
{
	EXMeshPTR						mesh;				// pointer to the mesh it effects
	uint							maxWeights;
	booln							validVertexIndex;

	std::vector<EXMeshPTR>			skinParents;	// holds all used "bones"
	std::vector<EXSkinVertex>		skinVerts;		// holds all vertices

	EXSkin(void*host, const char *name, uint id) : EXParam(host,name,id),mesh(NULL),validVertexIndex(FALSE) {}
};

struct EXMaterial : public EXParam
{
	std::string					shaderName;
	std::vector<EXMaterialPTR>	submats;

	EXMaterial(void*host,const char *name, uint id) : EXParam(host,name,id) {}
};

struct EXModel : public EXParam
{
	std::string					name;

	std::vector<EXMeshPTR>		meshes;
	std::vector<EXMaterialPTR>	materials;
	std::vector<EXSkinPTR>		skins;

	const char*					errorstring;

	EXModel() {}
	EXModel(const char *name) : name(name),errorstring(NULL) {}
	~EXModel();
};


//////////////////////////////////////////////////////////////////////////
// Common Functions

#define ERRORSTRING(host,str)		host->errorstring = (str)

LUX_INLINE booln ParamCompare(EXParam *a, EXParam *b)
{
	return (a->param.id!=EXMODEL_NOID && (a->param.id == b->param.id)) || 
			(a->param.name.compare(EXMODEL_NONAME) && !a->param.name.compare(b->param.name));
}

LUX_INLINE booln ParamIsSame(EXParam *a, const char *str, uint id)
{
	return (a->param.id!=EXMODEL_NOID && (a->param.id == id)) || 
		(str && a->param.name.compare(EXMODEL_NONAME) && !a->param.name.compare(str));
}

LUX_INLINE booln ParamIsValid(const char *name, uint id)
{
	return (id != EXMODEL_NOID || (name && name[0] != 0));
}
	
template <class TYPE>
LUX_INLINE TYPE* SearchAndInsert(std::vector<TYPE*> &vec, void* host, const char *name, uint id)
{
	if (!ParamIsValid(name,id))
		return NULL;

	for (size_t i = 0; i < vec.size(); i++){
		if (ParamIsSame(vec[i],name,id))
			return vec[i];
	}

	TYPE *out = new TYPE(host,name,id);
	vec.push_back(out);

	return out;
}

template <class TYPE>
LUX_INLINE TYPE* ProvideAt(std::vector<TYPE> &vec, uint idx)
{
	size_t oldsize = vec.size();
	if (idx >= oldsize){
		vec.resize(idx+1);
	}
	return &vec[idx];
}

template <class TYPE>
LUX_INLINE void SetAt(std::vector<TYPE> &vec, uint idx, TYPE add)
{
	size_t oldsize = vec.size();
	if (idx >= oldsize){
		vec.resize(idx+1);
	}
	vec[idx] = add;
}

template <class TYPE>
LUX_INLINE TYPE* AddTo(std::vector<TYPE> &vec)
{
	size_t oldsize = vec.size();
	vec.resize(oldsize+1);
	return &vec[oldsize];
}

template <class TYPE>
LUX_INLINE TYPE* GetAt(std::vector<TYPE> &vec, uint idx)
{
	return idx < vec.size() ? &vec[idx] : NULL;
}
template <class TYPE>
LUX_INLINE TYPE* GetAt(std::vector<TYPE*> &vec, uint idx)
{
	return idx < vec.size() ? vec[idx] : NULL;
}

template <class TYPE>
LUX_INLINE uint FindOrAdd(std::vector<TYPE> &vec, TYPE key)
{
	for (size_t i = 0; i < vec.size(); i++){
		if (vec[i] == key)
			return i;
	}
	vec.push_back(key);
	return vec.size()-1;
}

//////////////////////////////////////////////////////////////////////////
// FIX functions

void EXTrack_sortAndPackKeys(EXTrack &imesh);
void EXTrack_fixKeys(EXTrack &itrack,booln spline, booln loop);

booln	EXSkin_fix(EXSkin &iskin);
void	EXMesh_buildOrder(EXMesh &imesh);
booln	EXMesh_linkIndices(EXMesh &imesh);

void	EXModelMesh_splitMultiMaterialToMeshes(EXModelPTR imodel,EXMesh &imesh);

#endif