/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena 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 2 of the License,
or (at your option) any later version.

Quake III Arena 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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
#include <float.h>

#include "qbsp.h"
#include "../botlib/aasfile.h"
#include "aas_store.h"
#include "aas_cfg.h"
#include "../botlib/l_precomp.h"
#include "../botlib/l_struct.h"
#include "../botlib/l_libvar.h"

#include <stddef.h>

//structure field offsets
#define BBOX_OFS(x) offsetof(aas_bbox_t, x)
#define CFG_OFS(x) offsetof(cfg_t, x)

//bounding box definition
fielddef_t bbox_fields[] =
{
	{"presencetype", BBOX_OFS(presencetype), FT_INT,            0, 0, 0, NULL},
	{"flags",        BBOX_OFS(flags),        FT_INT,            0, 0, 0, NULL},
	{"mins",         BBOX_OFS(mins),         FT_FLOAT|FT_ARRAY, 3, 0, 0, NULL},
	{"maxs",         BBOX_OFS(maxs),         FT_FLOAT|FT_ARRAY, 3, 0, 0, NULL},
	{NULL,           0,                      0,                 0, 0, 0, NULL}
};

fielddef_t cfg_fields[] =
{
	{"phys_gravitydirection",   CFG_OFS(phys_gravitydirection),   FT_FLOAT|FT_ARRAY, 3, 0, 0, NULL},
	{"phys_friction",           CFG_OFS(phys_friction),           FT_FLOAT,          0, 0, 0, NULL},
	{"phys_stopspeed",          CFG_OFS(phys_stopspeed),          FT_FLOAT,          0, 0, 0, NULL},
	{"phys_gravity",            CFG_OFS(phys_gravity),            FT_FLOAT,          0, 0, 0, NULL},
	{"phys_waterfriction",      CFG_OFS(phys_waterfriction),      FT_FLOAT,          0, 0, 0, NULL},
	{"phys_watergravity",       CFG_OFS(phys_watergravity),       FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxvelocity",        CFG_OFS(phys_maxvelocity),        FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxwalkvelocity",    CFG_OFS(phys_maxwalkvelocity),    FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxcrouchvelocity",  CFG_OFS(phys_maxcrouchvelocity),  FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxswimvelocity",    CFG_OFS(phys_maxswimvelocity),    FT_FLOAT,          0, 0, 0, NULL},
	{"phys_walkaccelerate",     CFG_OFS(phys_walkaccelerate),     FT_FLOAT,          0, 0, 0, NULL},
	{"phys_airaccelerate",      CFG_OFS(phys_airaccelerate),      FT_FLOAT,          0, 0, 0, NULL},
	{"phys_swimaccelerate",     CFG_OFS(phys_swimaccelerate),     FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxstep",            CFG_OFS(phys_maxstep),            FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxsteepness",       CFG_OFS(phys_maxsteepness),       FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxwaterjump",       CFG_OFS(phys_maxwaterjump),       FT_FLOAT,          0, 0, 0, NULL},
	{"phys_maxbarrier",         CFG_OFS(phys_maxbarrier),         FT_FLOAT,          0, 0, 0, NULL},
	{"phys_jumpvel",            CFG_OFS(phys_jumpvel),            FT_FLOAT,          0, 0, 0, NULL},
	{"phys_falldelta5",         CFG_OFS(phys_falldelta5),         FT_FLOAT,          0, 0, 0, NULL},
	{"phys_falldelta10",        CFG_OFS(phys_falldelta10),        FT_FLOAT,          0, 0, 0, NULL},
	{"rs_waterjump",            CFG_OFS(rs_waterjump),            FT_FLOAT,          0, 0, 0, NULL},
	{"rs_teleport",             CFG_OFS(rs_teleport),             FT_FLOAT,          0, 0, 0, NULL},
	{"rs_barrierjump",          CFG_OFS(rs_barrierjump),          FT_FLOAT,          0, 0, 0, NULL},
	{"rs_startcrouch",          CFG_OFS(rs_startcrouch),          FT_FLOAT,          0, 0, 0, NULL},
	{"rs_startgrapple",         CFG_OFS(rs_startgrapple),         FT_FLOAT,          0, 0, 0, NULL},
	{"rs_startwalkoffledge",    CFG_OFS(rs_startwalkoffledge),    FT_FLOAT,          0, 0, 0, NULL},
	{"rs_startjump",            CFG_OFS(rs_startjump),            FT_FLOAT,          0, 0, 0, NULL},
	{"rs_rocketjump",           CFG_OFS(rs_rocketjump),           FT_FLOAT,          0, 0, 0, NULL},
	{"rs_bfgjump",              CFG_OFS(rs_bfgjump),              FT_FLOAT,          0, 0, 0, NULL},
	{"rs_jumppad",              CFG_OFS(rs_jumppad),              FT_FLOAT,          0, 0, 0, NULL},
	{"rs_aircontrolledjumppad", CFG_OFS(rs_aircontrolledjumppad), FT_FLOAT,          0, 0, 0, NULL},
	{"rs_funcbob",              CFG_OFS(rs_funcbob),              FT_FLOAT,          0, 0, 0, NULL},
	{"rs_startelevator",        CFG_OFS(rs_startelevator),        FT_FLOAT,          0, 0, 0, NULL},
	{"rs_falldamage5",          CFG_OFS(rs_falldamage5),          FT_FLOAT,          0, 0, 0, NULL},
	{"rs_falldamage10",         CFG_OFS(rs_falldamage10),         FT_FLOAT,          0, 0, 0, NULL},
	{"rs_maxfallheight",        CFG_OFS(rs_maxfallheight),        FT_FLOAT,          0, 0, 0, NULL},
	{"rs_maxjumpfallheight",    CFG_OFS(rs_maxjumpfallheight),    FT_FLOAT,          0, 0, 0, NULL},
	{NULL,                      0,                                0,                 0, 0, 0, NULL}
};

structdef_t bbox_struct =
{
	sizeof(aas_bbox_t), bbox_fields
};
structdef_t cfg_struct =
{
	sizeof(cfg_t), cfg_fields
};

//global cfg
cfg_t cfg;

//===========================================================================
// the default Q3A configuration
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void DefaultCfg(void)
{
	int i;

	// default all float values to infinite
	for (i = 0; cfg_fields[i].name; i++)
	{
		if ((cfg_fields[i].type & FT_TYPE) == FT_FLOAT)
			*(float *)( ((char*)&cfg) + cfg_fields[i].offset ) = FLT_MAX;
	} //end for
	//
	cfg.numbboxes = 2;
	//bbox 0
	cfg.bboxes[0].presencetype = PRESENCE_NORMAL;
	cfg.bboxes[0].flags = 0;
	cfg.bboxes[0].mins[0] = -15;
	cfg.bboxes[0].mins[1] = -15;
	cfg.bboxes[0].mins[2] = -24;
	cfg.bboxes[0].maxs[0] = 15;
	cfg.bboxes[0].maxs[1] = 15;
	cfg.bboxes[0].maxs[2] = 32;
	//bbox 1
	cfg.bboxes[1].presencetype = PRESENCE_CROUCH;
	cfg.bboxes[1].flags = 1;
	cfg.bboxes[1].mins[0] = -15;
	cfg.bboxes[1].mins[1] = -15;
	cfg.bboxes[1].mins[2] = -24;
	cfg.bboxes[1].maxs[0] = 15;
	cfg.bboxes[1].maxs[1] = 15;
	cfg.bboxes[1].maxs[2] = 16;
	//
	cfg.allpresencetypes = PRESENCE_NORMAL|PRESENCE_CROUCH;
	cfg.phys_gravitydirection[0]	= 0;
	cfg.phys_gravitydirection[1]	= 0;
	cfg.phys_gravitydirection[2]	= -1;
	cfg.phys_maxsteepness			= 0.7;
} //end of the function DefaultCfg
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
char	* QDECL va( char *format, ... )
{
	va_list		argptr;
	static char		string[2][32000];	// in case va is called by nested functions
	static int		index = 0;
	char	*buf;

	buf = string[index & 1];
	index++;

	va_start (argptr, format);
	vsprintf (buf, format,argptr);
	va_end (argptr);

	return buf;
} //end of the function va
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
void SetCfgLibVars(void)
{
	int i;
	float value;

	for (i = 0; cfg_fields[i].name; i++)
	{
		if ((cfg_fields[i].type & FT_TYPE) == FT_FLOAT)
		{
			value = *(float *)(((char*)&cfg) + cfg_fields[i].offset);
			if (value != FLT_MAX)
			{
				LibVarSet(cfg_fields[i].name, va("%f", value));
			} //end if
		} //end if
	} //end for
} //end of the function SetCfgLibVars
//===========================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//===========================================================================
int LoadCfgFile(char *filename)
{
	source_t *source;
	token_t token;
	int settingsdefined;

	source = LoadSourceFile(filename);
	if (!source)
	{
		Log_Print("couldn't open cfg file %s\n", filename);
		return false;
	} //end if

	settingsdefined = false;
	memset(&cfg, 0, sizeof(cfg_t));

	while(PC_ReadToken(source, &token))
	{
		if (!stricmp(token.string, "bbox"))
		{
			if (cfg.numbboxes >= AAS_MAX_BBOXES)
			{
				SourceError(source, "too many bounding box volumes defined");
			} //end if
			if (!ReadStructure(source, &bbox_struct, (char *) &cfg.bboxes[cfg.numbboxes]))
			{
				FreeSource(source);
				return false;
			} //end if
			cfg.allpresencetypes |= cfg.bboxes[cfg.numbboxes].presencetype;
			cfg.numbboxes++;
		} //end if
		else if (!stricmp(token.string, "settings"))
		{
			if (settingsdefined)
			{
				SourceWarning(source, "settings already defined\n");
			} //end if
			settingsdefined = true;
			if (!ReadStructure(source, &cfg_struct, (char *) &cfg))
			{
				FreeSource(source);
				return false;
			} //end if
		} //end else if
	} //end while
	if (VectorLength(cfg.phys_gravitydirection) < 0.9 || VectorLength(cfg.phys_gravitydirection) > 1.1)
	{
		SourceError(source, "invalid gravity direction specified");
	} //end if
	if (cfg.numbboxes <= 0)
	{
		SourceError(source, "no bounding volumes specified");
	} //end if
	FreeSource(source);
	SetCfgLibVars();
	Log_Print("using cfg file %s\n", filename);
	return true;
} //end of the function LoadCfgFile
