/*
   Copyright (C) 2006-2007 Benjamin Litzelmann ("Kurim")
   for Chasseur de bots association.

   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.

 */

#include "g_local.h"

#define EBHIT_FOR_AWARD		3
#define DIRECTROCKET_FOR_AWARD	3
#define DIRECTGRENADE_FOR_AWARD	3
#define MULTIKILL_INTERVAL	3000
#define LB_TIMEOUT_FOR_COMBO	200

#define COMBO_FLAG( a )	  ( 1<<( a-1 ) )

static void G_AwardNotify( edict_t *ent, int type, int level )
{
	if( !ent->r.client )
		return;

	// todo : send the award only to the client who get it
	trap_GameCmd( NULL, va( "aw %i %i %i", PLAYERNUM( ent ), type, level ) );
}

void G_AwardPlayerHit( edict_t *targ, edict_t *attacker, int mod )
{
	int flag = -1;

	if( attacker->s.team == targ->s.team && attacker->s.team > TEAM_PLAYERS )
		return;

	switch( mod )
	{
	case MOD_INSTAGUN_W:
	case MOD_INSTAGUN_S:
		attacker->r.client->awardInfo.ebhit_count++;
		if( attacker->r.client->awardInfo.ebhit_count == EBHIT_FOR_AWARD )
		{
			attacker->r.client->awardInfo.ebhit_count = 0;
			attacker->r.client->awardInfo.accuracy_award++;
			G_AwardNotify( attacker, ACCURACY_AWARD, attacker->r.client->awardInfo.accuracy_award );
		}
		flag = COMBO_FLAG( WEAP_INSTAGUN );
		break;
	case MOD_ELECTROBOLT_W:
	case MOD_ELECTROBOLT_S:
		attacker->r.client->awardInfo.ebhit_count++;
		if( attacker->r.client->awardInfo.ebhit_count == EBHIT_FOR_AWARD )
		{
			attacker->r.client->awardInfo.ebhit_count = 0;
			attacker->r.client->awardInfo.accuracy_award++;
			G_AwardNotify( attacker, ACCURACY_AWARD, attacker->r.client->awardInfo.accuracy_award );
		}
		flag = COMBO_FLAG( WEAP_ELECTROBOLT );
		break;
	case MOD_ROCKET_W:
	case MOD_ROCKET_S:
	case MOD_ROCKET_SPLASH_W:
	case MOD_ROCKET_SPLASH_S:
		flag = COMBO_FLAG( WEAP_ROCKETLAUNCHER );
		break;
	case MOD_GUNBLADE_W:
	case MOD_GUNBLADE_S:
		flag = COMBO_FLAG( WEAP_GUNBLADE );
		break;
	case MOD_RIOTGUN_W:
	case MOD_RIOTGUN_S:
		flag = COMBO_FLAG( WEAP_RIOTGUN );
		break;
	case MOD_GRENADE_W:
	case MOD_GRENADE_S:
	case MOD_GRENADE_SPLASH_W:
	case MOD_GRENADE_SPLASH_S:
		flag = COMBO_FLAG( WEAP_GRENADELAUNCHER );
		break;
	case MOD_PLASMA_W:
	case MOD_PLASMA_S:
	case MOD_PLASMA_SPLASH_W:
	case MOD_PLASMA_SPLASH_S:
		flag = COMBO_FLAG( WEAP_PLASMAGUN );
		break;
	case MOD_LASERGUN_W:
	case MOD_LASERGUN_S:
		flag = COMBO_FLAG( WEAP_LASERGUN );
		break;
	default:
		break;
	}

	if( flag )
	{
		if( attacker->r.client->awardInfo.combo[PLAYERNUM( targ )] == COMBO_FLAG( WEAP_ROCKETLAUNCHER ) && G_IsDead( targ ) ) // RL...
		{
			if( flag == COMBO_FLAG( WEAP_ELECTROBOLT ) )  // to EB
				G_AwardNotify( attacker, COMBO_RL_TO_EB_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_LASERGUN ) )  // to LG
				G_AwardNotify( attacker, COMBO_RL_TO_LG_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_RIOTGUN ) )  // to RG
				G_AwardNotify( attacker, COMBO_RL_TO_RG_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_GRENADELAUNCHER ) )  // to GL
				G_AwardNotify( attacker, COMBO_RL_TO_GL_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_ROCKETLAUNCHER ) )  // to RL
				G_AwardNotify( attacker, COMBO_RL_TO_RL_AWARD, 1 );
		}
		else if( attacker->r.client->awardInfo.combo[PLAYERNUM( targ )] == COMBO_FLAG( WEAP_ROCKETLAUNCHER ) && G_IsDead( targ ) ) // GL...
		{
			if( flag == COMBO_FLAG( WEAP_ELECTROBOLT ) )  // to EB
				G_AwardNotify( attacker, COMBO_GL_TO_EB_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_LASERGUN ) )  // to LG
				G_AwardNotify( attacker, COMBO_GL_TO_LG_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_RIOTGUN ) )  // to RG
				G_AwardNotify( attacker, COMBO_GL_TO_RG_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_ROCKETLAUNCHER ) )  // to RL
				G_AwardNotify( attacker, COMBO_GL_TO_RL_AWARD, 1 );
			else if( flag == COMBO_FLAG( WEAP_GRENADELAUNCHER ) )  // to GL
				G_AwardNotify( attacker, COMBO_GL_TO_GL_AWARD, 1 );
		}
		else if( attacker->r.client->awardInfo.combo[PLAYERNUM( targ )] == COMBO_FLAG( WEAP_LASERGUN ) && G_IsDead( targ ) ) // LG...
		{
			if( flag == COMBO_FLAG( WEAP_ELECTROBOLT ) )  // to EB
				if( attacker->r.client->awardInfo.lasthit == targ && level.time < attacker->r.client->awardInfo.lasthit_time + LB_TIMEOUT_FOR_COMBO )
					G_AwardNotify( attacker, COMBO_LB_TO_EB_AWARD, 1 );
		}
		attacker->r.client->awardInfo.combo[PLAYERNUM( targ )] = flag;
	}

	attacker->r.client->awardInfo.lasthit = targ;
	attacker->r.client->awardInfo.lasthit_time = level.time;
}

void G_AwardResetPlayerComboStats( edict_t *ent )
{
	int i;
	int resetvalue;

	// combo from LB can be cancelled only if player's dead, if he missed or if he hasnt shot with LB for too long
	resetvalue = ( G_IsDead( ent ) ? 0 : COMBO_FLAG( WEAP_LASERGUN ) );

	for( i = 0; i < game.maxclients; i++ )
		game.clients[i].awardInfo.combo[PLAYERNUM( ent )] &= resetvalue;
}

void G_AwardPlayerMissedElectrobolt( edict_t *self, int mod )
{
	if( mod == MOD_ELECTROBOLT_W || mod == MOD_ELECTROBOLT_S || mod == MOD_INSTAGUN_W || mod == MOD_INSTAGUN_S )
		self->r.client->awardInfo.ebhit_count = 0;
}

void G_AwardPlayerMissedLasergun( edict_t *self, int mod )
{
	int i;
	if( mod == MOD_LASERGUN_W || mod == MOD_LASERGUN_S )
	{
		for( i = 0; i < game.maxclients; i++ )  // cancelling lasergun combo award
			game.clients[i].awardInfo.combo[PLAYERNUM( self )] &= ~COMBO_FLAG( WEAP_LASERGUN );
	}
}

void G_AwardPlayerKilled( edict_t *self, edict_t *inflictor, edict_t *attacker, int mod )
{
	trace_t trace;

	if( self->r.svflags & SVF_CORPSE )
		return;

	if( !attacker->r.client )
		return;

	if( !self->r.client )
		return;

	if( attacker == self )
		return;

	if( attacker->s.team == self->s.team && attacker->s.team > TEAM_PLAYERS )
		return;

	if( mod == MOD_ROCKET_W || mod == MOD_ROCKET_S )
	{
		// direct hit
		attacker->r.client->awardInfo.directrocket_count++;
		if( attacker->r.client->awardInfo.directrocket_count == DIRECTROCKET_FOR_AWARD )
		{
			attacker->r.client->awardInfo.directrocket_count = 0;
			attacker->r.client->awardInfo.directrocket_award++;
			G_AwardNotify( attacker, DIRECTROCKET_AWARD, attacker->r.client->awardInfo.directrocket_award );
		}
		// Midair
		if( self->groundentity == NULL && !self->waterlevel )
		{
			// check for height to the ground
			G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID );
			if( trace.fraction == 1.0f )
			{
				attacker->r.client->awardInfo.rl_midair_award++;
				G_AwardNotify( attacker, RL_MIDAIR_AWARD, attacker->r.client->awardInfo.rl_midair_award );
			}
		}
	}
	if( mod == MOD_GRENADE_W || mod == MOD_GRENADE_S )
	{
		// direct hit
		attacker->r.client->awardInfo.directgrenade_count++;
		if( attacker->r.client->awardInfo.directgrenade_count == DIRECTGRENADE_FOR_AWARD )
		{
			attacker->r.client->awardInfo.directgrenade_count = 0;
			attacker->r.client->awardInfo.directgrenade_award++;
			G_AwardNotify( attacker, DIRECTGRENADE_AWARD, attacker->r.client->awardInfo.directgrenade_award );
		}

		// Midair
		if( self->groundentity == NULL && !self->waterlevel )
		{
			// check for height to the ground
			G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID );
			if( trace.fraction == 1.0f )
			{
				attacker->r.client->awardInfo.gl_midair_award++;
				G_AwardNotify( attacker, GL_MIDAIR_AWARD, attacker->r.client->awardInfo.gl_midair_award );
			}
		}
	}

	// Multikill
	if( game.serverTime - attacker->r.client->awardInfo.multifrag_timer < MULTIKILL_INTERVAL )
	{
		attacker->r.client->awardInfo.multifrag_count++;
	}
	else
		attacker->r.client->awardInfo.multifrag_count = 1;

	attacker->r.client->awardInfo.multifrag_timer = game.serverTime;

	if( attacker->r.client->awardInfo.multifrag_count >= 2 )
		G_AwardNotify( attacker, MULTIKILL_AWARD, attacker->r.client->awardInfo.multifrag_count );

	// Sprees
	attacker->r.client->awardInfo.frag_count++;

	if( attacker->r.client->awardInfo.frag_count % 5 == 0 )
		G_AwardNotify( attacker, SPREE_AWARD, attacker->r.client->awardInfo.frag_count / 5 );
}

void G_AwardPlayerPickup( edict_t *self, edict_t *item )
{
	if( !item )
		return;

	if( game.gametype == GAMETYPE_DUEL )
	{
		// MH control
		if( item->item->tag == HEALTH_MEGA )
		{
			self->r.client->awardInfo.mh_control_award++;
			if( self->r.client->awardInfo.mh_control_award % 5 == 0 )
				G_AwardNotify( self, MH_CTRL_AWARD, self->r.client->awardInfo.mh_control_award / 5 );
		}

		// RA control
		if( item->item->tag == ARMOR_RA )
		{
			self->r.client->awardInfo.ra_control_award++;
			if( self->r.client->awardInfo.ra_control_award % 5 == 0 )
				G_AwardNotify( self, RA_CTRL_AWARD, self->r.client->awardInfo.ra_control_award / 5 );
		}
	}
}

void G_AwardRaceRecord( edict_t *self )
{
	G_AwardNotify( self, RACERECORD_AWARD, 1 );
}

void G_AwardCapture( edict_t *ent )
{
	G_AwardNotify( ent, FLAG_CAPTURE_AWARD, 1 );
}

void G_AwardCTFRecovery( edict_t *ent )
{
	G_AwardNotify( ent, FLAG_RECOVERY_AWARD, 1 );
}

void G_AwardCaptureArea( edict_t *ent )
{
	G_AwardNotify( ent, CAPTURE_AREA_AWARD, 1 );
}

void G_AwardAllCaptureAreas( edict_t *ent )
{
	G_AwardNotify( ent, ALL_CAPTURE_AREAS_AWARD, 1 );
}

void G_AwardFragFlagCarrier( edict_t *ent )
{
	G_AwardNotify( ent, FLAG_CARRIER_KILL_AWARD, 1 );
}

void G_AwardDefendFlagCarrier( edict_t *ent )
{
	G_AwardNotify( ent, FLAG_CARRIER_DEFEND_AWARD, 1 );
}

void G_AwardDefendFlagBase( edict_t *ent )
{
	G_AwardNotify( ent, FLAG_BASE_DEFEND_AWARD, 1 );
}
