/*
 * Copyright (c) 2016-2019 Marco Hladik <marco@icculus.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

void SV_SendChat(entity eSender, string sMessage, entity eEnt, float fType)
{
	WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
	WriteByte(MSG_MULTICAST, fType == 0 ? EV_CHAT:EV_CHAT_TEAM);
	WriteByte(MSG_MULTICAST, num_for_edict(eSender) - 1); 
	WriteByte(MSG_MULTICAST, eSender.team); 
	WriteString(MSG_MULTICAST, sMessage);

	if (eEnt) {
		msg_entity = eEnt;
		multicast([0,0,0], MULTICAST_ONE);
	} else {
		multicast([0,0,0], MULTICAST_ALL);
	}
}

/*
=================
SV_ParseClientCommand

Intercepts 'cmd' calls. We use it to intercept
chat messages and handle distribution ourselves.
=================
*/
void Game_ParseClientCommand(string sCommand)
{
	tokenize(sCommand);

	if (argv(1) == "timeleft") {
		float fTimeLeft = cvar("mp_timelimit") - (time / 60);
		Vox_Singlecast(self, sprintf("we have %s minutes remaining", Vox_TimeToString(fTimeLeft)));
		return;
	}

	// Players talk to players, spectators to spectators.
	if (self.health) {
		if (argv(0) == "say") {
			localcmd(sprintf("echo %s: %s\n", self.netname, argv(1)));
			SV_SendChat(self, argv(1), world, 0);
			return;
		} else if (argv(0) == "say_team") {
			localcmd(sprintf("echo [TEAM %d] %s: %s\n", self.team, self.netname, argv(1)));
			for (entity eFind = world; (eFind = find(eFind, classname, "player"));) { 
				if (eFind.team == self.team) {
					SV_SendChat(self, argv(1), eFind, 1);
				}
			}
			return;
		}
	} else {
		if (argv(0) == "say") {
			localcmd(sprintf("echo [DEAD] %s: %s\n", self.netname, argv(1)));
			for (entity eFind = world; (eFind = find(eFind, classname, "spectator"));) { 
				SV_SendChat(self, argv(1), eFind, 1);
			}
			return;	
		} else if (argv(0) == "say_team") {
			localcmd(sprintf("echo [DEAD] %s: %s\n", self.netname, argv(1)));
			return;
		} 
	}

	clientcommand(self, sCommand);
}

float Game_ConsoleCmd(string sCommand)
{
	tokenize(sCommand);
	switch (argv(0)) {
	case "vox":
		Vox_Broadcast(argv(1));
		break;
	default:
		return FALSE;
	}
	
	return TRUE;
}

void SV_PausedTic(float fDuration)
{

}

/*
=================
StartFrame

Runs every frame... by worldspawn?
=================
*/
void Game_StartFrame(void)
{
	// We've got hostages, but no rescue zones, create some
	if (!iRescueZones && iHostagesMax > 0) {
		Game_CreateRescueZones();
	}
		
	if (iBuyZones == 0) {
		Game_CreateBuyZones();
	}

	// TODO: Optimise this
	if ((iAlivePlayers_T + iAlivePlayers_CT) == 0) {
		int iInGamePlayers = 0;
		for (entity eFind = world; (eFind = find(eFind, classname, "player"));) {
			iInGamePlayers++;
		}
		
		if ((iInGamePlayers > 0) && (fGameState != GAME_COMMENCING && fGameState != GAME_END)) {
			Timer_Begin(2, GAME_COMMENCING);
		} else if (iInGamePlayers == 0) {
			fGameState = GAME_INACTIVE;
			fGameTime = 0;
			iWon_T = 0;
			iWon_CT = 0;
			iRounds = 0;
		} else {
			Timer_Update(); // Timer that happens once players have started joining
		}
	} else {
		Timer_Update(); // Normal gameplay timer
	}
}

/*
=================
worldspawn

It's the map entity, literally
=================
*/

void Game_Worldspawn(void)
{
	string sTemp;
	int iMOTDLines = 0;

	// The message of the day.
	localcmd(sprintf("echo [MOTD] Loading %s.\n", autocvar_motdfile));
	filestream fmMOTD = fopen(autocvar_motdfile, FILE_READ);
	
	if (fmMOTD >= 0) {
		for (int i = 0; i < 25; i++) {
			sTemp = fgets(fmMOTD);
			if not (sTemp) {
				break;
			} 
			
			if (sTemp == __NULL__) {
				localcmd(sprintf("serverinfo motdline%i /\n", iMOTDLines));
			} else {
				localcmd(sprintf("serverinfo motdline%i %s\n", iMOTDLines, sTemp));
			}
			iMOTDLines++;
		}
		localcmd(sprintf("serverinfo motdlength %i\n", iMOTDLines));
		fclose(fmMOTD);
	} else {
		error("[MOTD] Loading failed.\n");	
	}

	// The mapcycle information.
	localcmd(sprintf("echo [MAPCYCLE] Loading %s.\n", autocvar_mapcyclefile));
	filestream fmMapcycle = fopen(autocvar_mapcyclefile, FILE_READ);
	
	if (fmMapcycle >= 0) {
		for (int i = 0;; i++) {
			sTemp = fgets(fmMapcycle);
			if not (sTemp) {
				break;
			} 
			
			if (sTemp != __NULL__) {
				iMapCycleCount++;
			}
		}
		
		fseek(fmMapcycle, 0);
		localcmd(sprintf("echo [MAPCYCLE] List has %i maps.\n", iMapCycleCount));
		sMapCycle = memalloc(sizeof(string) * iMapCycleCount);
		for (int i = 0; i < iMapCycleCount; i++) {
			sMapCycle[i] = fgets(fmMapcycle);
		}
		fclose(fmMapcycle);
		
		for (int i = 0; i < iMapCycleCount; i++) {
			if (sMapCycle[i] == mapname) {
				if ((i + 1) < iMapCycleCount) {
					localcmd(sprintf("echo [MAPCYCLE] Next map: %s\n", sMapCycle[i + 1]));
				} else {
					break;
				}
			}
		}
	} else {
		iMapCycleCount = 0;
		error("[MAPCYCLE] Loading failed.\n");	
	}

	// Let's make our version information clear
	localcmd(sprintf("serverinfo fcs_ver %s\n", __DATE__));

	// All the important precaches
	for (int i = 1; i < CS_WEAPON_COUNT; i++) {
		precache_model(sWeaponModels[i]);
	}

	/*Bot_Init();*/

	precache_model("models/w_flashbang.mdl");
	precache_model("models/w_hegrenade.mdl");
	precache_model("models/w_smokegrenade.mdl");

	precache_model(sCSPlayers[1]);
	precache_model(sCSPlayers[2]);
	precache_model(sCSPlayers[3]);
	precache_model(sCSPlayers[4]);
	precache_model(sCSPlayers[5]);
	precache_model(sCSPlayers[6]);
	precache_model(sCSPlayers[7]);
	precache_model(sCSPlayers[8]);
	precache_model("models/player/vip/vip.mdl");
	precache_model("models/w_c4.mdl");

	precache_sound("misc/null.wav");
	precache_sound("hostage/hos1.wav");
	precache_sound("hostage/hos2.wav");
	precache_sound("hostage/hos3.wav");
	precache_sound("hostage/hos4.wav");
	precache_sound("hostage/hos5.wav");
	precache_sound("player/pl_pain2.wav");
	precache_sound("player/pl_pain4.wav");
	precache_sound("player/pl_pain5.wav");
	precache_sound("player/pl_pain6.wav");
	precache_sound("player/pl_pain7.wav");
	precache_sound("player/die1.wav");
	precache_sound("player/die2.wav");
	precache_sound("player/die3.wav");
	precache_sound("player/headshot1.wav");
	precache_sound("player/headshot2.wav");
	precache_sound("player/headshot3.wav");
	precache_sound("items/tr_kevlar.wav");
	precache_sound("weapons/ak47-1.wav");
	precache_sound("weapons/ak47-2.wav");
	precache_sound("weapons/ak47_boltpull.wav");
	precache_sound("weapons/ak47_clipin.wav");
	precache_sound("weapons/ak47_clipout.wav");
	precache_sound("weapons/aug-1.wav");
	precache_sound("weapons/aug_boltpull.wav");
	precache_sound("weapons/aug_boltslap.wav");
	precache_sound("weapons/aug_clipin.wav");
	precache_sound("weapons/aug_clipout.wav");
	precache_sound("weapons/aug_forearm.wav");
	precache_sound("weapons/awp1.wav");
	precache_sound("weapons/awp_clipin.wav");
	precache_sound("weapons/awp_clipout.wav");
	precache_sound("weapons/awp_deploy.wav");
	precache_sound("weapons/boltdown.wav");
	precache_sound("weapons/boltpull1.wav");
	precache_sound("weapons/boltup.wav");
	precache_sound("weapons/c4_beep1.wav");
	precache_sound("weapons/c4_beep2.wav");
	precache_sound("weapons/c4_beep3.wav");
	precache_sound("weapons/c4_beep4.wav");
	precache_sound("weapons/c4_beep5.wav");
	precache_sound("weapons/c4_click.wav");
	precache_sound("weapons/c4_disarm.wav");
	precache_sound("weapons/c4_disarmed.wav");
	precache_sound("weapons/c4_explode1.wav");
	precache_sound("weapons/c4_plant.wav");
	precache_sound("weapons/clipin1.wav");
	precache_sound("weapons/clipout1.wav");
	precache_sound("weapons/de_clipin.wav");
	precache_sound("weapons/de_clipout.wav");
	precache_sound("weapons/de_deploy.wav");
	precache_sound("weapons/deagle-1.wav");
	precache_sound("weapons/deagle-2.wav");
	precache_sound("weapons/dryfire_pistol.wav");
	precache_sound("weapons/dryfire_rifle.wav");
	precache_sound("weapons/elite_clipout.wav");
	precache_sound("weapons/elite_deploy.wav");
	precache_sound("weapons/elite_fire.wav");
	precache_sound("weapons/elite_leftclipin.wav");
	precache_sound("weapons/elite_reloadstart.wav");
	precache_sound("weapons/elite_rightclipin.wav");
	precache_sound("weapons/elite_sliderelease.wav");
	precache_sound("weapons/elite_twirl.wav");
	precache_sound("weapons/fiveseven-1.wav");
	precache_sound("weapons/fiveseven_clipin.wav");
	precache_sound("weapons/fiveseven_clipout.wav");
	precache_sound("weapons/fiveseven_slidepull.wav");
	precache_sound("weapons/fiveseven_sliderelease.wav");
	precache_sound("weapons/flashbang-1.wav");
	precache_sound("weapons/flashbang-2.wav");
	precache_sound("weapons/g3sg1-1.wav");
	precache_sound("weapons/g3sg1_clipin.wav");
	precache_sound("weapons/g3sg1_clipout.wav");
	precache_sound("weapons/g3sg1_slide.wav");
	precache_sound("weapons/generic_reload.wav");
	precache_sound("weapons/generic_shot_reload.wav");
	precache_sound("weapons/glock18-1.wav");
	precache_sound("weapons/glock18-2.wav");
	precache_sound("weapons/grenade_hit1.wav");
	precache_sound("weapons/grenade_hit2.wav");
	precache_sound("weapons/grenade_hit3.wav");
	precache_sound("weapons/he_bounce-1.wav");
	precache_sound("weapons/headshot2.wav");
	precache_sound("weapons/hegrenade-1.wav");
	precache_sound("weapons/hegrenade-2.wav");
	precache_sound("weapons/knife_deploy1.wav");
	precache_sound("weapons/knife_hit1.wav");
	precache_sound("weapons/knife_hit2.wav");
	precache_sound("weapons/knife_hit3.wav");
	precache_sound("weapons/knife_hit4.wav");
	precache_sound("weapons/knife_hitwall1.wav");
	precache_sound("weapons/knife_slash1.wav");
	precache_sound("weapons/knife_slash2.wav");
	precache_sound("weapons/knife_stab.wav");
	precache_sound("weapons/m249-1.wav");
	precache_sound("weapons/m249-2.wav");
	precache_sound("weapons/m249_boxin.wav");
	precache_sound("weapons/m249_boxout.wav");
	precache_sound("weapons/m249_chain.wav");
	precache_sound("weapons/m249_coverdown.wav");
	precache_sound("weapons/m249_coverup.wav");
	precache_sound("weapons/m3-1.wav");
	precache_sound("weapons/m3_insertshell.wav");
	precache_sound("weapons/m3_pump.wav");
	precache_sound("weapons/m4a1-1.wav");
	precache_sound("weapons/m4a1_boltpull.wav");
	precache_sound("weapons/m4a1_clipin.wav");
	precache_sound("weapons/m4a1_clipout.wav");
	precache_sound("weapons/m4a1_deploy.wav");
	precache_sound("weapons/m4a1_silencer_off.wav");
	precache_sound("weapons/m4a1_silencer_on.wav");
	precache_sound("weapons/m4a1_unsil-1.wav");
	precache_sound("weapons/m4a1_unsil-2.wav");
	precache_sound("weapons/mac10-1.wav");
	precache_sound("weapons/mac10_boltpull.wav");
	precache_sound("weapons/mac10_clipin.wav");
	precache_sound("weapons/mac10_clipout.wav");
	precache_sound("weapons/mp5-1.wav");
	precache_sound("weapons/mp5-2.wav");
	precache_sound("weapons/mp5_clipin.wav");
	precache_sound("weapons/mp5_clipout.wav");
	precache_sound("weapons/mp5_slideback.wav");
	precache_sound("weapons/p228-1.wav");
	precache_sound("weapons/p228_clipin.wav");
	precache_sound("weapons/p228_clipout.wav");
	precache_sound("weapons/p228_slidepull.wav");
	precache_sound("weapons/p228_sliderelease.wav");
	precache_sound("weapons/p90-1.wav");
	precache_sound("weapons/p90_boltpull.wav");
	precache_sound("weapons/p90_clipin.wav");
	precache_sound("weapons/p90_clipout.wav");
	precache_sound("weapons/p90_cliprelease.wav");
	precache_sound("weapons/pinpull.wav");
	precache_sound("weapons/ric1.wav");
	precache_sound("weapons/ric2.wav");
	precache_sound("weapons/ric3.wav");
	precache_sound("weapons/ric4.wav");
	precache_sound("weapons/ric5.wav");
	precache_sound("weapons/ric_conc-1.wav");
	precache_sound("weapons/ric_conc-2.wav");
	precache_sound("weapons/ric_metal-1.wav");
	precache_sound("weapons/ric_metal-2.wav");
	precache_sound("weapons/scout_bolt.wav");
	precache_sound("weapons/scout_clipin.wav");
	precache_sound("weapons/scout_clipout.wav");
	precache_sound("weapons/scout_fire-1.wav");
	precache_sound("weapons/sg550-1.wav");
	precache_sound("weapons/sg550_boltpull.wav");
	precache_sound("weapons/sg550_clipin.wav");
	precache_sound("weapons/sg550_clipout.wav");
	precache_sound("weapons/sg552-1.wav");
	precache_sound("weapons/sg552-2.wav");
	precache_sound("weapons/sg552_boltpull.wav");
	precache_sound("weapons/sg552_clipin.wav");
	precache_sound("weapons/sg552_clipout.wav");
	precache_sound("weapons/sg_explode.wav");
	precache_sound("weapons/slideback1.wav");
	precache_sound("weapons/sliderelease1.wav");
	precache_sound("weapons/tmp-1.wav");
	precache_sound("weapons/tmp-2.wav");
	precache_sound("weapons/ump45-1.wav");
	precache_sound("weapons/ump45_boltslap.wav");
	precache_sound("weapons/ump45_clipin.wav");
	precache_sound("weapons/ump45_clipout.wav");
	precache_sound("weapons/usp1.wav");
	precache_sound("weapons/usp2.wav");
	precache_sound("weapons/usp_clipin.wav");
	precache_sound("weapons/usp_clipout.wav");
	precache_sound("weapons/usp_silencer_off.wav");
	precache_sound("weapons/usp_silencer_on.wav");
	precache_sound("weapons/usp_slideback.wav");
	precache_sound("weapons/usp_sliderelease.wav");
	precache_sound("weapons/usp_unsil-1.wav");
	precache_sound("weapons/xm1014-1.wav");
	precache_sound("weapons/zoom.wav");

	clientstat(0, EV_FLOAT, health);
	clientstat(10, EV_FLOAT, weapon);
	clientstat(16, EV_FLOAT, view_ofs[2]);
	clientstat(21, EV_FLOAT, viewzoom);

	/* FIXME: Turn those into bitflags */
	clientstat(STAT_BUYZONE, EV_FLOAT, fInBuyZone);
	clientstat(STAT_HOSTAGEZONE, EV_FLOAT, fInHostageZone);
	clientstat(STAT_BOMBZONE, EV_FLOAT, fInBombZone);
	clientstat(STAT_ESCAPEZONE, EV_FLOAT, fInEscapeZone);
	clientstat(STAT_VIPZONE, EV_FLOAT, fInVIPZone);

	clientstat(4, EV_FLOAT, armor);
	clientstat(STAT_MONEY, EV_FLOAT, fMoney);
	clientstat(STAT_SLOT_MELEE, EV_FLOAT, fSlotMelee);
	clientstat(STAT_SLOT_PRIMARY, EV_FLOAT, fSlotPrimary);
	clientstat(STAT_SLOT_SECONDARY, EV_FLOAT, fSlotSecondary);
	clientstat(STAT_SLOT_GRENADE, EV_FLOAT, fSlotGrenade);
	clientstat(STAT_SLOT_C4BOMB, EV_FLOAT, fSlotC4Bomb);
	clientstat(STAT_ITEM_FLASHBANG, EV_INTEGER, iAmmo_FLASHBANG);
	clientstat(STAT_ITEM_HEGRENADE, EV_INTEGER, iAmmo_HEGRENADE);
	clientstat(STAT_ITEM_SMOKEGRENADE, EV_INTEGER, iAmmo_SMOKEGRENADE);
	clientstat(STAT_EQUIPMENT, EV_INTEGER, iEquipment);
	clientstat(STAT_CURRENT_MAG, EV_INTEGER, iCurrentMag);
	clientstat(STAT_CURRENT_CALIBER, EV_INTEGER, iCurrentCaliber);
	clientstat(STAT_TEAM, EV_INTEGER, team);
	clientstat(STAT_PROGRESS, EV_FLOAT, fProgressBar);
	clientstat(STAT_FLAGS, EV_FLOAT, flags);
	pointerstat(STAT_GAMETIME, EV_FLOAT, &fGameTime);
	pointerstat(STAT_GAMESTATE, EV_FLOAT, &fGameState);
	pointerstat(STAT_WON_T, EV_INTEGER, &iWon_T);
	pointerstat(STAT_WON_CT, EV_INTEGER, &iWon_CT);

	iBombRadius = 1024;
	localcmd(sprintf("serverinfo slots %d\n", cvar("sv_playerslots")));
	localcmd("teamplay 1\n");
}
