/*
	ZNetBandwidthMeter.cpp
	Author: Patrick Baggett	<ptbaggett@762studios.com>
	Created: 7/10/2013

	Purpose:

	** NOT PART OF PUBLIC SDK **
	This class is not part of the public SDK; its fields and methods are not present
	in the documentation and cannot be guaranteed in future revisions.
	** NOT PART OF PUBLIC SDK **
	
	Bandwidth metering using a simple token bucket algorithm. A single value is
	metered, so a incoming / outgoing each need an instance.

	License:
	
	Copyright 2013, 762 Studios
*/

#include <ZNet/ZNetBandwidthMeter.hpp>

/*************************************************************************/

void ZNetBandwidthMeter::SetLimit(uint32_t newLimit)
{
	limit = newLimit;
	
	//Clamp existing token bucket
	if(tokens > newLimit)
		tokens = newLimit;

}

/*************************************************************************/
	
void ZNetBandwidthMeter::Reset(uint64_t newStartTime)
{
	lastTime = newStartTime;
	tokens = limit;
}

/*************************************************************************/
		
bool ZNetBandwidthMeter::TryAllocate(uint32_t bytes)
{
	if(bytes <= tokens)
	{
		tokens -= bytes;
		return true;
	}
	
	//Not enough
	return false;
}

/*************************************************************************/

void ZNetBandwidthMeter::Update(uint64_t newTime)
{	
	//Sanity check -- the time _did_ monotonically increase, right?
	if(newTime > lastTime)
	{
		uint32_t delta = (uint32_t)(newTime - lastTime);
		
		//If less than a second has passed...
		if(delta < 1000)
		{
			//We could do: delta / 1000.0 * limit, but that involves int -> float conversions
			//instead we do u32 x u32 = u64 multiply and divide by 1000, keeping it all in fixed
			//point happiness. Because delta < 1000, dividing by 1000 should put it back in the
			//range of a u32.
			uint32_t amount = (uint64_t)delta * (uint64_t)limit / 1000;
			
			//Add newly generated tokens, but clamp to the limit.
			tokens += amount;
			if(tokens > limit)
				tokens = limit;
		}
		else //More than a second, so restore all tokens
			tokens = limit;
			
		//Save this as the new time
		lastTime = newTime;
	}
}
