/*
 *  Copyright (c) 2014, Oculus VR, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant 
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include "RandSync.h"
#include "BitStream.h"
#include <limits>
#include <limits.h>

namespace RakNet
{

RakNetRandomSync::RakNetRandomSync()
{
	seed = (uint32_t) -1;
	callCount = 0;
	usedValueBufferCount = 0;
}
RakNetRandomSync::~RakNetRandomSync()
{
}
void RakNetRandomSync::SeedMT( uint32_t _seed )
{
	seed = _seed;
	rnr.SeedMT( seed );
	callCount = 0;
	usedValueBufferCount = 0;
}
void RakNetRandomSync::SeedMT( uint32_t _seed, uint32_t skipValues )
{
	SeedMT(_seed);
	Skip(skipValues);
}
float RakNetRandomSync::FrandomMT( void )
{
	return ( float ) ( ( double ) RandomMT() / (double) UINT_MAX );
}
unsigned int RakNetRandomSync::RandomMT( void )
{
	if (usedValueBufferCount > 0)
	{
		--usedValueBufferCount;
		if (usedValueBufferCount < usedValues.Size())
		{
			// The remote system had less calls than the current system, so return values from the past
			return usedValues[usedValues.Size()-usedValueBufferCount-1];
		}
		else
		{
			// Unknown past value, too far back
			// Return true random
			return rnr.RandomMT();
		}
	}
	else
	{
		// Get random number and store what it is
		usedValues.Push(rnr.RandomMT(), _FILE_AND_LINE_);
		++callCount;
		while (usedValues.Size()>64)
			usedValues.Pop();		
		return usedValues[usedValues.Size()-1];		
	}
}
uint32_t RakNetRandomSync::GetSeed( void ) const
{
	return seed;
}
uint32_t RakNetRandomSync::GetCallCount( void ) const
{
	return callCount;
}
void RakNetRandomSync::SetCallCount( uint32_t i )
{
	callCount = i;
}
void RakNetRandomSync::SerializeConstruction(RakNet::BitStream *constructionBitstream)
{
	constructionBitstream->Write(seed);
	constructionBitstream->Write(callCount);
}
bool RakNetRandomSync::DeserializeConstruction(RakNet::BitStream *constructionBitstream)
{
	uint32_t _seed;
	uint32_t _skipValues;
	constructionBitstream->Read(_seed);
	bool success = constructionBitstream->Read(_skipValues);
	if (success)
		SeedMT(_seed, _skipValues);
	return success;
}
void RakNetRandomSync::Serialize(RakNet::BitStream *outputBitstream)
{
	outputBitstream->Write(callCount);
}
void RakNetRandomSync::Deserialize(RakNet::BitStream *outputBitstream)
{
	uint32_t _callCount;
	outputBitstream->Read(_callCount);
	if (_callCount < callCount )
	{
		// We locally read more values than the remote system
		// The next n calls should come from buffered values
		usedValueBufferCount = callCount - _callCount;
	}
	else if (_callCount > callCount )
	{
		// Remote system read more values than us
		uint32_t diff = _callCount - callCount;
		if (diff <= usedValueBufferCount)
			usedValueBufferCount -= diff;
		if (diff > 0)
			Skip(diff);
	}
}
void RakNetRandomSync::Skip( uint32_t count )
{
	for (uint32_t i = 0; i < count; i++)
		rnr.RandomMT();
	callCount+=count;
}

} // namespace RakNet

/*
RakNetRandomSync r1, r2;
BitStream bsTest;
r1.SeedMT(0);
r1.SerializeConstruction(&bsTest);
bsTest.SetReadOffset(0);
r2.DeserializeConstruction(&bsTest);
printf("1. (r1) %f\n", r1.FrandomMT());
printf("1. (r2) %f\n", r2.FrandomMT());
printf("2. (r1) %f\n", r1.FrandomMT());
printf("2. (r2) %f\n", r2.FrandomMT());
printf("3. (r1) %f\n", r1.FrandomMT());
printf("3. (r2) %f\n", r2.FrandomMT());
printf("4. (r1) %f\n", r1.FrandomMT());
printf("4. (r2) %f\n", r2.FrandomMT());
printf("5. (r2) %f\n", r2.FrandomMT());
printf("6. (r2) %f\n", r2.FrandomMT());
printf("7. (r2) %f\n", r2.FrandomMT());
bsTest.Reset();
r1.Serialize(&bsTest);
bsTest.SetReadOffset(0);
r2.Deserialize(&bsTest);
printf("Synched r2 to match r1\n");
printf("5. (r1) %f\n", r1.FrandomMT());
printf("5. (r2) %f --Should continue sequence from 5-\n", r2.FrandomMT());
printf("6. (r1) %f\n", r1.FrandomMT());
printf("6. (r2) %f\n", r2.FrandomMT());
printf("7. (r1) %f -- Extra call to r1, no r2 equivalent --\n", r1.FrandomMT());
printf("8. (r1) %f -- Extra call to r1, no r2 equivalent --\n", r1.FrandomMT());
bsTest.Reset();
r1.Serialize(&bsTest);
bsTest.SetReadOffset(0);
r2.Deserialize(&bsTest);
printf("Synched r2 to match r1\n");
printf("9. (r1) %f\n", r1.FrandomMT());
printf("9. (r2) %f --SKIPPED 7,8, SHOULD MATCH 9-\n", r2.FrandomMT());
*/