//------------------------------------------------------------
// Tony Hyun Kim
// Spring 2007
// 18.354 Project: Lattice gas
// Detailed reflection model
//------------------------------------------------------------

#include "DetailedReflection.h"

void ProcessDetailedReflection(NODE* np, int dir)
{
	static int chosenDir;
	static int dice;

	if( !CheckPosByte(np->current.occ,dir) )
		return;
	else
	{
		chosenDir = -1;
		dice = rand()%10;
		switch(ComputeWallType(np,dir))
		{
		//------------------------------------------------------------
		// One connection missing
		//------------------------------------------------------------
		case PIN:
			switch(dice)
			{
			case 0:
			case 1:
				chosenDir = (dir+3)%6;
				break;
			case 2:
				chosenDir = (dir+2)%6;
				break;
			case 3:
				chosenDir = (dir+4)%6;
				break;
			case 4:
			case 5:
			case 6:
				chosenDir = (dir+1)%6;
				break;
			case 7:
			case 8:
			case 9:
				chosenDir = (dir+5)%6;
				break;
			}
			break;

		//------------------------------------------------------------
		// Two connections missing
		//------------------------------------------------------------
		case WALL_DOWNSLOPE:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+2)%6;
				break;
			case 1:
				chosenDir = (dir+3)%6;
				break;
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
				chosenDir = (dir+4)%6;
				break;
			case 7:
			case 8:
			case 9:
				chosenDir = (dir+5)%6;
				break;
			}
			break;

		case WALL_UPSLOPE:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+4)%6;
				break;
			case 1:
				chosenDir = (dir+3)%6;
				break;
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
				chosenDir = (dir+2)%6;
				break;
			case 7:
			case 8:
			case 9:
				chosenDir = (dir+1)%6;
				break;
			}
			break;

		case FUNNEL_UPSLOPE:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+3)%6;
				break;
			case 1:
			case 2:
				chosenDir = (dir+1)%6;
				break;
			case 3:
			case 4:
				chosenDir = (dir+4)%6;
				break;
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:
				chosenDir = (dir+5)%6;
				break;
			}	
			break;

		case FUNNEL_DOWNSLOPE:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+3)%6;
				break;
			case 1:
			case 2:
				chosenDir = (dir+5)%6;
				break;
			case 3:
			case 4:
				chosenDir = (dir+2)%6;
				break;
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:
				chosenDir = (dir+1)%6;
				break;
			}
			break;

		//------------------------------------------------------------
		// Three connections missing
		//------------------------------------------------------------
		case THREEWAY:
			switch(dice)
			{
			case 0:
			case 1:
			case 2:
			case 3:
				chosenDir = (dir+1)%6;
				break;
			case 4:
			case 5:
				chosenDir = (dir+3)%6;
				break;
			case 6:
			case 7:
			case 8:
			case 9:
				chosenDir = (dir+5)%6;
				break;
			}
			break;

		case CORNER:
			switch(dice)
			{
			case 0:
			case 1:
				chosenDir = (dir+2)%6;
				break;
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
				chosenDir = (dir+3)%6;
				break;
			case 8:
			case 9:
				chosenDir = (dir+4)%6;
				break;
			}
			break;

		case CORNER_DOWN:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+3)%6;
				break;
			case 1:
			case 2:
			case 3:
				chosenDir = (dir+4)%6;
				break;
			case 4:
			case 5:
			case 6:
			case 7:
			case 8: 
			case 9:
				chosenDir = (dir+5)%6;
				break;
			}
			break;
		
		case CORNER_UP:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+3)%6;
				break;
			case 1:
			case 2:
			case 3:
				chosenDir = (dir+2)%6;
				break;
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:
				chosenDir = (dir+1)%6;
				break;
			}
			break;

		case FORK_UP:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+4)%6;
				break;
			case 1:
			case 2:
				chosenDir = (dir+3)%6;
				break;
			default:
				chosenDir = (dir+1)%6;
				break;
			}
			break;

		case FORK_DOWN:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+2)%6;
				break;
			case 1:
			case 2:
				chosenDir = (dir+3)%6;
				break;
			default:
				chosenDir = (dir+5)%6;
				break;
			};
			break;
		
		//------------------------------------------------------------
		// Four connections missing
		//------------------------------------------------------------
		case PATH_UP:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+3)%6;
				break;
			default:
				chosenDir = (dir+1)%6;
				break;
			}
			break;

		case PATH_DOWN:
			switch(dice)
			{
			case 0:
				chosenDir = (dir+3)%6;
				break;
			default:
				chosenDir = (dir+5)%6;
				break;
			}
			break;

		case SHARPPATH_DOWN:
			switch(dice)
			{
			case 0:
			case 1:
				chosenDir = (dir+3)%6;
				break;
			default:
				chosenDir = (dir+4)%6;
				break;
			}
			break;

		case SHARPPATH_UP:
			switch(dice)
			{
			case 0:
			case 1:
				chosenDir = (dir+3)%6;
				break;
			default:
				chosenDir = (dir+2)%6;
				break;
			}
			break;

		//------------------------------------------------------------
		// Five connections missing
		//------------------------------------------------------------
		case DEAD_END:
			chosenDir = (dir+3)%6;
			break;

		}

		if ( (chosenDir >= 0) && (np->neighbors[chosenDir]) )
		{
			np->neighbors[chosenDir]->next.occ |= g_PosFlags[chosenDir];
			if( CheckPosByte(np->current.type, dir) )
				np->neighbors[chosenDir]->next.type |= g_PosFlags[chosenDir];
		}
	}
}

walltype ComputeWallType(const NODE* np, int dir)
{
	static int cMissing;
	static int dir_p1, dir_p2, dir_m1, dir_m2;

	static walltype result = UNKNOWN;

	cMissing = 6 - CountNeighbors(np);
	dir_p1 = (dir+1)%6;
	dir_p2 = (dir+2)%6;
	dir_m1 = (dir+5)%6;
	dir_m2 = (dir+4)%6;

	switch(cMissing)
	{
	case 1:
		result = PIN;
		break;

	case 2:
		if(!np->neighbors[dir_p1])		
			result = WALL_DOWNSLOPE;
		else if(!np->neighbors[dir_m1])
			result = WALL_UPSLOPE;
		else if(!np->neighbors[dir_p2])
			result = FUNNEL_UPSLOPE;
		else
			result = FUNNEL_DOWNSLOPE;
		break;

	case 3:
		if( np->neighbors[dir_p1] && np->neighbors[dir_m1] )
			result = THREEWAY;
		else if( np->neighbors[dir_p2] && np->neighbors[dir_m2] )
			result = CORNER;
		else if( np->neighbors[dir_p1] && np->neighbors[dir_p2] )
			result = CORNER_UP;
		else if( np->neighbors[dir_m1] && np->neighbors[dir_m2] )
			result = CORNER_DOWN;
		else if( np->neighbors[dir_p1] && np->neighbors[dir_m2] )
			result = FORK_UP;
		else
			result = FORK_DOWN;
		break;

	case 4:
		if( np->neighbors[dir_p1] )
			result = PATH_UP;
		else if( np->neighbors[dir_m1] )
			result = PATH_DOWN;
		else if( np->neighbors[dir_p2] )
			result = SHARPPATH_UP;
		else
			result = SHARPPATH_DOWN;
		break;

	case 5:
		result = DEAD_END;
		break;

	}
	return result;
}
