#include <v_lib.h>

typedef struct trivert_s
{
	vec3_t		vertex;
	vec3_t		normal;
	color4b_t	color;
	vec2_t		texcoord;
	u8_t		attrib[4];
}	trivert_t, *trivert_p;

typedef struct triface_s
{
	mesh_vertex_t	triangle[3];
	plane_t			face_plane;
	u8_t			attrib[4];	/* variable-sized per-face attrib */
}	triface_t, *triface_p;

typedef struct trimesh_s
{
	libv_object_t	base;
	
	u32_t			vertex_struct_size;
	u32_t			face_struct_size;
	u32_t			map_struct_size;
	u32_t			mtl_struct_size;
	u32_t			obj_struct_size;
	
	trivert_p		curvert;
	triface_p		curface;
	mesh_map_p		curmap;
	mesh_mtl_p		curmtl;
	mesh_object_p	curobj;

	bstack_t		vertex_pool;
	bstack_t		face_pool;
	bstack_t		map_pool;
	bstack_t		mtl_pool;
	bstack_t		obj_pool;
	

	/* Extra per-mesh memory */
	u32_t			attrib_size;
	void_p			attrib;
	
}	trimesh_t, trimesh_p;

#define	MESH_MAGIC 0x4D455348	/* MESH */
V_LOCAL char_p	mesh_object = "triangle mesh";

V_LOCAL	u32_t	V_CALL
__meshDestroy(vhandle_t handle)
{
	u32_t i, num_maps;
	mesh_map_t *map;
	trimesh_p mesh=(trimesh_p)handle;
	if(--mesh->base.ref_count)
		return mesh->base.ref_count;
	stackClear(&mesh->vertex_pool);
	stackClear(&mesh->normal_pool);
	stackClear(&mesh->color_pool);
	stackClear(&mesh->texcoord_pool);
	stackClear(&mesh->face_pool);
	/* Unload all textures */
	num_maps=stackCount(&mesh->map_pool);
	map=(mesh_map_t*)stackGetStorage(&mesh->map_pool);
	for(i=0;i<map_count;i++)
	{
		if(glIsTexture(map->handle))
			glDeleteTextures(1,&map->handle);
		maps++;
	}
	stackClear(&mesh->map_pool);
	stackClear(&mesh->mtl_pool);
	stackClear(&mesh->obj_pool);
	memFreeObject(mesh);
	return 0;
}

vhandle_t V_GLOBAL
meshCreateEx(u32_t vertex_attrib_size,u32_t face_attrib_size,
	u32_t map_attrib_size,u32_t mtl_attrib_size,
	u32_t object_attrib_size,u32_t mesh_attrib_size)
{
	u32_t extra;
	u8_t *chunk;
	trimesh_p mesh;
	
	/* Compute extra memory size */
	extra = 
	
	ALLOC_OBJECT(mesh,trimesh_t,mesh_attrib_size,MESH_MAGIC);
	mesh->base.destroy=__meshDestroy;
	mesh->mesh_attrib_size=mesh_attrib_size;
	mesh->vertex_struct_size = sizeof(mesh_vert
	
	/* Allocate extra memory */
	ptr = (u8_t*)(mesh + 1);
	/* Global attribute */
	if(mesh_attrib_size > 0)
	{
		mesh->attrib = ptr;
		ptr += mesh_attrib_size;
	}
	else
		mesh->attrib=NULL;
	/* Struct object */
	
	/* Initialize storages */
	stackInit(&mesh->vertex_pool,sizeof(vec3_t));
	stackInit(&mesh->normal_pool,sizeof(vec3_t));
	stackInit(&mesh->color_pool,sizeof(color4b_t));
	stackInit(&mesh->texcoord_pool,sizeof(vec2_t));
	stackInit(&mesh->face_pool,sizeof(triface_t));
	stackInit(&mesh->map_pool,sizeof(mesh_map_t));
	stackInit(&mesh->mtl_pool,sizeof(mesh_mtl_t));
	stackInit(&mesh->obj_pool,sizeof(mesh_object_t));
	vec3Set(mesh->curvert.normal,0,0,1);
	rgbaSet(mesh->curvert.color,1,1,1,.5f);
	/* Create default map */
	/* Create default material */
	return mesh;
}

vhandle_t V_GLOBAL
meshCreate(void)
{
	return meshCreateEx(0,0,0,0,0,0);
}


void V_GLOBAL	meshVertex3(vhandle_t mesh,vec_t x,vec_t y,vec_t z);
void V_GLOBAL	meshVertex3v(vhandle_t mesh,vec3_t vert);
void V_GLOBAL	meshColor4(vhandle_t mesh,u8_t r,u8_t g,u8_t b,u8_t a);

void V_GLOBAL	meshColor4v(vhandle_t mesh,vec3_t vert);
void V_GLOBAL	meshVertex3(vhandle_t mesh,vec_t x,vec_t y,vec_t z);
void V_GLOBAL	meshVertex3v(vhandle_t mesh,vec3_t vert);
void V_GLOBAL	meshVertex3(vhandle_t mesh,vec_t x,vec_t y,vec_t z);
void V_GLOBAL	meshVertex3v(vhandle_t mesh,vec3_t vert);
void V_GLOBAL	meshVertex3(vhandle_t mesh,vec_t x,vec_t y,vec_t z);
void V_GLOBAL	meshVertex3v(vhandle_t mesh,vec3_t vert);



















/*
 * 3DS MAX ASE tokens
 */
typedef enum
{
	/* global tokens */
	ASE_SCENE,
	ASE_SCENE_FILENAME,
	ASE_SCENE_FIRSTFRAME,
	ASE_SCENE_LASTFRAME,
	ASE_SCENE_FRAMESPEED,
	ASE_SCENE_TICKSPERFRAME,
	ASE_SCENE_BACKGROUND_STATIC,
	ASE_SCENE_AMBIENT_STATIC,
	/* material list tokens */
	ASE_MATERIAL_LIST,
	ASE_MATERIAL_COUNT,
	ASE_MATERIAL,
	/* material definition tokens */
	ASE_MATERIAL_NAME,
	ASE_MATERIAL_CLASS,
	ASE_MATERIAL_AMBIENT,
	ASE_MATERIAL_DIFFUSE,
	ASE_MATERIAL_SPECULAR,
	ASE_MATERIAL_SHINE,
	ASE_MATERIAL_SHINESTRENGTH,
	ASE_MATERIAL_TRANSPARENCY,
	ASE_MATERIAL_WIRESIZE,
	ASE_MATERIAL_SHADING,
	ASE_MATERIAL_XP_FALLOFF,
	ASE_MATERIAL_SELFILLUM,
	ASE_MATERIAL_FALLOFF,
	ASE_MATERIAL_SOFTEN,
	ASE_MATERIAL_XP_TYPE,
	/* map definition tokens */
	ASE_MAP_GENERIC,
	ASE_MAP_DIFFUSE,
	ASE_MAP_REFLECT,
	ASE_MAP_NAME,
	ASE_MAP_CLASS,
	ASE_MAP_SUBNO,
	ASE_MAP_AMOUNT,
	ASE_BITMAP,
	ASE_MAP_TYPE,
	ASE_UVW_U_OFFSET,
	ASE_UVW_V_OFFSET,
	ASE_UVW_U_TILING,
	ASE_UVW_V_TILING,
	ASE_UVW_ANGLE,
	
	/* geomobject definition tokens */
	ASE_GEOMOBJECT,
	ASE_NODE_NAME,
  
	ASE_NODE_TM,
	ASE_INHERIT_POS,
	ASE_INHERIT_ROT,
	ASE_INHERIT_SCL,
	ASE_TM_ROW0,
	ASE_TM_ROW1,
	ASE_TM_ROW2,
	ASE_TM_ROW3,
	ASE_TM_POS,
	ASE_TM_ROTAXIS,
	ASE_TM_ROTANGLE,
	ASE_TM_SCALE,
	ASE_TM_SCALEAXIS,
	ASE_TM_SCALEAXISANG,
  
	ASE_MESH,
	ASE_TIMEVALUE,
	ASE_MESH_NUMVERTEX,
	ASE_MESH_NUMFACES,
	ASE_MESH_VERTEX_LIST,
	ASE_MESH_VERTEX,
	ASE_MESH_FACE_LIST,
	ASE_MESH_FACE,
	ASE_MESH_SMOOTHING,
	ASE_MESH_MTLID,
	ASE_MESH_NUMTVERTEX,
	ASE_MESH_TVERTLIST,
	ASE_MESH_TVERT,
	ASE_MESH_NUMTVFACES,
	ASE_MESH_TFACELIST,
	ASE_MESH_TFACE,
	ASE_MESH_NUMCVERTEX,
	ASE_MESH_CVERTLIST,
	ASE_MESH_CVERT,
	ASE_MESH_NUMCVFACES,
	ASE_MESH_CFACELIST,
	ASE_MESH_CFACE,
	ASE_MESH_NORMALS,
	ASE_MESH_FACENORMAL,
	ASE_MESH_VERTEXNORMAL,
  
	ASE_PROP_MOTIONBLUR,
	ASE_PROP_CASTSHADOW,
	ASE_PROP_RECVSHADOW,
	ASE_WIREFRAME_COLOR,
	ASE_MATERIAL_REF,
  
	ASE_MAX_TOKEN
  
}	dsmax_token_t;

typedef struct
{
  dsmax_token_t id;
  char *value;
} dsmax_token_pair;

/* Tokens in global scope */
static dsmax_token_pair dsmax_global_tokens[] =
{
	{ ASE_SCENE, "*SCENE"},
	{ ASE_MATERIAL_LIST, "*MATERIAL_LIST"},
	{ ASE_GEOMOBJECT, "*GEOMOBJECT" },
	{ ASE_MAX_TOKEN, "" }
};

/* Tokens in material list */
static dsmax_token_pair dsmax_mtl_list_tokens[] =
{
	{ ASE_MATERIAL_COUNT, "*MATERIAL_COUNT"},
	{ ASE_MATERIAL, "*MATERIAL"},
	{ ASE_MAX_TOKEN, "" }
};

/* Tokens in material definition
 * fixme: use hash !!!
 */
static dsmax_token_pair dsmax_mtl_tokens[] =
{
	{ ASE_MATERIAL_NAME, "*MATERIAL_NAME"},
	{ ASE_MATERIAL_CLASS, "*MATERIAL_CLASS"},
	{ ASE_MATERIAL_AMBIENT, "*MATERIAL_AMBIENT" },
	{ ASE_MATERIAL_DIFFUSE, "*MATERIAL_DIFFUSE" },
	{ ASE_MATERIAL_SPECULAR, "*MATERIAL_SPECULAR" },
	{ ASE_MATERIAL_SHINE, "*MATERIAL_SHINE" },
	{ ASE_MATERIAL_SHINESTRENGTH, "*MATERIAL_SHINESTRENGTH" },
	{ ASE_MATERIAL_TRANSPARENCY, "*MATERIAL_TRANSPARENCY" },
	{ ASE_MATERIAL_WIRESIZE, "*MATERIAL_WIRESIZE" },
	{ ASE_MATERIAL_SHADING, "*MATERIAL_SHADING" },
	{ ASE_MATERIAL_XP_FALLOFF, "*MATERIAL_XP_FALLOFF" },
	{ ASE_MATERIAL_SELFILLUM, "*MATERIAL_SELFILLUM" },
	{ ASE_MATERIAL_FALLOFF, "*MATERIAL_FALLOFF" },
	{ ASE_MATERIAL_XP_TYPE, "*MATERIAL_XP_TYPE" },
	{ ASE_MAP_GENERIC, "*MAP_GENERIC" },
	{ ASE_MAP_DIFFUSE, "*MAP_DIFFUSE" },
	{ ASE_MAP_REFLECT, "*MAP_REFLECT" },
	{ ASE_MAX_TOKEN, "" }
};

/* Tokens in map definition */
static dsmax_token_pair dsmax_map_tokens[] =
{
	{ ASE_MAP_NAME, "*MAP_NAME" },
	{ ASE_MAP_CLASS, "*MAP_CLASS" },
	{ ASE_MAP_SUBNO, "*MAP_SUBNO" },
	{ ASE_MAP_AMOUNT, "*MAP_AMOUNT" },
	{ ASE_BITMAP, "*BITMAP" },
	{ ASE_MAP_TYPE, "*MAP_TYPE" },
	{ ASE_UVW_U_OFFSET, "*UVW_U_OFFSET" },
	{ ASE_UVW_V_OFFSET, "*UVW_V_OFFSET" },
	{ ASE_UVW_U_TILING, "*UVW_U_TILING" },
	{ ASE_UVW_V_TILING, "*UVW_V_TILING" },
	{ ASE_UVW_ANGLE, "*UVW_ANGLE" },
	{ ASE_MAX_TOKEN, "" }
};

/* Tokens in geomobject definition */
static dsmax_token_pair dsmax_geomobject_tokens[] =
{
	{ ASE_NODE_NAME, "*NODE_NAME" },
	{ ASE_NODE_TM, "*NODE_TM" },
	{ ASE_MESH, "*MESH" },
	{ ASE_PROP_MOTIONBLUR, "*PROP_MOTIONBLUR" },
	{ ASE_PROP_CASTSHADOW, "*PROP_CASTSHADOW" },
	{ ASE_PROP_RECVSHADOW, "*PROP_RECVSHADOW" },
	{ ASE_MATERIAL_REF, "*MATERIAL_REF" },
	{ ASE_MAX_TOKEN, "" }
}

/* Tokens in node transform definition */
static dsmax_token_pair dsmax_node_tm_tokens[] =
{
	{ ASE_NODE_NAME, "*NODE_NAME" },
	{ ASE_INHERIT_POS, "*INHERIT_POS" },
	{ ASE_INHERIT_ROT, "*INHERIT_ROT" },
	{ ASE_INHERIT_SCL, "*INHERIT_SCL" },
	{ ASE_TM_ROW0, "*TM_ROW0" },
	{ ASE_TM_ROW1, "*TM_ROW1" },
	{ ASE_TM_ROW2, "*TM_ROW2" },
	{ ASE_TM_ROW3, "*TM_ROW3" },
	{ ASE_TM_POS, "*TM_POS" },
	{ ASE_TM_ROTAXIS, "*TM_ROTAXIS" },
	{ ASE_TM_ROTANGLE, "*TM_ROTANGLE" },
	{ ASE_TM_SCALE, "*TM_SCALE" },
	{ ASE_TM_SCALEAXIS, "*TM_SCALEAXIS" },
	{ ASE_TM_SCALEAXISANG, "*TM_SCALEAXISANG" },
	{ ASE_MAX_TOKEN, "" }
}

/* Tokens in mesh definition */
static dsmax_token_pair dsmax_ase_scene_pokens[] =
{

	{ ASE_MESH_NUMVERTEX, "*MESH_NUMVERTEX" },
	{ ASE_MESH_NUMFACES, "*MESH_NUMFACES" },
	{ MESH_VERTEX_LIST, "*MESH_VERTEX_LIST" },
	{ ASE_MESH_FACE_LIST, "*MESH_FACE_LIST" },
	{ ASE_MESH_NUMTVERTEX, "*MESH_NUMTVERTEX" },
	{ ASE_MESH_TVERTLIST, "*MESH_TVERTLIST" },
	{ ASE_MESH_NUMTVFACES, "*MESH_NUMTVFACES" },
	{ ASE_MESH_TFACELIST, "*MESH_TFACELIST" },
	{ ASE_MESH_NUMCVERTEX, "*MESH_NUMCVERTEX" },
	{ ASE_MESH_CVERTLIST, "*MESH_CVERTLIST" },
	{ ASE_MESH_NUMCVFACES, "*MESH_NUMCVFACES" },
	{ ASE_MESH_CFACELIST, "*MESH_CFACELIST" },
	{ ASE_MESH_NORMALS, "*MESH_NORMALS"},
	{ ASE_TIMEVALUE, "*TIMEVALUE" },
	{ ASE_MAX_TOKEN, "" }
}

V_LOCAL aseToken(dsmax_token_pair_t *pairs,char *token)
{
	int i;
	for(i=0;pairs[i].id != ASE_MAX_TOKEN;i++)
		if(!strcmp(pairs[i].value,token))
			break;
	return pairs[i].id;
}

#define	aseGlobalToken(str)			aseToken(dsmax_global_tokens,str)
#define	aseMaterialListToken(str)	aseToken(dsmax_mtl_list_tokens,str)
#define	aseMaterialToken(str)		aseToken(dsmax_mtl_tokens,str)
#define	aseMapToken(str)			aseToken(dsmax_map_tokens,str)
#define	aseGeomobjectToken(str)		aseToken(dsmax_geomobject_tokens,str)
#define	aseNodeTransformToken(str)	aseToken(dsmax_node_tm_tokens,str)
#define	aseMeshToken(str)			aseToken(dsmax_ase_scene_pokens,str)

typedef struct ase_scene_s
{
	bstack_t materials;
	bstack_t maps;
	bstack_t meshes;
	mesh_material_t curmat;
	mesh_map_t curmap;
}	ase_scene_t, ase_scene_p;

V_LOCAL void
aseSkipToCloseBrace(scanner_t in)
{
	while(lexNext(in))
		if(lexLast(in)[0] == '}')
			break;
}

vboolean aseParseScene(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMaterialList(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMaterial(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMap(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseGeomobject(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseNodeTransform(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMesh(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMeshVertexList(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMeshFaceList(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMeshTextureVertexList(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}

vboolean aseParseMeshTextureFaceList(ase_scene_p out,scanner_t in)
{
	aseSkipToCloseBrace(in);
	return true;
}



int main(int argc,char *argv[])
{
	scanner_t in;
	ase_scene_p out;
	in=lexParseFile(argv[1],"");
	if(!tok)
  		return -1;
  		
	if(lexNext(in)==NULL || strcmp(lexLast(in),"*3DSMAX_ASCIIEXPORT"))
	{
		/* wrong file */
		return -1;
	}
	while(lexNext(in) != NULL)
	{
		char *token=lexLast(in);
		switch(aseGlobalToken(token))
		{
			case ASE_GEOMOBJECT:
				aseParseGeomobject(out,in);
				break;
			case ASE_MATERIAL_LIST:
				aseParseMaterialList(out,in);
				break;
			case ASE_SCENE:
				aseParseScene(out,in);
				break;
			default:
				break;
		}
		aseSkipToCloseBrace(in);
	}
	return 0;
}

vboolean V_LOCAL dsmaxLoadMap();
vboolean V_LOCAL dsmaxLoadMaterial();
vboolean V_LOCAL dsmaxLoadMaterialList();
vboolean V_LOCAL dsmaxLoadNodeTM();

vboolean V_LOCAL dsmaxLoadVertexList(vec3_t *dest,int n,tokenizer_t *src);
vboolean V_LOCAL dsmaxLoadFaceList(dsmax_face_t *dest,int n,tokenizer_t *src);

vboolean V_LOCAL dsmaxLoadTextureVertexList();
vboolean V_LOCAL dsmaxLoadTextureFaceList();
vboolean V_LOCAL dsmaxLoadNormals();
vboolean V_LOCAL dsmaxLoadMesh();
vboolean V_LOCAL dsmaxLoadGeomObject();
vboolean V_LOCAL dsmaxLoadScene();
