#include "stdafx.h"
#include "screenshot_server.h"
#include "xrMessages.h"
#include "Level.h"
#include "xrServer.h"
#include "game_sv_base.h"
#include "game_cl_mp.h"
#include "../xrCore/fastdelegate.h"

extern BOOL	g_sv_mp_save_proxy_screenshots;
extern BOOL g_sv_mp_save_proxy_configs;

clientdata_proxy::clientdata_proxy(file_transfer::server_site * ft_server) :
	m_ft_server(ft_server)
{
}

clientdata_proxy::~clientdata_proxy()
{
	if (is_active())
	{
		if (m_ft_server->is_receiving_active(m_chearer_id))
			m_ft_server->stop_receive_file(m_chearer_id);
		if (m_ft_server->is_transfer_active(m_admin_id, m_chearer_id))
			m_ft_server->stop_transfer_file(std::make_pair(m_admin_id, m_chearer_id));
	}
}

void clientdata_proxy::make_screenshot(ClientID const & admin_id, ClientID const & cheater_id)
{
	m_admin_id = admin_id;
	m_chearer_id = cheater_id;
	xrClientData* tmp_cheater = static_cast<xrClientData*>(
		Level().Server->GetClientByID(m_chearer_id));
	if (!tmp_cheater)
	{
		Msg("! ERROR: SV: client [%u] not found ...", cheater_id.value());
		return;
	}
	if (m_ft_server->is_receiving_active(cheater_id))
	{
		Msg("! Receiving from client [%u] already active, please try later", cheater_id.value());
		return;
	}
	m_cheater_digest = tmp_cheater->m_cdkey_digest;
	m_cheater_name	= tmp_cheater->ps ? tmp_cheater->ps->getName() : "unknown";
	NET_Packet ssr_packet;
	ssr_packet.w_begin				(M_GAMEMESSAGE); 
	ssr_packet.w_u32				(GAME_EVENT_MAKE_DATA);
	ssr_packet.w_u8					(e_screenshot_request);	//make screenshot

	//alligning size to GAME_EVENT_PLAYER_KILLED message size
	ssr_packet.w_u16		(u16(Random.randI(2)));	//food for thought for crackers :)
	ssr_packet.w_u16		(u16(Random.randI(2)));
	ssr_packet.w_u16		(u16(Random.randI(2)));
	ssr_packet.w_u8			(u8(Random.randI(2)));

	Level().Server->SecureSendTo	(tmp_cheater, ssr_packet, net_flags(TRUE, TRUE));
	
	file_transfer::receiving_state_callback_t receiving_cb =
		fastdelegate::MakeDelegate(this, &clientdata_proxy::download_screenshot_callback);
	if (my_proxy_mem_file.size())
		my_proxy_mem_file.clear();
	m_first_receive = true;
	m_receiver = m_ft_server->start_receive_file(my_proxy_mem_file, m_chearer_id, receiving_cb);
}

void clientdata_proxy::make_config_dump(ClientID const & admin_id, ClientID const & cheater_id)
{
	m_admin_id = admin_id;
	m_chearer_id = cheater_id;
	xrClientData* tmp_cheater = static_cast<xrClientData*>(
		Level().Server->GetClientByID(m_chearer_id));
	if (!tmp_cheater)
	{
		Msg("! ERROR: SV: client [%u] not found ...", cheater_id.value());
		return;
	}
	if (m_ft_server->is_receiving_active(cheater_id))
	{
		Msg("! Receiving from client [%u] already active, please try later", cheater_id.value());
		return;
	}
	m_cheater_digest = tmp_cheater->m_cdkey_digest;
	m_cheater_name	= tmp_cheater->ps ? tmp_cheater->ps->getName() : "unknown";
	NET_Packet ssr_packet;
	ssr_packet.w_begin				(M_GAMEMESSAGE); 
	ssr_packet.w_u32				(GAME_EVENT_MAKE_DATA);
	ssr_packet.w_u8					(e_configs_request);	//make screenshot

	//alligning size to GAME_EVENT_PLAYER_KILLED message size
	ssr_packet.w_u16		(u16(Random.randI(2)));	//food for thought for crackers :)
	ssr_packet.w_u16		(u16(Random.randI(2)));
	ssr_packet.w_u16		(u16(Random.randI(2)));
	ssr_packet.w_u8			(u8(Random.randI(2)));

	Level().Server->SecureSendTo	(tmp_cheater, ssr_packet, net_flags(TRUE, TRUE));
	
	file_transfer::receiving_state_callback_t receiving_cb =
		fastdelegate::MakeDelegate(this, &clientdata_proxy::download_config_callback);
	if (my_proxy_mem_file.size())
		my_proxy_mem_file.clear();
	m_first_receive = true;
	m_receiver = m_ft_server->start_receive_file(my_proxy_mem_file, m_chearer_id, receiving_cb);

}

bool clientdata_proxy::is_active()
{
	return (m_ft_server->is_receiving_active(m_chearer_id) ||
		m_ft_server->is_transfer_active(m_admin_id, m_chearer_id));
}

void clientdata_proxy::notify_admin(clientdata_event_t event_for_admin, char const * reason)
{
	NET_Packet ssr_packet;
	ssr_packet.w_begin		(M_GAMEMESSAGE); 
	ssr_packet.w_u32		(GAME_EVENT_MAKE_DATA);
	ssr_packet.w_u8			(static_cast<u8>(event_for_admin));	//receive data
	ssr_packet.w_u32		(m_chearer_id.value());

	if ((event_for_admin == e_screenshot_response) ||
		(event_for_admin == e_configs_response))
	{
		ssr_packet.w_stringZ	(m_cheater_name);
	} else
	{
		ssr_packet.w_stringZ	(reason ? reason : "failed to download screenshot");
	}
	Level().Server->SendTo	(m_admin_id, ssr_packet, net_flags(TRUE, TRUE));
}


void clientdata_proxy::save_proxy_screenshot()
{
	game_cl_mp* clgame = smart_cast<game_cl_mp*>(Level().game);
	if (!clgame)
		return;

	string_path	screenshot_fn;
	string_path str_digest;

	LPCSTR dest_file_name = NULL;
	STRCONCAT(dest_file_name,
		clgame->make_file_name(m_cheater_name.c_str(), screenshot_fn),
		"_",
		(m_cheater_digest.size() ? 
			clgame->make_file_name(m_cheater_digest.c_str(), str_digest) :
			"nulldigest"
		)
	);
	SYSTEMTIME					date_time;
	GetLocalTime				(&date_time);
	clgame->generate_file_name	(screenshot_fn, dest_file_name, date_time);

	clgame->decompress_and_save_screenshot(
		screenshot_fn,
		my_proxy_mem_file.pointer(),
		my_proxy_mem_file.size(),
		m_receiver->get_user_param()
	);
}

void clientdata_proxy::save_proxy_config()
{
	game_cl_mp* clgame = smart_cast<game_cl_mp*>(Level().game);
	if (!clgame)
		return;

	string_path	config_fn;
	LPCSTR		fn_suffix = NULL;
	string_path dest_file_name;

	STRCONCAT(fn_suffix,
		clgame->make_file_name(m_cheater_name.c_str(), config_fn),
		".cltx");
		
	SYSTEMTIME					date_time;
	GetLocalTime				(&date_time);
	clgame->generate_file_name	(dest_file_name, fn_suffix, date_time);
	IWriter*	tmp_writer		= FS.w_open("$screenshots$", dest_file_name);
	if (!tmp_writer)
		return;
	tmp_writer->w_u32			(m_receiver->get_user_param());	//unpacked size
	tmp_writer->w				(
		my_proxy_mem_file.pointer(),
		my_proxy_mem_file.size());
	FS.w_close					(tmp_writer);
}

void clientdata_proxy::download_screenshot_callback(file_transfer::receiving_status_t status, 
													u32 downloaded, 
													u32 total)
{
	switch (status)
	{
	case file_transfer::receiving_data:
		{
			Msg("* downloaded %d from %d bytes of screenshot from client [%d]", downloaded, total, m_chearer_id);
			if (m_first_receive)
			{
				notify_admin(e_screenshot_response, "prepare for receive...");
				file_transfer::sending_state_callback_t sending_cb = 
					fastdelegate::MakeDelegate(
						this, &clientdata_proxy::upload_file_callback);
				m_ft_server->start_transfer_file(
					my_proxy_mem_file, 
					total,
					m_admin_id,
					m_chearer_id,
					sending_cb,
					m_receiver->get_user_param()
				);
				m_first_receive = false;
			}
		}break;
	case file_transfer::receiving_aborted_by_user:
		{
			FATAL("* download screenshot aborted by user...");
		}break;
	case file_transfer::receiving_aborted_by_peer:
		{
			Msg("* download screenshot aborted by peer [%u]", m_chearer_id);
			LPCSTR error_msg;
			char bufforint[16];
			STRCONCAT(error_msg, "download screenshot terminated by peer [",
				ultoa(m_chearer_id.value(), bufforint, 10), "]");
			notify_admin(e_screenshot_error_notif, error_msg);
		}break;
	case file_transfer::receiving_timeout:
		{
			LPCSTR error_msg = "* download screenshot incomplete - timeout";
			Msg(error_msg);
			notify_admin(e_screenshot_error_notif, error_msg);
		}break;
	case file_transfer::receiving_complete:
		{
			if (m_first_receive)
			{
				notify_admin(e_screenshot_response, "prepare for receive...");
				file_transfer::sending_state_callback_t sending_cb = 
					fastdelegate::MakeDelegate(
						this, &clientdata_proxy::upload_file_callback);
				m_ft_server->start_transfer_file(
					my_proxy_mem_file, 
					total,
					m_admin_id,
					m_chearer_id,
					sending_cb,
					m_receiver->get_user_param()
				);
				m_first_receive = false;
			}
			if (g_sv_mp_save_proxy_screenshots)
			{
				save_proxy_screenshot();
			}
		}break;
	};
}

void clientdata_proxy::download_config_callback(file_transfer::receiving_status_t status, 
													u32 downloaded, 
													u32 total)
{
	switch (status)
	{
	case file_transfer::receiving_data:
		{
			Msg("* downloaded %d from %d bytes of config from client [%d]", downloaded, total, m_chearer_id);
			if (m_first_receive)
			{
				notify_admin(e_configs_response, "prepare for receive...");
				file_transfer::sending_state_callback_t sending_cb = 
					fastdelegate::MakeDelegate(
						this, &clientdata_proxy::upload_file_callback);
				m_ft_server->start_transfer_file(
					my_proxy_mem_file, 
					total,
					m_admin_id,
					m_chearer_id,
					sending_cb,
					m_receiver->get_user_param()
				);
				m_first_receive = false;
			}
		}break;
	case file_transfer::receiving_aborted_by_user:
		{
			FATAL("* download config aborted by user...");
		}break;
	case file_transfer::receiving_aborted_by_peer:
		{
			Msg("* download config aborted by peer [%u]", m_chearer_id);
			LPCSTR error_msg;
			char bufforint[16];
			STRCONCAT(error_msg, "download config terminated by peer [",
				ultoa(m_chearer_id.value(), bufforint, 10), "]");
			notify_admin(e_configs_error_notif, error_msg);
		}break;
	case file_transfer::receiving_timeout:
		{
			LPCSTR error_msg = "* download config incomplete - timeout";
			Msg(error_msg);
			notify_admin(e_configs_error_notif, error_msg);
		}break;
	case file_transfer::receiving_complete:
		{
			if (m_first_receive)
			{
				notify_admin(e_configs_response, "prepare for receive...");
				file_transfer::sending_state_callback_t sending_cb = 
					fastdelegate::MakeDelegate(
						this, &clientdata_proxy::upload_file_callback);
				m_ft_server->start_transfer_file(
					my_proxy_mem_file, 
					total,
					m_admin_id,
					m_chearer_id,
					sending_cb,
					m_receiver->get_user_param()
				);
				m_first_receive = false;
			}
			if (g_sv_mp_save_proxy_configs)
			{
				save_proxy_config();
			}
		}break;
	};
}

void clientdata_proxy::upload_file_callback(file_transfer::sending_status_t status,
												  u32 uploaded,
												  u32 total)
{
	switch (status)
	{
	case file_transfer::sending_data:
		{
			Msg("* uploaded %d from %d bytes to client [%d]", uploaded, total, m_admin_id);
		}break;
	case file_transfer::sending_aborted_by_user:
		{
			FATAL("* upload file terminated by user ");
		}break;
	case file_transfer::sending_rejected_by_peer:
		{
			Msg("* upload file terminated by peer [%d]", m_admin_id);
		}break;
	case file_transfer::sending_complete:
		{
			Msg("* upload file to admin [%d] complete !", m_admin_id);
		}break;
	};
}