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

Return to Castle Wolfenstein single player GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).  

RTCW SP Source Code 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 3 of the License, or
(at your option) any later version.

RTCW SP Source Code 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 RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

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


/*****************************************************************************
 * name:		l_struct.c
 *
 * desc:		structure reading / writing
 *
 *
 *****************************************************************************/

#ifdef BOTLIB
#include "../game/q_shared.h"
#include "../game/botlib.h"              //for the include of be_interface.h
#include "l_script.h"
#include "l_precomp.h"
#include "l_struct.h"
#include "l_utils.h"
#include "be_interface.h"
#endif //BOTLIB

#ifdef BSPC
//include files for usage in the BSP Converter
#include "../bspc/qbsp.h"
#include "../bspc/l_log.h"
#include "../bspc/l_mem.h"
#include "l_precomp.h"
#include "l_struct.h"

#define qtrue   true
#define qfalse  false
#endif //BSPC

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
fielddef_t *FindField( fielddef_t *defs, char *name ) {
	int i;

	for ( i = 0; defs[i].name; i++ )
	{
		if ( !strcmp( defs[i].name, name ) ) {
			return &defs[i];
		}
	} //end for
	return NULL;
} //end of the function FindField
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
qboolean ReadNumber( source_t *source, fielddef_t *fd, void *p ) {
	token_t token;
	int negative = qfalse;
	long int intval, intmin = 0, intmax = 0;
	double floatval;

	if ( !PC_ExpectAnyToken( source, &token ) ) {
		return 0;
	}

	//check for minus sign
	if ( token.type == TT_PUNCTUATION ) {
		if ( fd->type & FT_UNSIGNED ) {
			SourceError( source, "expected unsigned value, found %s", token.string );
			return 0;
		} //end if
		  //if not a minus sign
		if ( strcmp( token.string, "-" ) ) {
			SourceError( source, "unexpected punctuation %s", token.string );
			return 0;
		} //end if
		negative = qtrue;
		//read the number
		if ( !PC_ExpectAnyToken( source, &token ) ) {
			return 0;
		}
	} //end if
	  //check if it is a number
	if ( token.type != TT_NUMBER ) {
		SourceError( source, "expected number, found %s", token.string );
		return 0;
	} //end if
	  //check for a float value
	if ( token.subtype & TT_FLOAT ) {
		if ( ( fd->type & FT_TYPE ) != FT_FLOAT ) {
			SourceError( source, "unexpected float" );
			return 0;
		} //end if
		floatval = token.floatvalue;
		if ( negative ) {
			floatval = -floatval;
		}
		if ( fd->type & FT_BOUNDED ) {
			if ( floatval < fd->floatmin || floatval > fd->floatmax ) {
				SourceError( source, "float out of range [%f, %f]", fd->floatmin, fd->floatmax );
				return 0;
			} //end if
		} //end if
		*(float *) p = (float) floatval;
		return 1;
	} //end if
	  //
	intval = token.intvalue;
	if ( negative ) {
		intval = -intval;
	}
	//check bounds
	if ( ( fd->type & FT_TYPE ) == FT_CHAR ) {
		if ( fd->type & FT_UNSIGNED ) {
			intmin = 0; intmax = 255;
		} else {intmin = -128; intmax = 127;}
	} //end if
	if ( ( fd->type & FT_TYPE ) == FT_INT ) {
		if ( fd->type & FT_UNSIGNED ) {
			intmin = 0; intmax = 65535;
		} else {intmin = -32768; intmax = 32767;}
	} //end else if
	if ( ( fd->type & FT_TYPE ) == FT_CHAR || ( fd->type & FT_TYPE ) == FT_INT ) {
		if ( fd->type & FT_BOUNDED ) {
			intmin = Maximum( intmin, fd->floatmin );
			intmax = Minimum( intmax, fd->floatmax );
		} //end if
		if ( intval < intmin || intval > intmax ) {
			SourceError( source, "value %d out of range [%d, %d]", intval, intmin, intmax );
			return 0;
		} //end if
	} //end if
	else if ( ( fd->type & FT_TYPE ) == FT_FLOAT ) {
		if ( fd->type & FT_BOUNDED ) {
			if ( intval < fd->floatmin || intval > fd->floatmax ) {
				SourceError( source, "value %d out of range [%f, %f]", intval, fd->floatmin, fd->floatmax );
				return 0;
			} //end if
		} //end if
	} //end else if
	  //store the value
	if ( ( fd->type & FT_TYPE ) == FT_CHAR ) {
		if ( fd->type & FT_UNSIGNED ) {
			*(unsigned char *) p = (unsigned char) intval;
		} else { *(char *) p = (char) intval;}
	} //end if
	else if ( ( fd->type & FT_TYPE ) == FT_INT ) {
		if ( fd->type & FT_UNSIGNED ) {
			*(unsigned int *) p = (unsigned int) intval;
		} else { *(int *) p = (int) intval;}
	} //end else
	else if ( ( fd->type & FT_TYPE ) == FT_FLOAT ) {
		*(float *) p = (float) intval;
	} //end else
	return 1;
} //end of the function ReadNumber
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
qboolean ReadChar( source_t *source, fielddef_t *fd, void *p ) {
	token_t token;

	if ( !PC_ExpectAnyToken( source, &token ) ) {
		return 0;
	}

	//take literals into account
	if ( token.type == TT_LITERAL ) {
		StripSingleQuotes( token.string );
		*(char *) p = token.string[0];
	} //end if
	else
	{
		PC_UnreadLastToken( source );
		if ( !ReadNumber( source, fd, p ) ) {
			return 0;
		}
	} //end if
	return 1;
} //end of the function ReadChar
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int ReadString( source_t *source, fielddef_t *fd, void *p ) {
	token_t token;

	if ( !PC_ExpectTokenType( source, TT_STRING, 0, &token ) ) {
		return 0;
	}
	//remove the double quotes
	StripDoubleQuotes( token.string );
	//copy the string
	strncpy( (char *) p, token.string, MAX_STRINGFIELD );
	//make sure the string is closed with a zero
	( (char *)p )[MAX_STRINGFIELD - 1] = '\0';
	//
	return 1;
} //end of the function ReadString
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int ReadStructure( source_t *source, structdef_t *def, char *structure ) {
	token_t token;
	fielddef_t *fd;
	void *p;
	int num;

	if ( !PC_ExpectTokenString( source, "{" ) ) {
		return 0;
	}
	while ( 1 )
	{
		if ( !PC_ExpectAnyToken( source, &token ) ) {
			return qfalse;
		}
		//if end of structure
		if ( !strcmp( token.string, "}" ) ) {
			break;
		}
		//find the field with the name
		fd = FindField( def->fields, token.string );
		if ( !fd ) {
			SourceError( source, "unknown structure field %s", token.string );
			return qfalse;
		} //end if
		if ( fd->type & FT_ARRAY ) {
			num = fd->maxarray;
			if ( !PC_ExpectTokenString( source, "{" ) ) {
				return qfalse;
			}
		} //end if
		else
		{
			num = 1;
		} //end else
		p = ( void * )( structure + fd->offset );
		while ( num-- > 0 )
		{
			if ( fd->type & FT_ARRAY ) {
				if ( PC_CheckTokenString( source, "}" ) ) {
					break;
				}
			} //end if
			switch ( fd->type & FT_TYPE )
			{
			case FT_CHAR:
			{
				if ( !ReadChar( source, fd, p ) ) {
					return qfalse;
				}
				p = (char *) p + sizeof( char );
				break;
			}     //end case
			case FT_INT:
			{
				if ( !ReadNumber( source, fd, p ) ) {
					return qfalse;
				}
				p = (char *) p + sizeof( int );
				break;
			}     //end case
			case FT_FLOAT:
			{
				if ( !ReadNumber( source, fd, p ) ) {
					return qfalse;
				}
				p = (char *) p + sizeof( float );
				break;
			}     //end case
			case FT_STRING:
			{
				if ( !ReadString( source, fd, p ) ) {
					return qfalse;
				}
				p = (char *) p + MAX_STRINGFIELD;
				break;
			}     //end case
			case FT_STRUCT:
			{
				if ( !fd->substruct ) {
					SourceError( source, "BUG: no sub structure defined" );
					return qfalse;
				}     //end if
				ReadStructure( source, fd->substruct, (char *) p );
				p = (char *) p + fd->substruct->size;
				break;
			}     //end case
			} //end switch
			if ( fd->type & FT_ARRAY ) {
				if ( !PC_ExpectAnyToken( source, &token ) ) {
					return qfalse;
				}
				if ( !strcmp( token.string, "}" ) ) {
					break;
				}
				if ( strcmp( token.string, "," ) ) {
					SourceError( source, "expected a comma, found %s", token.string );
					return qfalse;
				} //end if
			} //end if
		} //end while
	} //end while
	return qtrue;
} //end of the function ReadStructure
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int WriteIndent( FILE *fp, int indent ) {
	while ( indent-- > 0 )
	{
		if ( fprintf( fp, "\t" ) < 0 ) {
			return qfalse;
		}
	} //end while
	return qtrue;
} //end of the function WriteIndent
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int WriteFloat( FILE *fp, float value ) {
	char buf[128];
	int l;

	sprintf( buf, "%f", value );
	l = strlen( buf );
	//strip any trailing zeros
	while ( l-- > 1 )
	{
		if ( buf[l] != '0' && buf[l] != '.' ) {
			break;
		}
		if ( buf[l] == '.' ) {
			buf[l] = 0;
			break;
		} //end if
		buf[l] = 0;
	} //end while
	  //write the float to file
	if ( fprintf( fp, "%s", buf ) < 0 ) {
		return 0;
	}
	return 1;
} //end of the function WriteFloat
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int WriteStructWithIndent( FILE *fp, structdef_t *def, char *structure, int indent ) {
	int i, num;
	void *p;
	fielddef_t *fd;

	if ( !WriteIndent( fp, indent ) ) {
		return qfalse;
	}
	if ( fprintf( fp, "{\r\n" ) < 0 ) {
		return qfalse;
	}

	indent++;
	for ( i = 0; def->fields[i].name; i++ )
	{
		fd = &def->fields[i];
		if ( !WriteIndent( fp, indent ) ) {
			return qfalse;
		}
		if ( fprintf( fp, "%s\t", fd->name ) < 0 ) {
			return qfalse;
		}
		p = ( void * )( structure + fd->offset );
		if ( fd->type & FT_ARRAY ) {
			num = fd->maxarray;
			if ( fprintf( fp, "{" ) < 0 ) {
				return qfalse;
			}
		} //end if
		else
		{
			num = 1;
		} //end else
		while ( num-- > 0 )
		{
			switch ( fd->type & FT_TYPE )
			{
			case FT_CHAR:
			{
				if ( fprintf( fp, "%d", *(char *) p ) < 0 ) {
					return qfalse;
				}
				p = (char *) p + sizeof( char );
				break;
			}     //end case
			case FT_INT:
			{
				if ( fprintf( fp, "%d", *(int *) p ) < 0 ) {
					return qfalse;
				}
				p = (char *) p + sizeof( int );
				break;
			}     //end case
			case FT_FLOAT:
			{
				if ( !WriteFloat( fp, *(float *)p ) ) {
					return qfalse;
				}
				p = (char *) p + sizeof( float );
				break;
			}     //end case
			case FT_STRING:
			{
				if ( fprintf( fp, "\"%s\"", (char *) p ) < 0 ) {
					return qfalse;
				}
				p = (char *) p + MAX_STRINGFIELD;
				break;
			}     //end case
			case FT_STRUCT:
			{
				if ( !WriteStructWithIndent( fp, fd->substruct, structure, indent ) ) {
					return qfalse;
				}
				p = (char *) p + fd->substruct->size;
				break;
			}     //end case
			} //end switch
			if ( fd->type & FT_ARRAY ) {
				if ( num > 0 ) {
					if ( fprintf( fp, "," ) < 0 ) {
						return qfalse;
					}
				} //end if
				else
				{
					if ( fprintf( fp, "}" ) < 0 ) {
						return qfalse;
					}
				} //end else
			} //end if
		} //end while
		if ( fprintf( fp, "\r\n" ) < 0 ) {
			return qfalse;
		}
	} //end for
	indent--;

	if ( !WriteIndent( fp, indent ) ) {
		return qfalse;
	}
	if ( fprintf( fp, "}\r\n" ) < 0 ) {
		return qfalse;
	}
	return qtrue;
} //end of the function WriteStructWithIndent
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int WriteStructure( FILE *fp, structdef_t *def, char *structure ) {
	return WriteStructWithIndent( fp, def, structure, 0 );
} //end of the function WriteStructure

