/*--------------------------------------------------------------------------
 * File: v3ds.c
 * Written by: Fredrik Kling, 1997-07-21
 * Description: 3Dstudio viewer
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 *
 * Todo:
 *
 *
 -------------------------------------------------------------------------------*/
#include "system/xstdlib.h"
#include "system/xstdio.h"
#include "system/xstring.h"
#include "system/xmath.h"
#include "formats/v3o.h"
#include "formats/3ds.h"
#include "vmath/vector.h"
#include "misc/col.h"

#if !defined(M_PI)
#define M_PI 3.141592654
#endif


static int debug = TRUE;
#define dprintf if ( debug) printf

int num_object=0;
int use_objname=0;		// 0 - Dont use object names as filenames...
											// 1 - Use 'em...
typedef enum
{
	ERR_NOERR,
	ERR_NOFILE,
	ERR_NOMEM,
	ERR_CUSTOM,
} ERROR;

static ERROR error = ERR_NOERR;
static char errstr[256];


static void maptextures(V3O *obj);
//static void fixnormals(V3O *obj);
//static void centerobject(V3O *obj);

static void skip (XFILE *f, int next)
{
	char dummy;
	while ((xftell(f) < next) && (!xfeof (f))) xfread (&dummy,1,1,f);
}
static void chunk (XFILE *f)
{
	short int id=0;
	int subend=0;
	xfread (&id, 2, 1, f);
  xfread (&subend, 4, 1, f);
}
/*#############################################################################
#
# Vertextlist Chunk
#
#############################################################################*/

static int vlist(XFILE *f,V3O *obj)
{
	short int num;
	int i;

	xfread (&num, 2, 1, f);
	//vobj -> vertices = (int) num;
	obj->numvertex = (int) num;

	dprintf("[#] Found points: %d\n", num);
	printf("[-] Found %d vertices\n", num);

	// Allocate memory for functions...

  obj->orgvertex=(VECTOR *)xsafe_malloc (sizeof(VECTOR) * obj->numvertex);
	obj->rotvertex=(VECTOR *)xsafe_malloc (sizeof(VECTOR) * obj->numvertex);
	obj->projvertex=(VECTOR *)xsafe_malloc (sizeof(VECTOR) * obj->numvertex);
	obj->orgnormal=(VECTOR *)xsafe_malloc (sizeof(VECTOR) * obj->numvertex);
	obj->rotnormal=(VECTOR *)xsafe_malloc (sizeof(VECTOR) * obj->numvertex);

	if (obj->orgvertex == NULL || obj->rotvertex == NULL || obj->projvertex == NULL || obj->orgnormal==NULL || obj->rotnormal==NULL)
	{
		error = ERR_NOMEM;
		return 0;
	}

	for ( i=0; i<obj->numvertex; i++)
	{
		obj->orgvertex[i].x = (float)xfrbe_float( f);
		obj->orgvertex[i].y = (float)xfrbe_float( f);
		obj->orgvertex[i].z = (float)xfrbe_float( f);
	}

	dprintf("[#] Done\n");
	return 1;
}

/*#############################################################################
#
# Surface Names Chunk
#
#############################################################################*/

static int srfs(XFILE *f,V3O *obj, int next)
{
	short int num,id,dummy;
	int i,subend;
	int material=1;

	xfread (&num, 2, 1, f);
	dprintf ("[-] Found %d surfaces\n",num);

	obj->numsurface = (int)num;


	if ( !material)
	{
		printf("[&] Empty surface chunk, skipping!\n");
		return 1;
	}

	obj->nummaterial = material;
	if ((obj->material = (V3OMATERIAL *)xmalloc( obj->nummaterial * sizeof(V3OMATERIAL)))==NULL)
	{
		error = ERR_NOMEM;
		return 0;
	}

	for( i=0; i<obj->nummaterial; i++)
	{
    obj->material[i].flags = V3OMATERIALFLAG_FLAT;
		obj->material[i].color.r = 200;
		obj->material[i].color.g = 200;
		obj->material[i].color.b = 200;
	}

  if ((obj->surface = (V3OSURFACE *)xmalloc( obj->numsurface * sizeof( V3OSURFACE)))==NULL)
	{
		error = ERR_NOMEM;
		return 0;
	}

	for (i=0;i<obj->numsurface;i++)
	{
  	obj->surface[i].flags = V3OSURFACEFLAG_POLY;
		obj->surface[i].v1 = xfrbe_word( f);
		obj->surface[i].v2 = xfrbe_word( f);
		obj->surface[i].v3 = xfrbe_word( f);
		dummy = xfrbe_word (f); // Read flags...  Unused...

		obj->surface[i].material = 0;
	}

	// Skip subchunks of no intrest...
	while ((xftell (f) < next) && (!xfeof (f)))
	{
		xfread (&id, 2, 1, f);
		xfread (&subend, 4, 1, f);
		subend = xftell (f) - 6 + subend;
		xfseek (f, subend, SEEK_SET);
	}
	dprintf("[#] Done\n");
	return 1;
}

/*#############################################################################
#
# Trimesh chunk
#
#############################################################################*/
static V3O *trimesh (XFILE *f, int next, char *objn)
{
	short int id;
	int subend;
	V3O *obj;


  if ((obj = v3o_create ())==NULL)
	{
		error = ERR_CUSTOM;
		strcpy (errstr,v3o_geterror());
		return NULL;
	}
	num_object++;


	dprintf ("        - Trimesh.\n");

	while ((xftell (f) < next) && (!xfeof (f)))
	{
		xfread (&id, 2, 1, f);
		xfread (&subend, 4, 1, f);
		subend = xftell (f) - 6 + subend;

		switch (id)
		{
			case 0x4110 : if (!vlist (f, obj)) return NULL; break;						/* Chunk $4110 - Vertex list. */
			case 0x4120 : if (!srfs (f,obj,subend)) return NULL; break;
			case 0x4140 : dprintf ("          - Texture coords: %x\n",xftell (f)); break;
			case 0x4160 : dprintf ("          - Mesh matrix   : %x\n",xftell (f)); break;
			case 0x4170 : dprintf ("          - Texture info  : %x\n",xftell (f)); break;
			case 0x4165 : dprintf ("          - Mesh color    : %x\n",xftell (f)); break;
		}
		xfseek (f, subend, SEEK_SET);
  }
	return obj;
}
/*#############################################################################
#
# Objekt chunk
#
#############################################################################*/
static V3O *obj_3ds (XFILE *f, int next)
{
	short int id;
	int subend,i;
	char obj_name[256],c;
	char sname[256];
	V3O *obj;


	for (i=0;c!='\0';i++)
	{
		xfread (&obj_name[i], 1, 1,f);
		c=obj_name[i];
	}

	dprintf ("þ Named object!:  %x,%x\n",xftell (f),next);
	dprintf ("    - Name: %s\n",obj_name);

	while ((xftell(f) < next) && (!xfeof(f)))
	{
		xfread (&id, 2, 1, f);
		xfread (&subend, 4, 1, f);
		subend = xftell (f) - 6 + subend;

		switch (id)
		{
			case 0x4100 : if ((obj=trimesh (f, subend, obj_name))==NULL)
										{
											error = ERR_CUSTOM;
											strcpy (errstr,"Unable to create object!.");
											return NULL;
										}
										v3o_center (obj);
										if (!use_objname)
										  sprintf (sname,"obj%i.v3o",num_object);
											else sprintf (sname,"%s.v3o",obj_name);
									  printf ("[-] Saving v3o object: %s\n",sname);
										v3o_save (sname,obj);
										v3o_free (obj);
										break;
			case 0x4600 : dprintf ("[#] Lightchunk, unsupported.\n"); break;
      case 0x4700 : dprintf ("[#] Camera chunk, unsupported.\n"); break;
		}
		xfseek (f, subend, SEEK_SET);
	}
	return obj;
}
/*#############################################################################
#
# Mesh chunk...
#
#############################################################################*/
static V3O *mesh (XFILE *f,int next)
{
	V3O *obj;
	short int id=0;
	int subend=0;

	dprintf ("[#] Meshdata : %x, %x\n",xftell (f),next);

	while ((xftell(f) < next) && (!xfeof (f)))
	{
		xfread (&id, 2, 1, f);
		xfread (&subend, 4, 1, f);
		subend = xftell (f) - 6 + subend;

		switch (id)
		{
			case 0x4000 : if ((obj=obj_3ds (f,subend))==NULL) return NULL; break;
			case 0x5000 : dprintf ("[#] 2D-poly\n"); break;
			case 0x6000 : dprintf ("[#] 3D-path\n"); break;
			case 0xa000 : dprintf ("[#] Material group\n"); break;
		}
		xfseek (f, subend, SEEK_SET);
	}

	return obj;
}
/*#############################################################################
#
# Keyframer data...
#
#############################################################################*/
static int kf_objnod (XFILE *f, int next,int oid)
{
	short int id=0,node_id,nodeflag,nodepar;
	char nodename[20],instname[20];
	int subend=0,i,j;
	float pivot[3];
	float pos[3];
	int max,min,knum;

	while ((xftell(f) < next) && (!xfeof (f)))
	{
		xfread (&id, 2, 1,f);
		xfread (&subend, 4, 1, f);
		subend = xftell (f) - 6 + subend;

		switch (id)
		{
			case 0xB030 : // Node ID
						chunk (f);
						xfread (&node_id,2,1,f);
						dprintf ("    [#] Node_ID: %d\n",node_id);
						break;
			case 0xB010 : // Node HDR
						chunk (f);
						xfread (nodename,20,1,f);
						xfread (&nodeflag,2,1,f);
						xfread (&nodepar,2,1,f);
						dprintf ("    [#] Nodename: %s\n",nodename);
						dprintf ("    [#] Nodeflag: %d\n",nodeflag);
						if (nodepar==0xffff)
							dprintf ("    [#] No parent object.\n");
						else
							dprintf ("    [#] Node has parent: %x\n",nodepar);
						break;
			case 0x8000 : // App data
						dprintf ("    [#] Appdata, skipping..\n");
						skip (f,subend);
						break;
			case 0xB011 : // Instname
						chunk (f);
						xfread (instname,20,1,f);
						dprintf ("    [#] Instance name: %s\n",instname);
						break;

			case 0xB013 : // Pivot
						chunk (f);
						for (i=0;i<3;i++)	xfread (&pivot[i],sizeof (float),1,f);
						dprintf ("    [#] Pivotpoint: (%f,%f,%f)\n",pivot[0],pivot[1],pivot[2]);
						break;
			case 0xB014 : // Bounding box data
						dprintf ("    [#] Bounding box data, skipping..\n");
						skip (f,subend);
						break;
			case 0xB020 : // POS_TAG
						chunk (f);
						xfread (&min,4,1,f);
						xfread (&max,4,1,f);
						xfread (&knum,4,1,f);
						dprintf ("    [#] Position keys found, reading: %i keys\n",knum);
						for (i=0;i<knum;i++)
						{
							for (j=0;j<3;j++)	xfread (&pos[j],sizeof (float),1,f);
							dprintf ("       key[%3i]: (%f,%f,%f)\n",i,pos[0],pos[1],pos[2]);
						}
						break;
			default :
				dprintf ("     [#] Found: '%x', skipping...\n",id);
				skip (f,subend);
				break;
		}
    xfseek (f, subend, SEEK_SET);
	}

}
static int kfdata (XFILE *f,int next)
{
	short int id=0;
	int subend=0;

	dprintf ("[#] Reading KeyFramer data!\n");
	while ((xftell(f) < next) && (!xfeof (f)))
	{
		xfread (&id, 2, 1,f);
		xfread (&subend, 4, 1, f);
		subend = xftell (f) - 6 + subend;

		switch (id)
		{
			case 0xB00A : // KF_HDR
					dprintf ("  [#] Keyframer header\n"); skip (f,subend);
					break;
			case 0xB008 : // KF_SEG
					dprintf ("  [#] Keyframer segment\n"); skip (f,subend);
					break;
			// Read for all object types...
			case 0xB002 : // OBJECT
			case 0xB003 :	// CAMERA
			case 0xB004 :	// CAMERA_TARGET
			case 0xB005 :	// LIGHT
			case 0xB006 : // LIGHT_TARGET
			case 0xB007 :	// SPOTLIGHT
					kf_objnod (f,subend,id);
					break;
			default : dprintf ("  [#] Unsupported chunk: '%x' skipping.\n",id);
								skip (f,subend);
								break;

		}
    xfseek (f, subend, SEEK_SET);
	}
}
/*#############################################################################
#
# Detecting routine...
#
#############################################################################*/
static int detect_3ds (XFILE * fp)
{
	short int id=0;

	xfread (&id, 2, 1, fp);

	if (id != 0x4d4d) return 0;
		else return 1;
}
/*******************************************************************************
*
* Main 3DS loader
*
**********************************************************************************/

V3O *load_3ds (char *filename,int ufn)
{
  V3O *obj;
  XFILE *f;
  short int id=0;
  int objend,subend;

  if ( (f = xfopen( filename, "rb")) == NULL)
	{
		error = ERR_NOFILE;
		return( NULL);
	}

	if (!detect_3ds (f))
	{
		error = ERR_CUSTOM;
		strcpy( errstr, "[LOAD] Invalid or no 3ds-file.");
		xfclose (f);
		return NULL;
	}

	use_objname = ufn;		// Set save flag...

  xfread (&objend, 4, 1, f);
  objend = xftell (f) - 6 + objend;

  while (xftell (f) < objend)
  {
    id = 0;
    xfread (&id, 2, 1, f);
    xfread (&subend, 4, 1, f);
    subend = (xftell (f) -6) + subend;
    switch (id)
    {
      case 0x3d3d : if ((obj=mesh (f,subend))==NULL)
									 	{
											xfclose (f);
											return NULL;
										}
										break;
      case 0xb000 : kfdata (f,subend);
										break;
    }
    xfseek (f, subend, SEEK_SET);
  }
	dprintf ("[#] File contains: %d objects.\n",num_object);
	return( obj);
}
/*################################################################
	# v3d - geterror
	#################################################################*/
char *geterror_3ds()
{
	switch( error)
	{
		case ERR_NOERR: return( "[3DS] No error");
		case ERR_NOFILE: return( "[3DS] Unable to open file");
		case ERR_NOMEM: return( "[3DS] Unable to allocate memory");
		case ERR_CUSTOM:
		{
			char str[256];
			strcpy( str, errstr);
			sprintf( errstr, "[3DS] %s", str);
			return( errstr);
		}
		default: return( "[3DS] Unknown error!");
	}
}

void main (void)
{
	printf ("just testing...\n");
	load_3ds ("heja.3ds",1);
}
