/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.

This file is part of GtkRadiant.

GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

// scriplib.c

#include "cmdlib.h"
#include "mathlib.h"
#include "inout.h"
#include "scriplib.h"
#include "vfs.h"

/*
=============================================================================

						PARSING STUFF

=============================================================================
*/

typedef struct
{
	char	filename[1024];
	char    *buffer,*script_p,*end_p;
	int     line;
} script_t;

#define	MAX_INCLUDES	8
script_t	scriptstack[MAX_INCLUDES];
script_t	*script;
int			scriptline;

char    token[MAXTOKEN];
qboolean endofscript;
qboolean tokenready;                     // only qtrue if UnGetToken was just called

/*
==============
AddScriptToStack
==============
*/
void AddScriptToStack (const char *filename, int index)
{
  int size;

  script++;
  if (script == &scriptstack[MAX_INCLUDES])
    Error ("script file exceeded MAX_INCLUDES");
  strcpy (script->filename, ExpandPath (filename));

  size = vfsLoadFile (script->filename, (void **)&script->buffer, index);

  if (size == -1)
    Sys_Printf ("Script file %s was not found\n", script->filename);
  else
  {
    if (index > 0)
      Sys_Printf ("entering %s (%d)\n", script->filename, index+1);
    else
      Sys_Printf ("entering %s\n", script->filename);
  }

  script->line = 1;
  script->script_p = script->buffer;
  script->end_p = script->buffer + size;
}


/*
==============
LoadScriptFile
==============
*/
void LoadScriptFile (const char *filename, int index)
{
  script = scriptstack;
  AddScriptToStack (filename, index);

  endofscript = qfalse;
  tokenready = qfalse;
}


/*
==============
ParseFromMemory
==============
*/
void ParseFromMemory (char *buffer, int size)
{
	script = scriptstack;
	script++;
	if (script == &scriptstack[MAX_INCLUDES])
		Error ("script file exceeded MAX_INCLUDES");
	strcpy (script->filename, "memory buffer" );

	script->buffer = buffer;
	script->line = 1;
	script->script_p = script->buffer;
	script->end_p = script->buffer + size;

	endofscript = qfalse;
	tokenready = qfalse;
}


/*
==============
UnGetToken

Signals that the current token was not used, and should be reported
for the next GetToken.  Note that

GetToken (qtrue);
UnGetToken ();
GetToken (qfalse);

could cross a line boundary.
==============
*/
void UnGetToken (void)
{
	tokenready = qtrue;
}


qboolean EndOfScript (qboolean crossline)
{
	if (!crossline)
		Error ("Line %i is incomplete\n",scriptline);

	if (!strcmp (script->filename, "memory buffer"))
	{
		endofscript = qtrue;
		return qfalse;
	}
	
	if( script->buffer == NULL )
		Sys_Printf( "WARNING: Attempt to free already freed script buffer\n" );
	else
		free( script->buffer );
	script->buffer = NULL;
	if (script == scriptstack+1)
	{
		endofscript = qtrue;
		return qfalse;
	}
	script--;
	scriptline = script->line;
	Sys_Printf ("returning to %s\n", script->filename);
	return GetToken (crossline);
}

/*
==============
GetToken
==============
*/
qboolean GetToken (qboolean crossline)
{
	char    *token_p;

	
	/* ydnar: dummy testing */
	if( script == NULL || script->buffer == NULL )
		return qfalse;
	
	if (tokenready)                         // is a token already waiting?
	{
		tokenready = qfalse;
		return qtrue;
	}

	if ((script->script_p >= script->end_p) || (script->script_p == NULL))
          return EndOfScript (crossline);

//
// skip space
//
skipspace:
	while (*script->script_p <= 32)
	{
		if (script->script_p >= script->end_p)
			return EndOfScript (crossline);
		if (*script->script_p++ == '\n')
		{
			if (!crossline)
				Error ("Line %i is incomplete\n",scriptline);
			script->line++;
			scriptline = script->line;
		}
	}

	if (script->script_p >= script->end_p)
		return EndOfScript (crossline);

	// ; # // comments
	if (*script->script_p == ';' || *script->script_p == '#'
		|| ( script->script_p[0] == '/' && script->script_p[1] == '/') )
	{
		if (!crossline)
			Error ("Line %i is incomplete\n",scriptline);
		while (*script->script_p++ != '\n')
			if (script->script_p >= script->end_p)
				return EndOfScript (crossline);
		script->line++;
		scriptline = script->line;
		goto skipspace;
	}

	// /* */ comments
	if (script->script_p[0] == '/' && script->script_p[1] == '*')
	{
		if (!crossline)
			Error ("Line %i is incomplete\n",scriptline);
		script->script_p+=2;
		while (script->script_p[0] != '*' && script->script_p[1] != '/')
		{
			if ( *script->script_p == '\n' )
			{
				script->line++;
				scriptline = script->line;
			}
			script->script_p++;
			if (script->script_p >= script->end_p)
				return EndOfScript (crossline);
		}
		script->script_p += 2;
		goto skipspace;
	}

//
// copy token
//
	token_p = token;

	if (*script->script_p == '"')
	{
		// quoted token
		script->script_p++;
		while (*script->script_p != '"')
		{
			*token_p++ = *script->script_p++;
			if (script->script_p == script->end_p)
				break;
			if (token_p == &token[MAXTOKEN])
				Error ("Token too large on line %i\n",scriptline);
		}
		script->script_p++;
	}
	else	// regular token
	while ( *script->script_p > 32 && *script->script_p != ';')
	{
		*token_p++ = *script->script_p++;
		if (script->script_p == script->end_p)
			break;
		if (token_p == &token[MAXTOKEN])
			Error ("Token too large on line %i\n",scriptline);
	}

	*token_p = 0;

	if (!strcmp (token, "$include"))
	{
		GetToken (qfalse);
		AddScriptToStack (token, 0);
		return GetToken (crossline);
	}

	return qtrue;
}


/*
==============
TokenAvailable

Returns qtrue if there is another token on the line
==============
*/
qboolean TokenAvailable (void) {
	int		oldLine, oldScriptLine;
	qboolean	r;
	
	/* save */
	oldLine = scriptline;
	oldScriptLine = script->line;
	
	/* test */
	r = GetToken( qtrue );
	if ( !r ) {
		return qfalse;
	}
	UnGetToken();
	if ( oldLine == scriptline ) {
		return qtrue;
	}
	
	/* restore */
	//%	scriptline = oldLine;
	//%	script->line = oldScriptLine;

	return qfalse;
}


//=====================================================================


void MatchToken( char *match ) {
	GetToken( qtrue );

	if ( strcmp( token, match ) ) {
		Error( "MatchToken( \"%s\" ) failed at line %i in file %s", match, scriptline, script->filename);
	}
}


void Parse1DMatrix (int x, vec_t *m) {
	int		i;

	MatchToken( "(" );

	for (i = 0 ; i < x ; i++) {
		GetToken( qfalse );
		m[i] = atof(token);
	}

	MatchToken( ")" );
}

void Parse2DMatrix (int y, int x, vec_t *m) {
	int		i;

	MatchToken( "(" );

	for (i = 0 ; i < y ; i++) {
		Parse1DMatrix (x, m + i * x);
	}

	MatchToken( ")" );
}

void Parse3DMatrix (int z, int y, int x, vec_t *m) {
	int		i;

	MatchToken( "(" );

	for (i = 0 ; i < z ; i++) {
		Parse2DMatrix (y, x, m + i * x*y);
	}

	MatchToken( ")" );
}


void Write1DMatrix (FILE *f, int x, vec_t *m) {
	int		i;

	fprintf (f, "( ");
	for (i = 0 ; i < x ; i++) {
		if (m[i] == (int)m[i] ) {
			fprintf (f, "%i ", (int)m[i]);
		} else {
			fprintf (f, "%f ", m[i]);
		}
	}
	fprintf (f, ")");
}

void Write2DMatrix (FILE *f, int y, int x, vec_t *m) {
	int		i;

	fprintf (f, "( ");
	for (i = 0 ; i < y ; i++) {
		Write1DMatrix (f, x, m + i*x);
		fprintf (f, " ");
	}
	fprintf (f, ")\n");
}


void Write3DMatrix (FILE *f, int z, int y, int x, vec_t *m) {
	int		i;

	fprintf (f, "(\n");
	for (i = 0 ; i < z ; i++) {
		Write2DMatrix (f, y, x, m + i*(x*y) );
	}
	fprintf (f, ")\n");
}

