/****************************************************************************
 Module
 BumpTapeDetection.c

 Description
 Module for Bump, Tape, and other sensors and inputs, as well as color outputs.
 ****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "GlobalHeader.h"
#include "BumpTapeDetection.h"
#include "ADS12.h"
#include "SMGameState.h"

/*----------------------------- Module Defines ----------------------------*/
//Bump sensor
#define BUMP_MASK (BIT0HI|BIT1HI)
#define BUMP_DEBOUNCE 10 //debounce time in ms

//Tape sensor
#define TAPE_LEFT 0		//sensor index
#define TAPE_CENTER 1
#define TAPE_RIGHT 2
#define TAPE_BACK_LEFT 3
#define TAPE_BACK_RIGHT 4
static unsigned char TAPE_MAP[5] = {5, 6, 7, 2, 3}; //map senor index to AD ports
static unsigned int TAPE_MIN[5] = {500, 500, 500, 500, 500}; //define tape sensor thresholds
#define TAPE_FRONT 0	//set of front/back tape sensors
#define TAPE_BACK 1
#define TAPE_DEBOUNCE 20 //debounce time in ms

//Ball sensor
#define BALL_SENSOR 4			//AD input port
#define BALL_THRESHOLD 900		//sensor threshold
#define BALL_SAMPLE_TIME 50		//sample period in ms
#define BALL_DEBOUNCE_TIME 200	//debounce time in ms

//Input switches
#define COLOR_SWITCH BIT3HI     //port p
#define HALFCOURT_SWITCH BIT1HI
#define EXTRA_SWITCH BIT0HI

//LED outputs
#define RED_LED BIT6HI			//port U
#define GREEN_LED BIT7HI
#define LED_FLASH_PERIOD 500	//(half) period for blinking LEDs


/*---------------------------- Module Functions ---------------------------*/
static void CheckTape(unsigned char SensorNumber);
static unsigned char GetBumpState(void);
static unsigned char GetTapeState(unsigned char SensorNumber);


/*---------------------------- Module Variables ---------------------------*/
static unsigned char TapeCache[5] = {0, 0, 0, 0, 0}; //Whether we have seen each tape sensor
static unsigned char CacheCheckedFlag[2] = {0, 0};   //front, back Indicator for whether we have checked cache since it's changed
static unsigned char LastTapeState[5] = {0,0,0,0,0};
static unsigned int LastTapeTime[5] = {0,0,0,0,0};


/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
 BumpTapeInit

 Parameters
 none

 Returns
 none

 Description
 Port initialization for the module.
 ****************************************************************************/
void BumpTapeInit(void)
{
	unsigned int i;
	ADS12_Init("AAAAAAII"); //AD port
	DDRP &= ~(COLOR_SWITCH | HALFCOURT_SWITCH | EXTRA_SWITCH); //port P
	DDRU |= (RED_LED | GREEN_LED); //port U
	for(i=0;i<1000;i++); //brief blocking pause to allow AD port initialization.
}

/****************************************************************************
 Function
 ReadColor

 Parameters
 none

 Returns
 Team color based on Team Color Switch.

 Description
 Checks team color switch and sets rope lights based on switch setting
 ****************************************************************************/
TeamColor_t ReadColor(void)
{
	//return TeamRed; //override
	static unsigned char LEDsOn = 1;
	static unsigned int LastTime;
	unsigned int CurrentTime = TMRS12_GetTime();

	if (QueryGameSM() != Game_On) //if game not on, flash LEDs
	{
		if (CurrentTime-LastTime > LED_FLASH_PERIOD)
		{
			LEDsOn = 1 - LEDsOn;
			LastTime = CurrentTime;
		}
	}
	else
	{
		LEDsOn = 1;
	}

	if ((PTP & COLOR_SWITCH) == 0) //TeamRed
	{
		if (LEDsOn)	PTU |= RED_LED; //turn red on, if LEDs on
		else PTU &= ~RED_LED;
		PTU &= ~GREEN_LED;			//turn green off.
		return TeamRed;
	}else						//TeamGreen
	{
		PTU &= ~RED_LED;			//turn red off
		if (LEDsOn) PTU |= GREEN_LED; //turn green on, if LEDs on
		else PTU &= ~GREEN_LED;
		return TeamGreen;
	}
}

/****************************************************************************
 Function
 TurnOnLEDs

 Parameters
 none

 Returns
 none

 Description
 Turns on both colors of rope lights (for debugging).
 ****************************************************************************/
void TurnOnLEDs (void)
{
	PTU |= (RED_LED | GREEN_LED);
}

/****************************************************************************
 Function
 ReadHalfCourt

 Parameters
 none

 Returns
 1 if switch is set to half-court mode, 0 otherwise.

 Description
 Checks Half Court Switch
 ****************************************************************************/
unsigned char ReadHalfCourt(void)
{
	//return TeamRed; //override

	return ((PTP & HALFCOURT_SWITCH) == HALFCOURT_SWITCH);
}

/****************************************************************************
 Function
 GetBumpState

 Parameters
 none

 Returns
 1 if a bumper is depressed, 0 otherwise

 Description
 Checks to see if a bumper is depressed.  Does not try to cheer bumper up.
 ****************************************************************************/
static unsigned char GetBumpState(void)
{
	return ((PTAD & BUMP_MASK) != 0);
}

/****************************************************************************
 Function
 CheckBumpSensors

 Parameters
 none

 Returns
 1 if there has just been a bumper depressed, 0 otherwise

 Description
 Checks for a new bump event
 ****************************************************************************/
unsigned char CheckBumpSensors(void)
{
	static unsigned char LastState = 0;
	static unsigned int LastTime;
	static unsigned int CurrentTime;
	CurrentTime = TMRS12_GetTime();
	if (CurrentTime - LastTime > BUMP_DEBOUNCE) //only checks every DEBOUNCE
	{
		unsigned char CurrentBumpState = GetBumpState();
		LastTime = CurrentTime;
		if (CurrentBumpState != LastState) //if state has changed
		{
			LastState = CurrentBumpState;
			return CurrentBumpState;		//return new state
		}
	}
	return 0;								//otherwise return no new bump.
}

/****************************************************************************
 Function
 CheckForBall

 Parameters
 none

 Returns
 1 if there is a ball at gate (for DEBOUNCE_TIME), 0 otherwise.

 Description
 Checks tape sensor to see if a ball is at gate
 Notes
 checks status every BALL_SAMPLE_TIME, and has a BALL_DEBOUNCE_TIME
 debounce.  Only debounces the presence of a ball, not the disappearance.
 ****************************************************************************/

unsigned char CheckForBall(void)
{
	static unsigned char LastBallState = 0;
	static unsigned char DebouncedState = 0;
	static unsigned int LastTime;
	static unsigned int FirstTime;
	unsigned int CurrentTime = TMRS12_GetTime();
	if (CurrentTime - LastTime > BALL_SAMPLE_TIME) //check every SAMPLE_TIME
	{
		unsigned char CurrentBallState = (ADS12_ReadADPin(BALL_SENSOR) < BALL_THRESHOLD);
		if (CurrentBallState == LastBallState) //if state is still the same
		{
			if (CurrentTime - FirstTime > BALL_DEBOUNCE_TIME) //if DEBOUNCE time passed
			{
				DebouncedState = CurrentBallState;
			}
		}
		else //if state not the same
		{
			LastBallState = CurrentBallState;
			FirstTime = CurrentTime;
		}
		if (CurrentBallState == 0) DebouncedState = 0; //don't debounce removal of ball

	}
	return DebouncedState;
}

/****************************************************************************
 Function
 GetTapeState

 Parameters
 unsigned char SensorNumber, the index of the sensor to check

 Returns
 1 if the tape sensor sees black tape, 0 otherwise

 Description
 Checks tape sensor for black tape
 ****************************************************************************/
static unsigned char GetTapeState(unsigned char SensorNumber)
{
	unsigned int TapeVal;
	TapeVal = ADS12_ReadADPin(TAPE_MAP[SensorNumber]);
	return (TapeVal >= TAPE_MIN[SensorNumber]);
}

/****************************************************************************
 Function
 CheckTape

 Parameters
 unsigned char SensorNumber, the index of the sensor to check

 Returns
 none

 Description
 checks tape sensor and updates cache
 ****************************************************************************/
static void CheckTape(unsigned char SensorNumber)
{
	unsigned int CurrentTime = TMRS12_GetTime();
	if (CurrentTime - LastTapeTime[SensorNumber] > TAPE_DEBOUNCE) //each sensor individually debounced
	{
		unsigned char CurrentTapeState = GetTapeState(SensorNumber);
		if (CurrentTapeState != LastTapeState[SensorNumber])
			TapeCache[SensorNumber] |= GetTapeState(SensorNumber);
		LastTapeState[SensorNumber] = CurrentTapeState;
		LastTapeTime[SensorNumber] = CurrentTime;
	}
}

/****************************************************************************
 Function
 ClearFrontTapeCache / ClearBackTapeCache

 Parameters
 none

 Returns
 none

 Description
 Clears tape cache so we can check for a new tape event.  This is a public function
 so that the cache can be cleared at any desired time.
 ****************************************************************************/
void ClearFrontTapeCache(void)
{
	TapeCache[TAPE_LEFT] = 0;
	TapeCache[TAPE_CENTER] = 0;
	TapeCache[TAPE_RIGHT] = 0;
	CacheCheckedFlag[TAPE_FRONT] = 0; //to note that we haven't checked a full cache yet
}

void ClearBackTapeCache(void)
{
	TapeCache[TAPE_BACK_LEFT] = 0;
	TapeCache[TAPE_BACK_RIGHT] = 0;
	CacheCheckedFlag[TAPE_BACK] = 0;
}

/****************************************************************************
 Function
 CheckFrontTapeCache / CheckBackTapeCache

 Parameters
 none

 Returns
 1 if the cache is full, and has not been checked before.  0 otherwise

 Description
 Checks whether all front or all back tape sensors have been triggered since
 last check.
 ****************************************************************************/
unsigned char CheckFrontTapeCache(void)
{
	unsigned char ReturnVal = 0;
	if (CacheCheckedFlag[TAPE_FRONT] == 0) //only checks tape, returns 1 if it hasnt since last clear
	{
		CheckTape(TAPE_LEFT);
		CheckTape(TAPE_CENTER);
		CheckTape(TAPE_RIGHT);
		ReturnVal = (TapeCache[TAPE_LEFT] && TapeCache[TAPE_CENTER] && TapeCache[TAPE_RIGHT]);
		CacheCheckedFlag[TAPE_FRONT] = ReturnVal; //If 1 then set cache checked flag
	}
	return (ReturnVal);
}

unsigned char CheckBackTapeCache(void)
{
	unsigned char ReturnVal = 0;
	if (CacheCheckedFlag[TAPE_BACK] == 0) //only checks tape, returns 1 if it hasnt since last clear
	{
		CheckTape(TAPE_BACK_LEFT);
		CheckTape(TAPE_BACK_RIGHT);
		ReturnVal = (TapeCache[TAPE_BACK_LEFT] && TapeCache[TAPE_BACK_RIGHT]);
		CacheCheckedFlag[TAPE_BACK] = ReturnVal; //If 1 then set cache checked flag
	}
	return (ReturnVal);
}