//------------------------------------------------------------
// Tony Hyun Kim
// Spring 2007
// 18.354 Project: Lattice gas
// NODE IMPLEMENTATION
//------------------------------------------------------------

#include "node.h"
#include "DetailedReflection.h"

int CountNeighbors(const NODE* np)
{
	int count = 0;
	for(int i=0; i<6; i++)
		if( np->neighbors[i] )
			count++;
	return count;
}

//------------------------------------------------------------
// NODE
//------------------------------------------------------------
node::node() {}
node::node(D3DXVECTOR2& p)
{
	pos = p;
	for(int i=0; i<6; i++)
		neighbors[i] = 0;
	current.occ  = POS_ALL_OFF;
	current.type = TYPE_ALL_0;
	next.occ  = POS_ALL_OFF;
	next.type = TYPE_ALL_0;
}

node::node(float x, float y)
{
	pos = D3DXVECTOR2(x,y);
	for(int i=0; i<6; i++)
		neighbors[i] = 0;
	current.occ  = POS_ALL_OFF;
	current.type = TYPE_ALL_0;
	next.occ  = POS_ALL_OFF;
	next.type = TYPE_ALL_0;
}

//------------------------------------------------------------
// COLLISION IMPLEMENTATION
//------------------------------------------------------------

#define COLL_TWO_ZERO_THREE 0x09 // 0000-1001 
#define COLL_TWO_ONE_FOUR   0x12 // 0001-0010
#define COLL_TWO_TWO_FIVE   0x24 // 0010-0100

void TwoBodyCollision(OCCUPATION& occ)
{
	int dice = rand()%2;
	// Head on collision?
	switch(occ.occ)
	{
	case COLL_TWO_ZERO_THREE:
		if(dice == 0)
		{
			occ.occ = COLL_TWO_ONE_FOUR;
			if( rand()%2 == 0 )
				BitRotateShiftLeft(occ.type,1);
			else
				BitRotateShiftRight(occ.type,2);
		}
		else
		{
			occ.occ = COLL_TWO_TWO_FIVE;
			if( rand()%2 == 0 )
				BitRotateShiftLeft(occ.type,2);
			else
				BitRotateShiftRight(occ.type,1);
		}
		break;
	case COLL_TWO_ONE_FOUR:
		if(dice == 0)
		{
			occ.occ = COLL_TWO_ZERO_THREE;
			if( rand()%2 == 0 )
				BitRotateShiftRight(occ.type,1);
			else
				BitRotateShiftLeft(occ.type,2);
		}
		else
		{
			occ.occ = COLL_TWO_TWO_FIVE;
			if( rand()%2 == 0)
				BitRotateShiftLeft(occ.type,1);
			else
				BitRotateShiftRight(occ.type,2);
		}
		break;
	case COLL_TWO_TWO_FIVE:
		if(dice == 0)
		{
			occ.occ = COLL_TWO_ZERO_THREE;
			if( rand()%2 == 0)
				BitRotateShiftRight(occ.type,2);
			else
				BitRotateShiftLeft(occ.type,1);
		}
		else
		{
			occ.occ = COLL_TWO_ONE_FOUR;
			if( rand()%2 == 0)
				BitRotateShiftRight(occ.type,1);
			else
				BitRotateShiftLeft(occ.type,2);
		}
		break;

	default:
		// No head on collision, simply find the two incoming particles
		// and swap identities
		{
			int i, first, second;
			for(i=0; i<6; i++)
			{
				if( CheckPosByte(occ.occ,i) )
				{
					first = i;
					break;
				}
			}

			for(i = first+1; i<6; i++)
			{
				if( CheckPosByte(occ.occ,i))
				{
					second = i;
					break;
				}
			}

			BitSwap(occ.type, first, second);
		}
	}
}

// three particle collision
#define COLL_THREE_ODDS    0x2a // 0010-1010 (1,3,5 OCCUPIED/ 0,2,4 ABSENT)
#define COLL_THREE_EVENS   0x15 // 0001-0101 (0,2,4 OCCUPIED/ 1,3,5 ABSENT)

void ThreeBodyCollision(OCCUPATION& occ)
{
	if(CheckByte(occ.occ,COLL_THREE_ODDS))
	{
		occ.occ = COLL_THREE_EVENS;
		BitRotateShiftLeft(occ.type,3);
	}
	else if(CheckByte(occ.occ,COLL_THREE_EVENS))
	{
		occ.occ = COLL_THREE_ODDS;
		BitRotateShiftLeft(occ.type,3);
	}
}

void node::CalculateNext()
{
	static int dir, opp_dir, dice;
	static int cOccupants;
	static int randomDir; // Used for random reflection model

	// first we do the collision calculations
	cOccupants = CountOccupants(current.occ);
	switch(cOccupants)
	{
	case 2:
		TwoBodyCollision(current);
		break;
	case 3:
		ThreeBodyCollision(current);
		break;
	}

	// Now we update the NEIGHBORS with the processed information
	for(dir=0; dir<6; dir++)
	{
		// There's somebody in the particular direction
		if(neighbors[dir])
		{
			neighbors[dir]->next.occ  |= current.occ  & g_PosFlags[dir];
			neighbors[dir]->next.type |= current.type & g_PosFlags[dir];
		}
		else
		{
			//------------------------------------------------------------
			// BOUNDARY CONDITIONS
			//------------------------------------------------------------

			//------------------------------------------------------------
			// DIRECT REFLECTION MODEL
			//------------------------------------------------------------
			/*
			opp_dir = (dir+3)%6;
			if( neighbors[opp_dir] && CheckPosByte(current.occ,dir) )
			{
				neighbors[opp_dir]->next.occ  |= g_PosFlags[opp_dir];
				if( CheckPosByte(current.type, dir) )
					neighbors[opp_dir]->next.type |= g_PosFlags[opp_dir];
			}
			*/

			//------------------------------------------------------------
			// RANDOM REFLECTION MODEL
			//------------------------------------------------------------
			/*
			if( CheckPosByte(current.occ,dir) )
			{
				do
				{
					randomDir = rand()%6;
				} while ( (neighbors[randomDir] == 0) || 
						  (CheckPosByte(neighbors[randomDir]->next.occ,randomDir))  );
				neighbors[randomDir]->next.occ |= g_PosFlags[randomDir];
				if( CheckPosByte(current.type, dir) )
						neighbors[randomDir]->next.type |= g_PosFlags[randomDir];
			}
			*/

			//------------------------------------------------------------
			// DETAILED REFLECTION MODEL
			//------------------------------------------------------------
			ProcessDetailedReflection(this,dir);
		}
	}
}

void node::Update()
{
	current   = next;
	next.occ  = POS_ALL_OFF;
	next.type = TYPE_ALL_0;
}