#include "digirule1.h"

#define	DEBOUNCE_TIME	33		// in milliseconds
#define HOLD1_TIME		1000	// in milliseconds
#define HOLD2_TIME		3000	// in milliseconds

///////////////////////////////////////////////////////////////////////////////
// public data

button_t button;

///////////////////////////////////////////////////////////////////////////////
// private data

static struct
{
	struct	// for debounce state machine
	{
		enum
		{
			DebounceStateReleased,
			DebounceStatePressing,
			DebounceStatePressed,
			DebounceStateReleasing,
		} state;
		uint8 timer;
	} debounce;
	
	struct	// for hold state machine
	{
		enum
		{
			HoldStateReleased,
			HoldStatePressed,
			HoldStateHold1,
			HoldStateHold2,
		} state;
		uint16 timer;
	} hold;
} priv[9];	// one for each button

///////////////////////////////////////////////////////////////////////////////
// public functions

void buttons_process( void )
{
	uint8 bi, input;
	
	// for each button, this code samples the raw input state, debounces it, and manages
	// state and transition flags that the rest of the user interface code can sample
	for( bi = 0; bi < 9; bi++ )
	{
		// reset any transition flags that may have been set on the previous tick
		button.array[bi].pressed  = 0;
		button.array[bi].held1    = 0;
		button.array[bi].held2    = 0;
		button.array[bi].released = 0;
		
		// get raw button input
		switch( bi )
		{
			case 0: input = A_SW;			break;
			case 1: input = B_SW;			break;
			case 2: input = GATE_SEL_SW;	break;
			case 3: input = SJTD_SW;		break;
			case 4: input = CLK_SW;			break;
			case 5: input = RK_SW;			break;
			case 6: input = FF_SEL_SW;		break;
			case 7: input = DOWN_SW;		break;
			case 8: input = UP_SW;			break;
		}

		// this state machine debounces the raw button input
		// the button.<name>.state bit is updated with the debounced logical state of the button
		// the button.<name>.pressed bit is set when a released-to-pressed transition is detected
		// the button.<name>.released bit is set when a pressed-to-released transition is detected
		switch( priv[bi].debounce.state )
		{
			case DebounceStateReleased:
				if( input )
				{
					priv[bi].debounce.timer = 0;
					priv[bi].debounce.state = DebounceStatePressing;
				}

				break;

			case DebounceStatePressing:
				if( !input )
					priv[bi].debounce.state = DebounceStateReleased;

				else if( ++priv[bi].debounce.timer == DEBOUNCE_TIME )
				{
					button.array[bi].state = 1;
					button.array[bi].pressed = 1;
					priv[bi].debounce.state = DebounceStatePressed;
				}

				break;

			case DebounceStatePressed:
				if( !input )
				{
					priv[bi].debounce.timer = 0;
					priv[bi].debounce.state = DebounceStateReleasing;
				}

				break;

			case DebounceStateReleasing:
				if( input )
					priv[bi].debounce.state = DebounceStatePressed;

				else if( ++priv[bi].debounce.timer == DEBOUNCE_TIME )
				{
					button.array[bi].state = 0;
					button.array[bi].released = 1;
					priv[bi].debounce.state = DebounceStateReleased;
				}

				break;
		}

		// this state machine measures how long the button.<name>.state bit has been asserted
		// the button.<name>.held1 bit is set when the button has been held for HOLD1_TIME
		// the button.<name>.held2 bit is set when the button has been held for HOLD2_TIME
		switch( priv[bi].hold.state )
		{
			case HoldStateReleased:
				if( button.array[bi].state )
				{
					priv[bi].hold.timer = 0;
					priv[bi].hold.state = HoldStatePressed;
				}

				break;

			case HoldStatePressed:
				if( !button.array[bi].state )
					priv[bi].hold.state = HoldStateReleased;

				else if( ++priv[bi].hold.timer == HOLD1_TIME )
				{
					button.array[bi].held1 = 1;
					priv[bi].hold.state = HoldStateHold1;
				}

				break;

			case HoldStateHold1:
				if( !button.array[bi].state )
					priv[bi].hold.state = HoldStateReleased;

				else if( ++priv[bi].hold.timer == HOLD2_TIME )
				{
					button.array[bi].held2 = 1;
					priv[bi].hold.state = HoldStateHold2;
				}

				break;

			case HoldStateHold2:
				if( !button.array[bi].state )
					priv[bi].hold.state = HoldStateReleased;

				break;
		}
	}
}
