//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "platform/platform.h"
#include "platform/event.h"
#include "platform/gameInterface.h"
#include "math/mRandom.h"

MRandomLCG gRandGen;
U32 gRandGenSeed = 1376312589;

void MRandomLCG::setGlobalRandSeed(U32 seed)
{
	if (Game->isJournalReading())
		Game->journalRead(&gRandGenSeed);
	else
	{
		gRandGenSeed = seed;
		if (Game->isJournalWriting())
			Game->journalWrite(gRandGenSeed);
	}

	//now actually set the seed
	gRandGen.setSeed(gRandGenSeed);
}

static U32 msSeed = 1376312589;

inline U32 generateSeed()
{
   // A very, VERY crude LCG but good enough to generate
   // a nice range of seed values
   msSeed = (msSeed * 0x015a4e35L) + 1;
   msSeed = (msSeed>>16)&0x7fff;
   return (msSeed);
}

//--------------------------------------
void MRandomGenerator::setSeed()
{
   setSeed(generateSeed());
}


//--------------------------------------
const S32 MRandomLCG::msQuotient  = S32_MAX / 16807L;
const S32 MRandomLCG::msRemainder = S32_MAX % 16807L;


//--------------------------------------
MRandomLCG::MRandomLCG()
{
   setSeed(generateSeed());
}

MRandomLCG::MRandomLCG(S32 s)
{
   setSeed(s);
}


//--------------------------------------
void MRandomLCG::setSeed(S32 s)
{
   mSeed = s;
}


//--------------------------------------
U32 MRandomLCG::randI()
{
   if ( mSeed <= msQuotient )
      mSeed = (mSeed * 16807L) % S32_MAX;
   else
   {
      S32 high_part = mSeed / msQuotient;
      S32 low_part  = mSeed % msQuotient;

      S32 test = (16807L * low_part) - (msRemainder * high_part);

      if ( test > 0 )
         mSeed = test;
      else
         mSeed = test + S32_MAX;

   }
   return mSeed;
}



//--------------------------------------
MRandomR250::MRandomR250()
{
   setSeed(generateSeed());
}

MRandomR250::MRandomR250(S32 s)
{
   setSeed(s);
}


//--------------------------------------
void MRandomR250::setSeed(S32 s)
{
   mSeed = s;
   MRandomLCG lcg( s );
   mIndex = 0;

   S32 j;
   for (j = 0; j < 250; j++)        // fill r250 buffer with bit values
      mBuffer[j] = lcg.randI();

   for (j = 0; j < 250; j++)        // set some MSBs to 1
      if ( lcg.randI() > 0x40000000L )
         mBuffer[j] |= 0x80000000L;


   U32 msb  = 0x80000000;           // turn on diagonal bit
   U32 mask = 0xffffffff;           // turn off the leftmost bits

   for (j = 0; j < 32; j++)
   {
      S32 k = 7 * j + 3;            // select a word to operate on
      mBuffer[k] &= mask;           // turn off bits left of the diagonal
      mBuffer[k] |= msb;            // turn on the diagonal bit
      mask >>= 1;
      msb  >>= 1;
   }
}


//--------------------------------------
U32 MRandomR250::randI()
{
   S32 j;

   // wrap pointer around
   if ( mIndex >= 147 ) j = mIndex - 147;
   else                 j = mIndex + 103;

   U32 new_rand = mBuffer[ mIndex ] ^ mBuffer[ j ];
   mBuffer[ mIndex ] = new_rand;

   // increment pointer for next time
   if ( mIndex >= 249 ) mIndex = 0;
   else                 mIndex++;

   return new_rand >> 1;
}