// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


/*
 * Layer 4 and Service example, ping server.
 *
 * This ping service expects pings, sends pongs back.
 *
 * To run this program, ensure there is a file "ping_service.cfg"
 * containing the location of the naming service (NSHost, NSPort)
 * in the working directory. The naming service must be running.
 */


// We're using the NeL Service framework, and layer 5
#include "nel/net/service.h"
#include "nel/misc/time_nl.h"
#include "nel/misc/displayer.h"
#include "nel/misc/command.h"
#include "nel/misc/hierarchical_timer.h"

using namespace std;
using namespace NLNET;
using namespace NLMISC;

uint32 NbId = 0;

//
TTime	pingDate;

void cbPong(CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
	uint32	counter;
	msgin.serial( counter );
	TTime	pingTime = CTime::getLocalTime()-pingDate;

	nlinfo("Received PONG %u (%u ms)", counter, pingTime);
}

void sendPing()
{
	pingDate = CTime::getLocalTime();
	uint32 counter = 0;
	CMessage msgout("PING");
	msgout.serial( counter );
	nlinfo( "Send PING 0");
	CUnifiedNetwork::getInstance()->send("PS", msgout);
}


//
void cbPos(CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
	CMessage msgout("ACK_POS");
	CUnifiedNetwork::getInstance()->send("PLS", msgout);

	TCPUCycle v1 = CTime::getPerformanceTime ();

	CMessage msgoutfe("POS", false, CMessage::UseDefault, NbId*8);

	uint64 id = rand();
	msgoutfe.serial (NbId);
	for (uint i = 0; i < NbId; i++)
	{
		msgoutfe.serial (id);
		id++;
	}

	TCPUCycle v2 = CTime::getPerformanceTime ();
	
	CUnifiedNetwork::getInstance()->send("FS", msgoutfe);
	
	TCPUCycle v3 = CTime::getPerformanceTime ();

	nlinfo( "Received POS, Sending POS to FS (serial %.2fs, send %.2fs)", CTime::ticksToSecond (v2-v1), CTime::ticksToSecond (v3-v2));
}

void cbAskVision(CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
	uint32 Value = '0ACE';

//	H_BEFORE (Vision);

	TCPUCycle v1 = CTime::getPerformanceTime ();

//	H_BEFORE (CMessage);
	CMessage msgout("VISION", false, CMessage::UseDefault, 10000000);
//	H_AFTER (CMessage);
//	H_BEFORE (serial);
	msgout.serial(NbId);
//	H_AFTER (serial);
//	H_BEFORE (serials);
	for (uint i = 0; i < NbId; i++)
		msgout.serial( Value );
//	H_AFTER(serials);
//	H_BEFORE (send);
	CUnifiedNetwork::getInstance()->send("FS", msgout);
//	H_AFTER (send);

	/*
	CMessage msgout("VISION");
	uint32 Nb = 10;

	for (uint j = 0; j < 1000; j++)
	{
		msgout.clear();
		msgout.setType("VISION");
		msgout.serial(Nb);
		for (uint i = 0; i < Nb; i++)
			msgout.serial( Value );
		CUnifiedNetwork::getInstance()->send("FS", msgout);
	}
	*/
	TCPUCycle v2 = CTime::getPerformanceTime ();

//	H_AFTER (Vision);

	// ca prend bcp de cpu un info...
	nlinfo("Sent Vision with %d values in %.2fms", NbId, CTime::ticksToSecond (v2-v1)*1000.0f);
}

//
void cbUpPS(const std::string &serviceName, TServiceId sid, void *arg)
{
	nlinfo("Ping Service connecting");
	sendPing();
}

void cbDownPS(const std::string &serviceName, TServiceId sid, void *arg)
{
	nlinfo("Ping Service disconnecting");
}

//
void cbUpFS(const std::string &serviceName, TServiceId sid, void *arg)
{
	nlinfo("F Service connecting");
	sendPing();
}

void cbDownFS(const std::string &serviceName, TServiceId sid, void *arg)
{
	nlinfo("F Service disconnecting");
}

//
void cbUpService(const std::string &serviceName, TServiceId sid, void *arg)
{
	nlinfo("Service %s %d is up", serviceName.c_str(), sid.get());

	CMessage msgout("TOTO");
	uint32 i = 10;
	msgout.serial(i);
	CUnifiedNetwork::getInstance()->send(sid, msgout);
}

void cbDownService(const std::string &serviceName, TServiceId sid, void *arg)
{
	nlinfo("Service %s %d is down", serviceName.c_str(), sid.get());
}


/*
 * Callback array for messages received from a client
 */
TUnifiedCallbackItem CallbackArray[] =
{
	{ "PONG", cbPong },
	{ "POS", cbPos },
	{ "ASK_VISION", cbAskVision }
};

void cbVar (CConfigFile::CVar &var)
{
	if (var.Name == "NbId") NbId = var.asInt ();
	else nlstop;
}

//
class CGPMService : public IService
{
public:

	bool	update()
	{
		static TTime	lastPing = CTime::getLocalTime();

		TTime	ctime = CTime::getLocalTime();
/*
		// check ping every 15 seconds
		if (ctime - lastPing > 15000)
		{
			sendPing();
			lastPing = ctime;
		}
*/

		return true;
	}

	/*
	 * Initialization
	 */
	void init()
	{
//		nlerror ("oups");
/*		//nlassert(false);
		char *p=0;
		p[0]=0;
		printf(p);
*/
		ConfigFile.setCallback ("NbId", cbVar);
		cbVar (ConfigFile.getVar ("NbId"));

		// Connect to the ping service
		CUnifiedNetwork	*instance = CUnifiedNetwork::getInstance();

		instance->setServiceUpCallback("PS", cbUpPS, NULL);
		instance->setServiceDownCallback("PS", cbDownPS, NULL);

		instance->setServiceUpCallback("FS", cbUpFS, NULL);
		instance->setServiceDownCallback("FS", cbDownFS, NULL);

		instance->setServiceUpCallback("*", cbUpService, NULL);
		instance->setServiceDownCallback("*", cbDownService, NULL);
	}
};


/*
 * Declare a service with the class IService, the names "PS" (short) and "ping_service" (long).
 * The port is automatically allocated (0) and the main callback array is CallbackArray.
 */
NLNET_SERVICE_MAIN( CGPMService, "GPMS", "gpm_service", 0, CallbackArray, "", "" )

NLMISC_COMMAND (wait, "", "<time>")
{
	if(args.size() != 1) return false;

	nlSleep (atoi (args[0].c_str()));

	return true;
}