// ----------------------------------------------------------------------
// RakNet version 1.405
// ClientFileVerification.cpp
// Created by Rakkar Software (rakkar@jenkinssoftware.com) December 1, 2003
// Shows how to verify that a file on the client matches a file on the server.
// ----------------------------------------------------------------------

#include "RakPeerInterface.h"
#include "RakNetworkFactory.h"
#include "SHA1.h"
#include "BitStream.h"
#include "FileVerifier.h"
#include "MessageIdentifiers.h"
#include "RakSleep.h"
using namespace RakNet;

#include <cstdio>

#ifdef WIN32
#include "Kbhit.h"
#else
#include "Kbhit.h"
#endif

// RPC callback prototype
void VerifyFiles(RPCParameters *rpcParameters);

// Derive a new class to handle unknown files
class TestFileVerifier : public FileVerifier
{
	bool OnUnknownFile(char *filename)
	{
		printf("Client has the file %s which we don't know about.\nPress 'y' to allow the file, 'n' to fail.\n",filename);
		char buff[256];
		if(gets(buff)&&(buff[0]=='y'))
			return true;
		else
			return false;
	}
} fileVerifier;

RakPeerInterface *rakClient;
RakPeerInterface *rakServer;

// C callback to handle the remote procedure call that verifies the file on the client, and compares the value generated by the client
// To the value generated by the server.
void VerifyFiles(RPCParameters *rpcParameters)
{
	if (rpcParameters->sender==rakClient->GetSystemAddressFromIndex(0))
	{
		// Called from the server.  Serialize our validation data and send it to the server.

		// Write the output to a bitstream
		BitStream myBitStream;
		fileVerifier.Serialize(&myBitStream);

		// Call this same function on the server with the SHA1Code we are returning.  This function will then run the block below.
		rakClient->RPC("VerifyFiles", &myBitStream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
	}
	else
	{
		// Called by the client.  Get an SHA1 code from the same files the client used.  We could also have compared the code against a precalculated value if the
		// Server doesn't have the files the client used
		char *output;
		BitStream inputBS(rpcParameters->input, BITS_TO_BYTES(rpcParameters->numberOfBitsOfData), false);

		output=fileVerifier.DeserializeAndValidate(&inputBS);

		if (output==0)
			printf("Client file verification succeeded\n");
		else
			printf("Client file verification failed on %s\n", output);
	}
}


// Gets an SHA1 code for the files listed here
void ValidateFiles(void)
{
		fileVerifier.AddFileForVerification("ClientFileVerification.txt", true);
		fileVerifier.AddFileForVerification("Client File Verification.dsp", true);
		fileVerifier.AddFileForVerification("Client File Verification.vcproj", true);
		fileVerifier.AddFileForVerification("ClientFileVerification.cpp", true);
		fileVerifier.AddFileForVerification("FileVerifier.cpp", true);
		fileVerifier.AddFileForVerification("FileVerifier.h", true);
		// This should generate a warning message:
		fileVerifier.AddFileForVerification("blah.txt", true);
}


int main(void)
{
	char serverIP[30];

	rakClient=RakNetworkFactory::GetRakPeerInterface();
	rakServer=RakNetworkFactory::GetRakPeerInterface();

	REGISTER_STATIC_RPC(rakServer, VerifyFiles);
	REGISTER_STATIC_RPC(rakClient, VerifyFiles);

	char ch;
	char buff[256];

	printf("This sample demonstrates the FileVerifier class.\n");
	printf("The class takes a set of files, performs a hash on these files\n");
	printf("And returns if they are different or not.  The class itself is simple but\n");
	printf("This sample also shows how to use remote procedure calls\n");
	printf("Difficulty: Intermediate\n\n");

	printf("Hit [enter] to begin CRC calculations.\n");
	gets(buff);
	printf("Calculating CRCs...\n");
	// Validate files in the debug directory
	ValidateFiles();
	printf("Done.\n");

	printf("Hit 'c' to run as a client.  Hit 's' to run as a server. Hit 'q' to quit\n");

	while (1)
	{		
		gets(buff);
		ch = buff[0];

		if (ch=='c')
		{
			// Run as a client.  If you don't have another machine, just run 2 instances of this program and use "127.0.0.1"
			puts ("Enter server IP\n");
			gets(serverIP);
			if (serverIP[0]==0)
				strcpy(serverIP, "127.0.0.1");
			SocketDescriptor socketDescriptor(0,0);
			rakClient->Startup(1,30,&socketDescriptor,1);
			rakClient->Connect(serverIP, 2000, 0, 0);
			printf("Connecting client\n");
			break;
		}
		else if (ch=='s')
		{
			// Run as a server.
			SocketDescriptor socketDescriptor(2000,0);
			rakServer->Startup(32, 30, &socketDescriptor,1);
			rakServer->SetMaximumIncomingConnections(32);
			printf("Server started\n");
			break;
		}
		else if (ch=='q')
			return 0;
		else
		{
			printf("Bad input.  Enter 'c' 's' or 'q'.\n");
		}
	}

	printf("Entering main game loop.  Press any key to quit.\n");
	while (!kbhit())
	{
		Packet *packet;
		packet = rakServer->Receive();
		if (packet)
		{
			if (packet->data[0]==ID_NEW_INCOMING_CONNECTION)
			{
				// The server got a new client.  Use RPC to call VerifyFiles on the client.
				// We do not pass any data along with this call, send the call reliably, and only to the player that just connected.
				rakServer->RPC("VerifyFiles", 0, 0, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false, 0, UNASSIGNED_NETWORK_ID,0);
			}
			rakServer->DeallocatePacket(packet);
		}
		rakClient->DeallocatePacket(rakClient->Receive());

		RakSleep(30);
	}

	// Shutdown stuff.  It's ok to call disconnect on the server if we are a client and vice-versa
	rakServer->Shutdown(0);
	rakClient->Shutdown(0);

	return 0;
}
