#include <math.h>
#include <stdlib.h>
#include <string.h>

#define CVAR_IMPLEMENTOR 1

#include "doomtype.h"
#include "doomdef.h"
#include "doomstat.h"
#include "d_netinf.h"
#include "d_net.h"
#include "d_protocol.h"
#include "v_palette.h"
#include "v_video.h"
#include "i_system.h"
#include "r_draw.h"
#include "r_state.h"
#include "st_stuff.h"
#include "p_local.h"

extern BOOL st_firsttime;

CVAR (autoaim,				"5000",		CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (name,					"Player",	CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (color,				"40 cf 00",	CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (skin,					"base",		CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (team,					"",			CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (gender,				"male",		CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (neverswitchonpickup,	"0",		CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (rocketpreferred,		"0",		CVAR_USERINFO | CVAR_ARCHIVE)	// [BC] New userinfo
CVAR (plasmapreferred,		"0",		CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (teamname,				"Blue",		CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (teamcolor,			"40 00 cf",	CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (railcolor,			"0",		CVAR_USERINFO | CVAR_ARCHIVE)
CVAR (expert,				"0",		CVAR_USERINFO | CVAR_ARCHIVE)

enum
{
	INFO_Name,
	INFO_Autoaim,
	INFO_Color,
	INFO_Skin,
	INFO_Team,
	INFO_Gender,
	INFO_NeverSwitchOnPickup,
	INFO_RocketPreferred,	// [BC] New stuff
	INFO_PlasmaPreferred,
	INFO_Teamname,
	INFO_Teamcolor,
	INFO_Railcolor,
	INFO_Expert
};

static const char *UserInfoStrings[] =
{
	"name",
	"autoaim",
	"color",
	"skin",
	"team",
	"gender",
	"neverswitchonpickup",
	"rocketpreferred",	// [BC]
	"plasmapreferred",
	"teamname",
	"teamcolor",
	"railcolor",
	"expert",
	NULL
};

int D_GenderToInt (const char *gender)
{
	if (!stricmp (gender, "female"))
		return GENDER_FEMALE;
	else if (!stricmp (gender, "cyborg"))
		return GENDER_NEUTER;
	else
		// [BC] Male is the default?! How sexist!!! Heh, kidding.
		return GENDER_MALE;
}

// [BC] Changes here
void D_SetupUserInfo (player_t *player)
{
	int i;
	userinfo_t *coninfo = &player->userinfo;

	for (i = 0; i < MAXPLAYERS; i++)
		memset (&players[i].userinfo, 0, sizeof(userinfo_t));

	strncpy (coninfo->netname, name.string, MAXPLAYERNAME);
	strncpy (coninfo->team, team.string, MAXPLAYERNAME);
	coninfo->aimdist = (fixed_t)(autoaim.value * 16384.0);
	coninfo->color = V_GetColorFromString (NULL, color.string);
	coninfo->railcolor = (int)railcolor.value;
	coninfo->skin = R_FindSkin (skin.string);
	coninfo->gender = D_GenderToInt (gender.string);
	coninfo->neverswitch = neverswitchonpickup.value ? true : false;
	coninfo->rocketpreferred = rocketpreferred.value ? true : false;
	coninfo->plasmapreferred = plasmapreferred.value ? true : false;
	coninfo->expert = expert.value ? true : false;
	// [BC] Translation no longer done here
}

void D_UserInfoChanged (cvar_t *cvar)
{
	char foo[256];

	if (cvar == &autoaim)
	{
		if (cvar->value < 0.0f)
		{
			cvar->Set (0.0f);
			return;
		}
		else if (cvar->value > 5000.0f)
		{
			cvar->Set (5000.0f);
			return;
		}
	}

	if (4 + strlen (cvar->name) + strlen (cvar->string) > 256)
		I_Error ("User info descriptor too big");

	sprintf (foo, "\\%s\\%s", cvar->name, cvar->string);

	Net_Command (DEM_UINFCHANGED, 0, foo, consoleplayer);
}

BOOL SetServerVar (char *name, char *value)
{
	cvar_t *dummy;
	cvar_t *var = FindCVar (name, &dummy);

	if (var)
	{
		unsigned oldflags = var->flags;

		var->flags &= ~(CVAR_SERVERINFO|CVAR_LATCH);
		var->Set (value);
		var->flags = oldflags;
		return true;
	}
	return false;
}

void D_SendServerInfoChange (const cvar_t *cvar, const char *value)
{
	char foo[256];

	if (4 + strlen (cvar->name) + strlen (value) > 256)
		I_Error ("Server info descriptor too big");

	sprintf (foo, "\\%s\\%s", cvar->name, value);

	Net_Command (DEM_SINFCHANGED, 0, foo, consoleplayer);
}

void D_DoServerInfoChange (char *stream)
{
	char *ptr = stream;
	char *breakpt;
	char *value;

	if (*ptr++ != '\\')
		return;

	while ( (breakpt = strchr (ptr, '\\')) ) {
		*breakpt = 0;
		value = breakpt + 1;
		if ( (breakpt = strchr (value, '\\')) )
			*breakpt = 0;

		if (SetServerVar (ptr, value) && (netstate == NS_CLIENT || netstate == NS_SERVER))
			Printf (PRINT_HIGH, "%s changed to %s\n", ptr, value);

		*(value - 1) = '\\';
		if (breakpt) {
			*breakpt = '\\';
			ptr = breakpt + 1;
		} else {
			break;
		}
	}

	*stream += strlen ((char *)stream) + 1;
}

void D_WriteUserInfoStrings (int i, char *stream, bool compact)
{
	if (i >= MAXPLAYERS)
	{
//		WriteByte (0, stream);
	}
	else
	{
		userinfo_t *info = &players[i].userinfo;

		if (!compact)
		{
			sprintf (*((char **)stream),
					 "\\name\\%s"
					 "\\autoaim\\%g"
					 "\\color\\%x %x %x"
					 "\\skin\\%s"
					 "\\team\\%s"
					 "\\gender\\%s"
					 "\\neverswitchonpickup\\%d",
					 "\\rocketpreferred\\%d",	// [BC]
					 "\\plasmapreferred\\%d",	// [BC]
					 "\\expert\\%d",		// [BC]
					 info->netname,
					 (double)info->aimdist / 16384.0,
					 RPART(info->color), GPART(info->color), BPART(info->color),
					 skins[info->skin].name, info->team,
					 info->gender == GENDER_FEMALE ? "female" :
					 info->gender == GENDER_NEUTER ? "cyborg" : "male",
					 info->neverswitch,
					 info->rocketpreferred,	// [BC]
					 info->plasmapreferred, // [BC]
					 info->expert			// [BC]
					 );
		}
		else
		{
			sprintf (*((char **)stream),
				"\\"
				"\\%s"			// name
				"\\%g"			// autoaim
				"\\%x %x %x"	// color
				"\\%s"			// skin
				"\\%s"			// team
				"\\%s"			// gender
				"\\%d"			// neverswitchonpickup
				"\\%d"			// rocketpreferred	[BC]
				"\\%d"			// plasmapreferred	[BC]
				"\\%d"			// expert	[BC]
				,
				info->netname,
				(double)info->aimdist / 16384.0,
				RPART(info->color), GPART(info->color), BPART(info->color),
				skins[info->skin].name,
				info->team,
				info->gender == GENDER_FEMALE ? "female" :
					info->gender == GENDER_NEUTER ? "cyborg" : "male",
				info->neverswitch,
				info->rocketpreferred,	// [BC]
				info->plasmapreferred,	// [BC]
				info->expert			// [BC]
			);
		}
	}

	*stream += strlen ((char *)stream) + 1;
}

void D_ReadUserInfoStrings (int i, char *stream, bool update)
{
	userinfo_t *info = &players[i].userinfo;
	char *ptr = stream;
	char *breakpt;
	char *value;
	bool compact;
	int infotype = -1;

	if (*ptr++ != '\\')
		return;

	compact = (*ptr == '\\') ? ptr++, true : false;

	if (i < MAXPLAYERS)
	{
		while ( (breakpt = strchr (ptr, '\\')) )
		{
			*breakpt = 0;
			if (compact)
			{
				value = ptr;
				infotype++;
			}
			else
			{
				value = breakpt + 1;
				if ( (breakpt = strchr (value, '\\')) )
					*breakpt = 0;

				int i = infotype + 1;
				while (UserInfoStrings[i] && stricmp (UserInfoStrings[i], ptr))
					i++;
				if (infotype >= 0)
				{
					i = 0;
					while (UserInfoStrings[i] && stricmp (UserInfoStrings[i], ptr))
						i++;
				}
				infotype = i;
			}

			switch (infotype)
			{
			case INFO_Autoaim:
				info->aimdist = (fixed_t)(atof (value) * 16384.0);
				break;

			case INFO_Name:
				{
					char oldname[MAXPLAYERNAME+1];

					strncpy (oldname, info->netname, MAXPLAYERNAME);
					oldname[MAXPLAYERNAME] = 0;
					strncpy (info->netname, value, MAXPLAYERNAME);
					info->netname[MAXPLAYERNAME] = 0;

					if (update && gamestate != GS_STARTUP)
						Printf (PRINT_HIGH, "%s is now known as %s\n", oldname, info->netname);
				}
				break;

			case INFO_Team:
				strncpy (info->team, value, MAXPLAYERNAME);
				info->team[MAXPLAYERNAME] = 0;
				if (update && gamestate != GS_STARTUP)
				{
					if (info->team[0])
						Printf (PRINT_HIGH, "%s joined the %s team\n", info->netname, info->team);
					else
						Printf (PRINT_HIGH, "%s is not on a team\n", info->netname);
				}
				break;

			case INFO_Color:
				info->color = V_GetColorFromString (NULL, value);
				if (gamestate != GS_STARTUP)
					P_BuildColor (i);
				st_firsttime = true;
				break;

			case INFO_Skin:
				if (oldschool.value)
					info->skin = 0;
				else
					info->skin = R_FindSkin (value);
				if (players[i].mo)
					players[i].mo->sprite = (spritenum_t)skins[info->skin].sprite;
				st_firsttime = true;
				break;

			case INFO_Gender:
				info->gender = D_GenderToInt (value);
				break;

			case INFO_NeverSwitchOnPickup:
				info->neverswitch = atoi (value) ? true : false;
				break;
// [BC]
			case INFO_RocketPreferred:
				info->rocketpreferred = atoi (value) ? true : false;
				break;
// [BC]
			case INFO_PlasmaPreferred:
				info->plasmapreferred = atoi (value) ? true : false;
				break;
// [BC]
			case INFO_Expert:
				info->expert = atoi (value) ? true : false;
				break;
// [BC]
			case INFO_Railcolor:
				info->railcolor = atoi (value);
				break;

			default:
				break;
			}

			if (!compact)
				*(value - 1) = '\\';
			if (breakpt)
			{
				*breakpt = '\\';
				ptr = breakpt + 1;
			}
			else
			{
				break;
			}
		}
	}

	*stream += strlen ((char *)stream) + 1;
}

FArchive &operator<< (FArchive &arc, userinfo_t &info)
{
	arc.Write (&info.netname, sizeof(info.netname));
	arc.Write (&info.team, sizeof(info.team));
	arc << info.aimdist
		<< info.color
		<< info.railcolor
		<< info.skin
		<< info.gender
		<< info.neverswitch
		<< info.rocketpreferred
		<< info.plasmapreferred
		<< info.expert;
	return arc;
}

FArchive &operator>> (FArchive &arc, userinfo_t &info)
{
	arc.Read (&info.netname, sizeof(info.netname));
	arc.Read (&info.team, sizeof(info.team));
	arc >> info.aimdist
		>> info.color
		>> info.railcolor
		>> info.skin
		>> info.gender
		>> info.neverswitch
		>> info.rocketpreferred
		>> info.plasmapreferred
		>> info.expert;
	return arc;
}
