/*
Copyright (C) 1997-2001 Id Software, Inc.

This program 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.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
--------------------------------------------------------------
The ACE Bot is a product of Steve Yeager, and is available from
the ACE Bot homepage, at http://www.axionfx.com/ace.

This program is a modification of the ACE Bot, and is therefore
in NO WAY supported by Steve Yeager.
*/

#include "../g_local.h"
#include "ai_local.h"
#include "g_fakeclient.h"



//===============================================================
//
//				BOT SPAWN
//
//===============================================================


//------------------------------------------------------------
//freedom to add as many skins & names as wished, but be
//careful. LocalBotSkins & LocalBotNames MUST fit each other.
//jalfixme: code a botlist.cfg to be read from disk?
//------------------------------------------------------------
/*
static char	*LocalBotSkins[] = 
{
	"major/daemia",
	"major/default",
	"xaero/default",
	"lucy/default",
	"hunter/default",
	"hunter/harpy",
	"grunt/stripe",
	"grunt/default",
	"razor/default", 
	"razor/id",
	"ranger/default",
	"ranger/wrack",
	"visor/default",
	"visor/gorre",
	"sarge/default",
	"sarge/roderic",
	"doom/default",
	
	NULL
};*/

 // JALFIXME : we only have 1 skin now, so I use invalid names so the randomizer check works
// and force the default one later on
static char	*LocalBotSkins[] = 
{
	"viciious/default",
	"viciious/default2",
	"viciious/default3",
	"viciious/default4",
	"viciious/default5",
	"monada/default",
	"monada/default1",
	"monada/default2",
	"monada/default3",
	"monada/default4",
	"silverclaw/default5",
	"silverclaw/default1",
	"silverclaw/default2",
	"silverclaw/default3",
	"silverclaw/default4",
	
	NULL
};

static char	*LocalBotNames[] = 
{
	"Viciious", 
	"Sid",
	"Pervert", 
	"Sick", 
	"Punk",
	"Blask Sister", 
	"Monada",
	"Afrodita", 
	"Goddess", 
	"Athena",
	"Silver", 
	"Cathy",
	"MishiMishi", 
	"Lobita", 
	"SisterClaw",
	
	NULL
};

//------------------------------------------------------------
/*
static char	*LocalBotNames[] = 
{
	"Daemia", 
	"Major",
	"Xaero", 
	"Lucy", 
	"Hunter",
	"Harpy",
	"Stripe",
	"Grunt",
	"Razor", 
	"id",
	"Ranger",
	"Wrack",
	"Visor",
	"Gorre",
	"Sarge",
	"Roderic", 
	"Doom",
	
	NULL
};
*/
//------------------------------------------------------------

typedef struct
{
	char	bot_model[MAX_INFO_STRING];
	char	bot_skin[MAX_INFO_STRING];
	char	bot_name[MAX_INFO_STRING];
} localbotskin_t;


//==========================================
// BOT_GetUnusedSkin
// Retrieve a random unused skin & name
//==========================================
qboolean BOT_GetUnusedSkin( char *bot_model, char *bot_skin, char *bot_name )
{
	qboolean		inuse;
	int				skinnumber;
	char			scratch[MAX_INFO_STRING];
	int				i, freeskins;
	edict_t			*ent;
	localbotskin_t	*botskins;
	localbotskin_t	*localbotskin;

	//count the unused skins, and make sure there is at least 1 of them
	skinnumber = freeskins = 0;
	while( LocalBotSkins[skinnumber] != NULL )
	{
		inuse = qfalse;
		for( i = 0, ent = game.edicts + 1; i < game.maxclients; i++, ent++ )
		{
			if (!(ent->r.svflags & SVF_FAKECLIENT) || !ent->r.client)
				continue;
			
			//scratch = Info_ValueForKey (ent->r.client->pers.userinfo, "skin");
			Q_snprintfz( scratch, sizeof(scratch), "%s/%s", Info_ValueForKey(ent->r.client->pers.userinfo, "model"), Info_ValueForKey(ent->r.client->pers.userinfo, "skin") );
			
			if( !Q_stricmp( scratch, LocalBotSkins[skinnumber]) )
			{
				inuse = qtrue;
				break;
			}
		}
		if( inuse == qfalse )
			freeskins++;

		skinnumber++;
	}

	//fallback to old method
	if (!freeskins)
		return qfalse;

	//assign tmp memory for storing unused skins
	botskins = G_Malloc( sizeof(localbotskin_t) * freeskins );
	
	//create a list of unused skins
	skinnumber = freeskins = 0;
	while( LocalBotSkins[skinnumber] != NULL )
	{
		inuse = qfalse;
		for( i = 0, ent = game.edicts + 1; i < game.maxclients; i++, ent++ )
		{
			if( !(ent->r.svflags & SVF_FAKECLIENT) || !ent->r.client )
				continue;
			
			//scratch = Info_ValueForKey (ent->r.client->pers.userinfo, "skin");
			Q_snprintfz( scratch, sizeof(scratch), "%s/%s", Info_ValueForKey(ent->r.client->pers.userinfo, "model"), Info_ValueForKey(ent->r.client->pers.userinfo, "skin") );
			
			if( !Q_stricmp( scratch, LocalBotSkins[skinnumber]) )
			{
				inuse = qtrue;
				break;
			}	
		}
		//store and advance
		if (inuse == qfalse)
		{
			char *p;
			localbotskin = botskins + freeskins;

			p = strstr( LocalBotSkins[skinnumber], "/" );
			if( !strlen(p) )
				continue;
			p++;

			Q_strncpyz( localbotskin->bot_model, LocalBotSkins[skinnumber], strlen(LocalBotSkins[skinnumber]) - strlen(p) );
			Q_strncpyz( localbotskin->bot_skin, p, sizeof(localbotskin->bot_skin) );
			Q_strncpyz( localbotskin->bot_name, LocalBotNames[skinnumber], sizeof(localbotskin->bot_name) );

			if( AIDevel.debugMode )
				Com_Printf( "Free skin: %i: %s %s\n", freeskins, localbotskin->bot_skin, localbotskin->bot_name );
			
			freeskins++;
		}

		skinnumber++;
	}

	//now get a random skin from the list
	skinnumber = (int)(random()*freeskins);
	localbotskin = botskins + skinnumber;
	Q_strncpyz( bot_model, localbotskin->bot_model, sizeof(localbotskin->bot_model) );
	Q_strncpyz( bot_skin, localbotskin->bot_skin, sizeof(localbotskin->bot_skin) );
	Q_strncpyz( bot_name, localbotskin->bot_name, sizeof(localbotskin->bot_name) );

	if( AIDevel.debugMode )
		Com_Printf( "Assigned bot character: %i: %s %s %s\n", skinnumber, bot_model, bot_skin, bot_name );

	G_Free( botskins );

	return qtrue;
}


//==========================================
// BOT_CreateUserinfo
// Creates UserInfo string to connect with
//==========================================
void BOT_CreateUserinfo( char *userinfo )
{
	char	bot_skin[MAX_INFO_STRING];
	char	bot_name[MAX_INFO_STRING];
	char	bot_model[MAX_INFO_STRING];

	//jalfixme: we have only one skin yet

	//GetUnusedSkin doesn't repeat already used skins/names
	if( !BOT_GetUnusedSkin( bot_model, bot_skin, bot_name ) )
	{
		int		i, botcount = 0;
		edict_t	*ent;
		
		//count spawned bots for the names
		for (i = 0, ent = game.edicts + 1; i < game.maxclients; i++, ent++) {
			if( !ent->r.inuse || !ent->ai.type ) continue;
			if( ent->r.svflags & SVF_FAKECLIENT && ent->ai.type == AI_ISBOT )
				botcount++;
		}
		
		// Set the name for the bot.
		Q_snprintfz( bot_name, sizeof(bot_name), "Bot%d", botcount+1 );
		
		// randomly choose skin
		if( random() > 0.66f ) {
			Q_snprintfz( bot_model, sizeof(bot_model), "viciious" );
		} 
		else if( random() > 0.33f ) {
			Q_snprintfz( bot_model, sizeof(bot_model), "silverclaw" );
		}
		else {
			Q_snprintfz( bot_model, sizeof(bot_model), "monada" );
		}

		Q_snprintfz( bot_skin, sizeof(bot_skin), "default" );
		
	}
	
	// initialize userinfo
	memset( userinfo, 0, sizeof(userinfo) );
	
	// add bot's name/skin/hand to userinfo
	Info_SetValueForKey( userinfo, "name", bot_name );
	Info_SetValueForKey( userinfo, "model", bot_model );
	//Info_SetValueForKey( userinfo, "skin", bot_skin );
	Info_SetValueForKey( userinfo, "skin", "default" ); // JALFIXME
	Info_SetValueForKey( userinfo, "hand", "2" );
	Info_SetValueForKey( userinfo, "color", va( "%i %i %i", (qbyte)(random()*255), (qbyte)(random()*255), (qbyte)(random()*255) ) ); 
}



//==========================================
// BOT_NextCTFTeam
// Get the emptier CTF team
//==========================================
/*
int	BOT_NextCTFTeam()
{
	int	i;
	int	onteam1 = 0;
	int	onteam2 = 0;
	edict_t		*self;

	// Only use in CTF games
	if (!ctf->integer)
		return 0;

	for (i = 0; i < game.maxclients + 1; i++)
	{
		self = game.edicts +i + 1;
		if (self->r.inuse && self->r.client)
		{
			if (self->r.client->resp.ctf_team == CTF_TEAM1)
				onteam1++;
			else if (self->r.client->resp.ctf_team == CTF_TEAM2)
				onteam2++;
		}
	}

	if (onteam1 > onteam2)
		return (2);
	else if (onteam2 >= onteam1)
		return (1);

	return (1);
}*/

//==========================================
// BOT_JoinCTFTeam
// Assign a team for the bot
//==========================================
/*
qboolean BOT_JoinCTFTeam (edict_t *ent, char *team_name)
{
	char	*s;
	int		team;
	edict_t	*event;


	if (ent->r.client->resp.ctf_team != CTF_NOTEAM)
		return qfalse;
	
	// find what ctf team
	if ((team_name !=NULL) && (strcmp(team_name, "blue") == 0))
		team = CTF_TEAM2;
	else if ((team_name !=NULL) && (strcmp(team_name, "red") == 0))
		team = CTF_TEAM1;
	else
		team = BOT_NextCTFTeam();

	if (team == CTF_NOTEAM)
		return qfalse;
	
	//join ctf team
	ent->r.svflags &= ~SVF_NOCLIENT;
	ent->r.client->resp.ctf_state = 1;//0?
	ent->r.client->resp.ctf_team = team;
	s = Info_ValueForKey (ent->r.client->pers.userinfo, "skin");
	CTFAssignSkin(ent, s);

	PutClientInServer (ent);

	G_AddEvent (ent, EV_TELEPORT, 0, qtrue);

	// add a teleportation effect
	event = G_SpawnEvent ( EV_PLAYER_TELEPORT_IN, 0, ent->s.origin );
	event->r.svflags = SVF_NOOLDORIGIN;
	event->s.ownerNum = ent - game.edicts;

	// hold in place briefly
	ent->r.client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
	ent->r.client->ps.pmove.pm_time = 14;

	G_PrintMsg (NULL, "%s%s joined the %s team.\n",
		ent->r.client->pers.netname, S_COLOR_WHITE, CTFTeamName(ent->r.client->resp.ctf_team));

	return qtrue;
}*/

/*
int G_TeamFromTeamName( char *teamname );
//==========================================
// ACESP_BotJoinGame
// the bot connected as spectator
//==========================================
void ACESP_BotJoinGame (edict_t *ent, char *team_name)
{
	//if ( BOT_JoinCTFTeam(ent, team_name) )
	//	return;

	if( team_name && team_name[0] )
		G_Teams_JoinTeam( ent, G_TeamFromTeamName( team_name ) );
	if( !ent->s.team )
		G_Teams_JoinAnyTeam( ent, qfalse );

	//respawn(ent);

	G_PrintMsg (NULL, "%s%s joined the game.\n",
		ent->r.client->pers.netname, S_COLOR_WHITE);
}*/


//==========================================
// BOT_Respawn
// Set up bot for Spawn. Called at first spawn & each respawn
//==========================================
void BOT_Respawn(edict_t *self)
{
	self->enemy = NULL;
	self->movetarget = NULL;

	AI_ResetWeights(self);
	AI_ResetNavigation(self);
}

//==========================================
// BOT_InitPersistant
// Persistant after respawns. To be class definition in the future
//==========================================
void BOT_InitPersistant( edict_t *self )
{
	float	sv_skill;

	//standard stuff
	self->think = AI_Think;
	self->nextthink = level.timemsec + game.framemsec;
	self->ai.type = AI_ISBOT;
	self->classname = "bot";
	self->yaw_speed = AI_DEFAULT_YAW_SPEED;
	self->die = player_die;

	// set skill based on sv_skilllevel cvar
	sv_skill = trap_Cvar_VariableValue( "sv_skilllevel" ); // 0 = easy, 2 = hard
	sv_skill += random();// so we have a float between 0 and 3 meaning the server skill
	if( !sv_skill ) sv_skill = 0.001f;
	self->ai.pers.skillLevel = sv_skill/3.0f; // the same being a fraction of 1.
	if( self->ai.pers.skillLevel < 0.1f ) self->ai.pers.skillLevel = 0.1f;
	
	self->yaw_speed -= 20 * (1.0f - self->ai.pers.skillLevel);

	//name
	if (self->r.client->pers.netname)
		self->ai.pers.netname = self->r.client->pers.netname;
	else
		self->ai.pers.netname = "SomeBot";

	//class: always set up default first 
	BOT_DMclass_InitPersistant(self);
}

//==========================================
// BOT_DoSpawnBot
// Spawn the bot
//==========================================
void BOT_DoSpawnBot( void )
{
	char			userinfo[MAX_INFO_STRING];
	fakeclient_t	*fakeClient;
	edict_t			*ent;

	if( !nav.loaded ) {
		Com_Printf( "AI: Can't spawn bots without a valid navigation file\n" );
		if( g_numbots->integer )trap_Cvar_Set( "g_numbots", "0" );
		return;
	}

	BOT_CreateUserinfo( userinfo );

	fakeClient = G_SpawnFakeClient( userinfo, NULL );
	if( !fakeClient || !fakeClient->ent )
		return;

	ent = fakeClient->ent;
	G_SpawnAI(ent); //jabot092(2)

	//finish fakeclient initialization
	fakeClient->state = FAKECLIENT_STATE_INUSE;
	fakeClient->respawn = BOT_Respawn;

	//init this bot
	BOT_InitPersistant( ent );

	//set up for Spawn
	BOT_Respawn( ent );

	//stay as spectator, give random time for joining
	ent->nextthink = level.timemsec + random() * 8000;
}

//==========================================
// BOT_SpawnerThink
// Call the real bot spawning function
//==========================================
void BOT_SpawnerThink( edict_t *spawner )
{
	BOT_DoSpawnBot();
	G_FreeEdict( spawner );
}

//==========================================
// BOT_SpawnBot
// Used Spawn the bot
//==========================================
void BOT_SpawnBot( char *team_name )
{
	edict_t *spawner;
	int		team;

	if( !nav.loaded ) {
		Com_Printf( "AI: Can't spawn bots without a valid navigation file\n" );
		if( g_numbots->integer )trap_Cvar_Set( "g_numbots", "0" );
		return;
	}
	
	// create a entity which will call the bot spawn
	spawner = G_Spawn();
	spawner->think = BOT_SpawnerThink;

	team = GS_Teams_TeamFromName( team_name );
	if( team != -1 )
		spawner->s.team = team;

	spawner->nextthink = level.timemsec + random() * 3000;
	spawner->movetype = MOVETYPE_NONE;
	spawner->r.solid = SOLID_NOT;
	spawner->r.svflags |= SVF_NOCLIENT;
	trap_LinkEntity( spawner );

	game.numBots++;
}


//==========================================
//	BOT_RemoveBot
//	Remove a bot by name or all bots
//==========================================
void BOT_RemoveBot(char *name)
{
	int			i;
	qboolean	freed=qfalse;
	edict_t		*ent;

	for( i=0, ent = game.edicts + 1; i < game.maxclients; i++, ent++ )
	{
		if( !ent->r.inuse || ent->ai.type != AI_ISBOT )
			continue;

		if( !Q_stricmp(ent->r.client->pers.netname,name) || !Q_stricmp(name,"all") )
		{
			trap_DropClient(ent, DROP_TYPE_GENERAL, "BOT_RemoveBot");
			freed = qtrue;
		}
	}

	if( !freed && Q_stricmp(name,"all") )
		G_Printf( "BOT: %s not found\n", name );
}

