
#include "qe3.h"
#include "io.h"

eclass_t	*eclass;
eclass_t	*eclass_bad;
char		eclass_directory[1024];

/*

the classname, color triple, and bounding box are parsed out of comments
A ? size means take the exact brush size.

/*QUAKED <classname> (0 0 0) ?
/*QUAKED <classname> (0 0 0) (-8 -8 -8) (8 8 8)

Flag names can follow the size description:

/*QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY

*/
char	*debugname;

eclass_t *Eclass_InitFromText (char *text)
{
	char	*t;
	int		len;
	int		r, i;
	char	parms[256], *p;
	eclass_t	*e;
	char	color[128];

	e = qmalloc(sizeof(*e));
	memset (e, 0, sizeof(*e));
	
	text += strlen("/*QUAKED ");
	
// grab the name
	text = COM_Parse (text);
	e->name = qmalloc (strlen(com_token)+1);
	strcpy (e->name, com_token);
	debugname = e->name;
	
// grab the color, reformat as texture name
	r = sscanf (text," (%f %f %f)", &e->color[0], &e->color[1], &e->color[2]);
	if (r != 3)
		return e;
	sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
	strcpy (e->texdef.name, color);

	while (*text != ')')
	{
		if (!*text)
			return e;
		text++;
	}
	text++;
	
// get the size	
	text = COM_Parse (text);
	if (com_token[0] == '(')
	{	// parse the size as two vectors
		e->fixedsize = true;
		r = sscanf (text,"%f %f %f) (%f %f %f)", &e->mins[0], &e->mins[1], &e->mins[2],
			&e->maxs[0], &e->maxs[1], &e->maxs[2]);
		if (r != 6)
			return e;

		for (i=0 ; i<2 ; i++)
		{
			while (*text != ')')
			{
				if (!*text)
					return e;
				text++;
			}
			text++;
		}
	}
	else
	{	// use the brushes
	}
	
// get the flags
	

// copy to the first /n
	p = parms;
	while (*text && *text != '\n')
		*p++ = *text++;
	*p = 0;
	text++;
	
// any remaining words are parm flags
	p = parms;
	for (i=0 ; i<8 ; i++)
	{
		p = COM_Parse (p);
		if (!p)
			break;
		strcpy (e->flagnames[i], com_token);
	} 

// find the length until close comment
	for (t=text ; t[0] && !(t[0]=='*' && t[1]=='/') ; t++)
	;
	
// copy the comment block out
	len = t-text;
	e->comments = qmalloc (len+1);
	memcpy (e->comments, text, len);
#if 0
	for (i=0 ; i<len ; i++)
		if (text[i] == '\n')
			e->comments[i] = '\r';
		else
			e->comments[i] = text[i];
#endif
	e->comments[len] = 0;
	
	return e;
}


/*
=================
Eclass_InsertAlphabetized
=================
*/
void Eclass_InsertAlphabetized (eclass_t *e)
{
	eclass_t	*s;
	
	if (!eclass)
	{
		eclass = e;
		return;
	}


	s = eclass;
	if (stricmp (e->name, s->name) < 0)
	{
		e->next = s;
		eclass = e;
		return;
	}

	do
	{
		if (!s->next || stricmp (e->name, s->next->name) < 0)
		{
			e->next = s->next;
			s->next = e;
			return;
		}
		s=s->next;
	} while (1);
}


/*
=================
Eclass_ScanFile
=================
*/
void Eclass_ScanFile (char *filename)
{
	int		size;
	char	*data;
	eclass_t	*e;
	int		i;
	char    temp[1024];

	QE_ConvertDOSToUnixName( temp, filename );

	Sys_Printf ("ScanFile: %s\n", temp);

	size = LoadFile (filename, (void *)&data);
	
	for (i=0 ; i<size ; i++)
		if (!strncmp(data+i, "/*QUAKED",8))
		{
			e = Eclass_InitFromText (data+i);
			if (e)
				Eclass_InsertAlphabetized (e);
			else
				printf ("Error parsing: %s in %s\n",debugname, filename);
		}
		
	free (data);
}

/* ------------------------- Daikatana DLL Stuff --------------------------- */

///////////////////////////////////////////////////////////////////////////////
//	define for katana DLL routines
///////////////////////////////////////////////////////////////////////////////

typedef struct
{
	char  *tag;
	char  *str;
} userEclass_t;

///////////////////////////////////////////////////////////////////////////////
//	Eclass_InitForSourceDirectory
///////////////////////////////////////////////////////////////////////////////

void Eclass_InitForSourceDirectory (char *path)
{
	struct _finddata_t fileinfo;
	int		handle;
	char	filename[1024];
	char	filebase[1024];
	char    temp[1024];
	char	*s;

	QE_ConvertDOSToUnixName( temp, path );

	Sys_Printf ("Eclass_InitForSourceDirectory: %s\n", temp );

	strcpy (filebase, path);
	s = filebase + strlen(filebase)-1;
	while (*s != '\\' && *s != '/' && s!=filebase)
		s--;
	*s = 0;

	eclass = NULL;

	handle = _findfirst (path, &fileinfo);
	if (handle != -1)
	{
		do
		{
			sprintf (filename, "%s\\%s", filebase, fileinfo.name);
			Eclass_ScanFile (filename);
		} while (_findnext( handle, &fileinfo ) != -1);

		_findclose (handle);
	}

	eclass_bad = Eclass_InitFromText ("/*QUAKED UNKNOWN_CLASS (0 0.5 0) ?");
}

///////////////////////////////////////////////////////////////////////////////
//	Eclass_ForName
///////////////////////////////////////////////////////////////////////////////

eclass_t *Eclass_ForName (char *name, qboolean has_brushes)
{
	eclass_t	*e;
	char		init[1024];

	if (!name)
		return eclass_bad;

	for (e=eclass ; e ; e=e->next)
		if (!strcmp (name, e->name))
			return e;

	// create a new class for it
	if (has_brushes)
	{
		sprintf (init, "/*QUAKED %s (0 0.5 0) ?\nNot found in source.\n", name);
		e = Eclass_InitFromText (init);
	}
	else
	{
		sprintf (init, "/*QUAKED %s (0 0.5 0) (-8 -8 -8) (8 8 8)\nNot found in source.\n", name);
		e = Eclass_InitFromText (init);
	}

	Eclass_InsertAlphabetized (e);

	return e;
}

///////////////////////////////////////////////////////////////////////////////
//	Eclass_New
//
///////////////////////////////////////////////////////////////////////////////

static eclass_t *Eclass_New(char *className)
{
	eclass_t *e;

    e = malloc(sizeof(eclass_t));
	memset(e, 0x00, sizeof(eclass_t));

	// classname
	e->name = strdup(className);
	return(e);
}

///////////////////////////////////////////////////////////////////////////////
//	Eclass_InitFromDLL
///////////////////////////////////////////////////////////////////////////////

static userEclass_t *Eclass_InitFromDLL (userEclass_t *eptr, eclass_t *e)
{
	char	color[128];
	char    comments[5120];
	int     flagCount, r;

	comments[0] = 0x00;
	flagCount   = 0;

	// to next entry
	eptr++;

	while (eptr && eptr->tag)
	{
		// Sys_Printf("tag: |%s|\n", eptr->tag);

		// see if we are done
		if (!strcmp(eptr->tag, "classname")) 
		{
			break;
		}

		// color
		if (!strcmp(eptr->tag, "color"))
		{
			r = sscanf(eptr->str, "%f %f %f", &e->color[0], &e->color[1], &e->color[2]);
			if (r != 3) return(eptr);

			sprintf(color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
			strcpy(e->texdef.name, color);
		}

		// size is optional
		else if (!strcmp(eptr->tag, "size"))
		{
			e->fixedsize = true;

			// Sys_Printf("size: %s\n", eptr->str);

			r = sscanf(eptr->str, "%f %f %f %f %f %f", 
				&e->mins[0], &e->mins[1], &e->mins[2],
				&e->maxs[0], &e->maxs[1], &e->maxs[2]);

			if (r != 6) 
			{
				Sys_Printf("Eclass_InitFromDLL: Unable to read size.\n");
				Sys_Printf("size: %s\n", eptr->str);
				return(eptr);
			}

			//Sys_Printf("size: %f %f %f\n", e->mins[0], e->mins[1], e->mins[2]);
			//Sys_Printf("size: %f %f %f\n", e->maxs[0], e->maxs[1], e->maxs[2]);
		}
	
		// flags are optional
		else if (!strcmp(eptr->tag, "flag"))
		{
			strcpy(e->flagnames[flagCount], eptr->str);
			flagCount++;
		}

		// comments are optional
		else if (!strcmp(eptr->tag, "rem"))
		{
			strcat(comments, eptr->str);
			strcat(comments, "\r\n");
		}

		eptr++;
	}

	// copy over comments if there were any
	if (comments[0] != 0x00)
	{
		e->comments = strdup(comments);
	}
	else
	{
		e->comments = strdup("No comments");
	}

	return(eptr);
}

///////////////////////////////////////////////////////////////////////////////
//	Eclass_ScanDLL
///////////////////////////////////////////////////////////////////////////////

void Eclass_ScanDLL (char *filename)
{
	char path[1024];
	userEclass_t *(*dll_Query)(void);
	HINSTANCE dll;
	eclass_t	  *e;
	userEclass_t  *eptr;

	sprintf(path, "%s\\%s", ValueForKey(g_qeglobals.d_project_entity, "entitypath"), filename);

	Sys_Printf("Scanfile: %s\n", path);

	// load dll
	dll = LoadLibrary(path); 

	if (dll == NULL)
	{
		Sys_Printf("WARNING -- Unable to load dll: %s.\n", path);
		return;
	}

	// if dll has an query function then call it
	dll_Query = (void *) GetProcAddress(dll, "dll_Query");

	if (!dll_Query)
	{
        FreeLibrary(dll);
		return;
	}

	eptr = dll_Query();

	// parse tags
	while (eptr && eptr->tag)
	{
		if (!strcmp(eptr->tag, "classname"))
		{
			e    = Eclass_New (eptr->str);
		    eptr = Eclass_InitFromDLL(eptr, e);

            Eclass_InsertAlphabetized(e);
		}
		else
		{
			eptr++;
		}
	}

	FreeLibrary(dll);
}

///////////////////////////////////////////////////////////////////////////////
//	Eclass_InitForSourceDirectory_DLL
///////////////////////////////////////////////////////////////////////////////

void Eclass_InitForSourceDirectory_DLL (char *path)
{
    struct _finddata_t fileinfo;
    int     handle;
//    char    filename[1024];
    char    filebase[1024];
    char    temp[1024];
//    char    *s;

    QE_ConvertDOSToUnixName( temp, path );

    Sys_Printf ("Eclass_InitForSourceDirectory_DLL: %s\n", temp );

    strcpy (filebase, path);
    strcat (filebase, "\\*.dll");
/*
    s = filebase + strlen(filebase)-1;
    while (*s != '\\' && *s != '/' && s!=filebase)
        s--;
    *s = 0;
*/
    eclass = NULL;

    handle = _findfirst (filebase, &fileinfo);
    if (handle != -1)
    {
        do
        {
            Eclass_ScanDLL(fileinfo.name);
        } while (_findnext( handle, &fileinfo ) != -1);

        _findclose (handle);
    }

    eclass_bad = Eclass_InitFromText ("/*QUAKED UNKNOWN_CLASS (0 0.5 0) ?");
}
