#include "stdafx.h"
#include "entity.h"
#include "xrserver_objects.h"
#include "level.h"
#include "xrmessages.h"
#include "game_cl_base.h"
#include "net_queue.h"
#include "xrServer.h"
#include "Actor.h"
#include "Artefact.h"
#include "game_cl_base_weapon_usage_statistic.h"
#include "file_transfer.h"
#include "message_filter.h"
#include "../xrphysics/iphworld.h"

extern LPCSTR map_ver_string;
LPSTR remove_version_option(LPCSTR opt_str, LPSTR new_opt_str, u32 new_opt_str_size)
{
	LPCSTR temp_substr = strstr(opt_str, map_ver_string);
	if (!temp_substr)
	{
		xr_strcpy(new_opt_str, new_opt_str_size, opt_str);
		return new_opt_str;
	}
	strncpy_s(new_opt_str, new_opt_str_size, opt_str, static_cast<size_t>(temp_substr - opt_str - 1));
	temp_substr = strchr(temp_substr, '/');
	if (!temp_substr)
		return new_opt_str;

	xr_strcat(new_opt_str, new_opt_str_size, temp_substr);
	return new_opt_str;
}

#ifdef DEBUG
s32 lag_simmulator_min_ping	= 0;
s32 lag_simmulator_max_ping	= 0;
static bool SimmulateNetworkLag()
{
	static u32 max_lag_time	= 0;

	if (!lag_simmulator_max_ping && !lag_simmulator_min_ping)
		return false;
	
	if (!max_lag_time || (max_lag_time <= Device.dwTimeGlobal))
	{
		CRandom				tmp_random(Device.dwTimeGlobal);
		max_lag_time		= Device.dwTimeGlobal + tmp_random.randI(lag_simmulator_min_ping, lag_simmulator_max_ping);
		return false;
	}
	return true;
}
#endif

void CLevel::ClientReceive()
{
	m_dwRPC = 0;
	m_dwRPS = 0;
	
	//if (IsDemoPlayStarted())
	//{
	//	SimulateServerUpdate();
	//}
#ifdef DEBUG
	if (SimmulateNetworkLag())
		return;
#endif
	StartProcessQueue();
	for (NET_Packet* P = net_msg_Retreive(); P; P=net_msg_Retreive())
	{
		//if (IsDemoSaveStarted())
		//{
		//	SavePacket(*P);
		//}
		//-----------------------------------------------------
		m_dwRPC++;
		m_dwRPS += P->B.count;
		//-----------------------------------------------------
		u16			m_type;
		u16			ID;
		P->r_begin	(m_type);
		switch (m_type)
		{
		case M_SPAWN:			
			{
				if (!bReady)
				{
					Msg ("! Unconventional M_SPAWN received : map_data[%s] | bReady[%s] | deny_m_spawn[%s]",
						(map_data.m_map_sync_received) ? "true" : "false",
						(bReady) ? "true" : "false",
						m_variables.deny_m_spawn ? "true" : "false");
					break;
				}
				game_events->insert		(*P);
				if (g_bDebugEvents)		ProcessGameEvents();
			}
			break;
		case M_EVENT:
			game_events->insert		(*P);
			if (g_bDebugEvents)		ProcessGameEvents();
			break;
		case M_EVENT_PACK:
			{
				NET_Packet	tmpP;
				while (!P->r_eof())
				{
					tmpP.B.count = P->r_u8();
					P->r(&tmpP.B.data, tmpP.B.count);
					tmpP.timeReceive = P->timeReceive;

					game_events->insert		(tmpP);
					if (g_bDebugEvents)		ProcessGameEvents();
				};			
			}break;
		case M_UPDATE:
			{
				game->net_import_update	(*P);
			}break;
		case M_UPDATE_OBJECTS:
			{
				Objects.net_Import		(P);

				if (OnClient()) UpdateDeltaUpd(timeServer());
				IClientStatistic pStat = Level().GetStatistic();
				u32 dTime = 0;
				
				if ((Level().timeServer() + pStat.getPing()) < P->timeReceive)
				{
					dTime = pStat.getPing();
				}
				else
					dTime = Level().timeServer() - P->timeReceive + pStat.getPing();

				u32 NumSteps = physics_world()->CalcNumSteps(dTime);
				SetNumCrSteps(NumSteps);
			}break;
		case M_COMPRESSED_UPDATE_OBJECTS:
			{
				u8 compression_type = P->r_u8();
				ProcessCompressedUpdate(*P, compression_type);
			}break;
		case M_CL_UPDATE:
			{
				if (OnClient()) break;
				P->r_u16		(ID);
				u32 Ping = P->r_u32();
				CGameObject*	O	= smart_cast<CGameObject*>(Objects.net_Find		(ID));
				if (0 == O)		break;
				O->net_Import(*P);
		//---------------------------------------------------
				UpdateDeltaUpd(timeServer());
				if (pObjects4CrPr.empty() && pActors4CrPr.empty())
					break;
				if (!smart_cast<CActor*>(O))
					break;

				u32 dTime = 0;
				if ((Level().timeServer() + Ping) < P->timeReceive)
				{
#ifdef DEBUG
//					Msg("! TimeServer[%d] < TimeReceive[%d]", Level().timeServer(), P->timeReceive);
#endif
					dTime = Ping;
				}
				else					
					dTime = Level().timeServer() - P->timeReceive + Ping;
				u32 NumSteps = physics_world()->CalcNumSteps(dTime);
				SetNumCrSteps(NumSteps);

				O->CrPr_SetActivationStep(u32(physics_world()->StepsNum()) - NumSteps);
				AddActor_To_Actors4CrPr(O);

			}break;
		case M_MOVE_PLAYERS:
			{
				game_events->insert		(*P);
				if (g_bDebugEvents)		ProcessGameEvents();
			}break;

		case M_MOVE_ARTEFACTS:
			{
				u8 Count = P->r_u8();
				for (u8 i=0; i<Count; ++i)
				{
					u16 ID = P->r_u16();					
					Fvector NewPos;
					P->r_vec3(NewPos);
					CArtefact * OArtefact = smart_cast<CArtefact*>(Objects.net_Find(ID));
					if (!OArtefact)		break;
					OArtefact->MoveTo(NewPos);
				};
			}break;
		case M_AUTH_CHALLENGE:
			{
				ClientSendProfileData		();
			}break;
		case M_CLIENT_CONNECT_RESULT:
			{
				OnConnectResult(P);
			}break;
		case M_SV_CONFIG_NEW_CLIENT:
			InitializeClientGame	(*P);
			break;
		case M_SV_CONFIG_GAME:
			game->net_import_state(*P);
			break;
		case M_SV_CONFIG_FINISHED:
			{
				m_variables.m_game_configured			= TRUE;
	#ifdef DEBUG
				Msg("- Game configuring : Finished ");
	#endif // #ifdef DEBUG
				//if (IsDemoPlayStarted() && !m_current_spectator)
				//{
				//	SpawnDemoSpectator();
				//}
			}break;
		case M_CHAT:
			{
				char	buffer[256];
				P->r_stringZ(buffer);
				Msg		("- %s",buffer);
			}
			break;
		case M_GAMEMESSAGE:
			{
				if (!game) break;
				game_events->insert		(*P);
				if (g_bDebugEvents)		ProcessGameEvents();
			}break;
		case M_CHANGE_LEVEL:
			break;
		case M_CHAT_MESSAGE:
			{
				if (!game) break;
				Game().OnChatMessage(P);
			}break;
		case M_CLIENT_WARN:
			{
				if (!game) break;
				Game().OnWarnMessage(P);
			}break;
		case M_REMOTE_CONTROL_AUTH:
		case M_REMOTE_CONTROL_CMD:
			{
				Game().OnRadminMessage(m_type, P);
			}break;
		case M_SV_MAP_NAME:
			{
				map_data.ReceiveServerMapSync(*P);
			}break;
		case M_CHANGE_LEVEL_GAME:
			{
				Msg("- M_CHANGE_LEVEL_GAME Received");

				if (OnClient())
				{
					MakeReconnect();
				}
				else
				{
					const char* m_SO = m_caServerOptions.c_str();

					m_SO = strchr(m_SO, '/'); if (m_SO) m_SO++;
					m_SO = strchr(m_SO, '/'); 

					shared_str LevelName;
					shared_str LevelVersion;
					shared_str GameType;

					P->r_stringZ(LevelName);
					P->r_stringZ(LevelVersion);
					P->r_stringZ(GameType);

					string4096 NewServerOptions = "";
					xr_sprintf(NewServerOptions, "%s/%s/%s%s",
						LevelName.c_str(),
						GameType.c_str(),
						map_ver_string,
						LevelVersion.c_str()
					);

					if (m_SO)
					{
						string4096 additional_options;
						xr_strcat(NewServerOptions, sizeof(NewServerOptions),
							remove_version_option(m_SO, additional_options, sizeof(additional_options))
						);
					}
					m_caServerOptions = NewServerOptions;
					MakeReconnect();
				};
			}break;
		case M_BULLET_CHECK_RESPOND:
			{
				if (!game) break;
				Game().m_WeaponUsageStatistic->On_Check_Respond(P);
			}break;
		//case M_STATISTIC_UPDATE:
		//	{
		//		Msg("--- CL: On Update Request");
		//			if (!game) break;
		//		game_events->insert		(*P);
		//		if (g_bDebugEvents)		ProcessGameEvents();
		//	}break;
		case M_FILE_TRANSFER:
			{
				game_events->insert		(*P);
				if (g_bDebugEvents)		ProcessGameEvents();
			}break;
		case M_SECURE_KEY_SYNC:
			{
				OnSecureKeySync			(*P);
			}break;
		case M_SECURE_MESSAGE:
			{
				OnSecureMessage			(*P);
			}break;
		}

		net_msg_Release();
	}	
	EndProcessQueue();

	if (g_bDebugEvents) ProcessGameSpawns();
}
