/*
 *  Original work: Copyright (c) 2014, Oculus VR, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  RakNet License.txt file in the licenses directory of this source tree. An additional grant 
 *  of patent rights can be found in the RakNet Patents.txt file in the same directory.
 *
 *
 *  Modified work: Copyright (c) 2016-2017, SLikeSoft UG (haftungsbeschränkt)
 *
 *  This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
 *  license found in the license.txt file in the root directory of this source tree.
 */

// ----------------------------------------------------------------------
// RakNet version 1.0
// Filename ChatExample.cpp
// Very basic chat engine example
// ----------------------------------------------------------------------
#include "slikenet/MessageIdentifiers.h"

#include "slikenet/peerinterface.h"
#include "slikenet/peerinterface.h"
#include "slikenet/types.h"
#include "slikenet/GetTime.h"
#include "slikenet/BitStream.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <limits> // used for std::numeric_limits
#include "slikenet/sleep.h"
#include "slikenet/Gets.h"
#include "slikenet/linux_adapter.h"
#include "slikenet/osx_adapter.h"

int main(void)
{
	// Pointers to the interfaces of our server and client.
	// Note we can easily have both in the same program
	SLNet::RakPeerInterface *client;
	SLNet::RakPeerInterface *server;
	bool b;
	char str[256];
	char serverPort[30], clientPort[30];
	SLNet::TimeMS quitTime;
	// Holds packets
	SLNet::Packet* p;

	printf("A client / server sample showing how clients can broadcast offline packets\n");
	printf("to find active servers.\n");
	printf("Difficulty: Beginner\n\n");

	printf("Instructions:\nRun one or more servers on the same port.\nRun a client and it will get pongs from those servers.\n");
	printf("Run as (s)erver or (c)lient?\n");
	Gets(str, sizeof(str));

	if (str[0]=='s' || str[0]=='S')
	{
		client=0;
		server= SLNet::RakPeerInterface::GetInstance();
		// A server
		printf("Enter the server port\n");
		Gets(serverPort,sizeof(serverPort));
		if (serverPort[0]==0)
			strcpy_s(serverPort, "60001");
		const int intServerPort = atoi(serverPort);
		if ((intServerPort < 0) || (intServerPort > std::numeric_limits<unsigned short>::max())) {
			printf("Specified server port %d is outside valid bounds [0, %u]", intServerPort, std::numeric_limits<unsigned short>::max());
			return 2;
		}

		printf("Starting server.\n");
		// The server has to be started to respond to pings.
		SLNet::SocketDescriptor socketDescriptor(static_cast<unsigned short>(intServerPort),0);
		socketDescriptor.socketFamily=AF_INET; // Only IPV4 supports broadcast on 255.255.255.255
		b = server->Startup(2, &socketDescriptor, 1)== SLNet::RAKNET_STARTED;
		server->SetMaximumIncomingConnections(2);
		if (b)
			printf("Server started, waiting for connections.\n");
		else
		{ 
			printf("Server failed to start.  Terminating.\n");
			exit(1);
		}
	}
	else
	{
		client= SLNet::RakPeerInterface::GetInstance();
		server=0;

		// Get our input
		printf("Enter the client port to listen on, or 0\n");
		Gets(clientPort,sizeof(clientPort));
		if (clientPort[0]==0)
			strcpy_s(clientPort, "60000");
		const int intClientPort = atoi(clientPort);
		if ((intClientPort < 0) || (intClientPort > std::numeric_limits<unsigned short>::max())) {
			printf("Specified client port %d is outside valid bounds [0, %u]", intClientPort, std::numeric_limits<unsigned short>::max());
			return 3;
		}
		unsigned short cPort = static_cast<unsigned short>(intClientPort);

		printf("Enter the port to ping\n");
		Gets(serverPort,sizeof(serverPort));
		if (serverPort[0]==0)
			strcpy_s(serverPort, "60001");
		const int intServerPort = atoi(serverPort);
		if ((intServerPort < 0) || (intServerPort > std::numeric_limits<unsigned short>::max())) {
			printf("Specified server port %d is outside valid bounds [0, %u]", intServerPort, std::numeric_limits<unsigned short>::max());
			return 2;
		}
		unsigned short sPort = static_cast<unsigned short>(intServerPort);
		SLNet::SocketDescriptor socketDescriptor(cPort,0);
		socketDescriptor.socketFamily=AF_INET; // Only IPV4 supports broadcast on 255.255.255.255
		client->Startup(1, &socketDescriptor, 1);

		// Connecting the client is very simple.  0 means we don't care about
		// a connectionValidationInteger, and false for low priority threads
		// All 255's mean broadcast
		client->Ping("255.255.255.255", sPort, false);

		printf("Pinging\n");
	}

	printf("How many seconds to run this sample for?\n");
	Gets(str, sizeof(str));
	if (str[0]==0)
	{
		printf("Defaulting to 5 seconds\n");
		quitTime = SLNet::GetTimeMS() + 5000;
	}
	else
		quitTime = SLNet::GetTimeMS() + atoi(str) * 1000;

	// Loop for input
	while (SLNet::GetTimeMS() < quitTime)
	{
		if (server)
			p = server->Receive();
		else 
			p = client->Receive();

		if (p==0)
		{
			RakSleep(30);
			continue;
		}
		if (server)
			server->DeallocatePacket(p);
		else
		{
			if (p->data[0]==ID_UNCONNECTED_PONG)
			{
				SLNet::TimeMS time;
				SLNet::BitStream bsIn(p->data,p->length,false);
				bsIn.IgnoreBytes(1);
				bsIn.Read(time);
				printf("Got pong from %s with time %i\n", p->systemAddress.ToString(), SLNet::GetTimeMS() - time);
			}
			else if (p->data[0]==ID_UNCONNECTED_PING)
			{
				printf("ID_UNCONNECTED_PING from %s\n",p->guid.ToString());
			}
			else if (p->data[0]==ID_UNCONNECTED_PING_OPEN_CONNECTIONS)
			{
				printf("ID_UNCONNECTED_PING_OPEN_CONNECTIONS from %s\n",p->guid.ToString());
			}
			client->DeallocatePacket(p);
		}

		RakSleep(30);
	}

	// We're done with the network
	if (server)
		SLNet::RakPeerInterface::DestroyInstance(server);
	if (client)
		SLNet::RakPeerInterface::DestroyInstance(client);

	return 0;
}
