#include "digirule2.h"

#define	DEBOUNCE_TIME	33		// in milliseconds
#define HOLD_TIME		1000	// in milliseconds

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

static union
{
	uint16 word;
	struct
	{
		union
		{
			uint8 data;
			struct
			{
				uint8 data0	: 1;
				uint8 data1	: 1;
				uint8 data2	: 1;
				uint8 data3	: 1;
				uint8 data4	: 1;
				uint8 data5	: 1;
				uint8 data6	: 1;
				uint8 data7	: 1;
			};
		};
		uint8 goto_			: 1;
		uint8 deposit		: 1;
		uint8 prev			: 1;
		uint8 next			: 1;
		uint8 run_stop		: 1;
		uint8 load			: 1;
		uint8 save_			: 1;
	};
} previous, current, button;

static enum 
{ 
	StateStop,
	StateData7,
	StateData0,
	StateLoad,
	StateSave,
	StateRun,
	StateRun2,
	StateRun3,
	StateWaitRelease,
	StateWaitRelease2,
} state = StateWaitRelease;

static uint8 debounce_timer, scan_timer, scan_index;
static uint16 hold_timer;

static void scan( void );

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

void ui_process( void )
{
	if( t1ms )		// if time to sample buttons
	{
		// sample buttons, inverting polarity
		current.data     = ~DATA_BUTTONS;
		current.goto_    = ~GOTO_BUTTON;
		current.deposit  = ~DEPOSIT_BUTTON;
		current.prev     = ~PREV_BUTTON;
		current.next     = ~NEXT_BUTTON;
		current.run_stop = ~RUN_STOP_BUTTON;
		current.load     = ~LOAD_BUTTON;
		current.save_    = ~SAVE_BUTTON;
		
		// if this sample is different than previous, start over
		if( previous.word != current.word )
		{
			previous = current;
			debounce_timer = 0;
		}
		// if successive samples are identical, update button state variable
		else if( debounce_timer != DEBOUNCE_TIME && ++debounce_timer == DEBOUNCE_TIME )
		{
			button = current;
			ram.button_reg = button.data;
		}
	
		// process events
		switch( state )
		{
			case StateStop:
				if( button.data7 )
				{
					DATA7_LED ^= 1;
					hold_timer = 0;
					state = StateData7;
				}
				else if( button.data6 )
				{
					DATA6_LED ^= 1;
					state = StateWaitRelease;
				}
				else if( button.data5 )
				{
					DATA5_LED ^= 1;
					state = StateWaitRelease;
				}
				else if( button.data4 )
				{
					DATA4_LED ^= 1;
					state = StateWaitRelease;
				}
				else if( button.data3 )
				{
					DATA3_LED ^= 1;
					state = StateWaitRelease;
				}
				else if( button.data2 )
				{
					DATA2_LED ^= 1;
					state = StateWaitRelease;
				}
				else if( button.data1 )
				{
					DATA1_LED ^= 1;
					state = StateWaitRelease;
				}
				else if( button.data0 )
				{
					DATA0_LED ^= 1;
					hold_timer = 0;
					state = StateData0;
				}
				else if( button.goto_ )
				{
					ADDRESS_LEDS = pc = DATA_LEDS;
					DATA_LEDS = RAM[pc];
					state = StateWaitRelease;
				}
				else if( button.deposit )
				{
					RAM[pc++] = DATA_LEDS;
					DATA_LEDS = RAM[pc];
					ADDRESS_LEDS = pc;
					state = StateWaitRelease;
				}
				else if( button.prev )
				{
					DATA_LEDS = RAM[--pc];
					ADDRESS_LEDS = pc;
					state = StateWaitRelease;
				}
				else if( button.next )
				{
					DATA_LEDS = RAM[++pc];
					ADDRESS_LEDS = pc;
					state = StateWaitRelease;
				}
				else if( button.load )
				{
					ADDRESS_LEDS = 0b00000000;
					scan_timer = scan_index = 0;
					state = StateLoad;
				}				
				else if( button.save_ )
				{
					ADDRESS_LEDS = 0b00000000;
					scan_timer = scan_index = 0;
					state = StateSave;
				}
				else if( button.run_stop )
				{
					RUN_LED = 1;
					state = StateRun;
				}

				break;
				
			case StateData7:
				if( !button.data7 )
					state = StateStop;
				
				else if( ++hold_timer == HOLD_TIME )
				{
					DATA_LEDS = 0b11111111;
					state = StateWaitRelease;
				}
				
				break;
				
			case StateData0:
				if( !button.data0 )
					state = StateStop;
				
				else if( ++hold_timer == HOLD_TIME )
				{
					DATA_LEDS = 0b00000000;
					state = StateWaitRelease;
				}
				
				break;

			///////////////////////////////////////////////////

			case StateLoad:
				if( !button.load )
				{
					ADDRESS_LEDS = pc;
					DATA_LEDS = RAM[pc];
					state = StateStop;
				}
				else if( button.run_stop )
				{
					DATA_LEDS = 0b00000000;
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.prev )
				{
					DATA_LEDS = 0b00000000;

					for( uint16 addr = 0; addr < 256; addr++ )
					{
						ADDRESS_LEDS = 0xFF80 >> (addr >> 5);	// show progress bar
						sleep_ms( 1 );			// for visual effect only
						RAM[addr] = 0;
					}
					
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data7 )
				{
					flash_program_load( 7 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data6 )
				{
					flash_program_load( 6 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data5 )
				{
					flash_program_load( 5 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data4 )
				{
					flash_program_load( 4 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data3 )
				{
					flash_program_load( 3 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data2 )
				{
					flash_program_load( 2 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data1 )
				{
					flash_program_load( 1 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else if( button.data0 )
				{
					flash_program_load( 0 );
					cpu_reset();
					state = StateWaitRelease2;
				}
				else
					scan();

				break;

			case StateSave:
				if( !button.save_ )
				{
					ADDRESS_LEDS = pc;
					DATA_LEDS = RAM[pc];
					state = StateStop;
				}
				else if( button.data7 )
				{
					flash_program_save( 7 );
					state = StateWaitRelease2;
				}
				else if( button.data6 )
				{
					flash_program_save( 6 );
					state = StateWaitRelease2;
				}
				else if( button.data5 )
				{
					flash_program_save( 5 );
					state = StateWaitRelease2;
				}
				else if( button.data4 )
				{
					flash_program_save( 4 );
					state = StateWaitRelease2;
				}
				else if( button.data3 )
				{
					flash_program_save( 3 );
					state = StateWaitRelease2;
				}
				else if( button.data2 )
				{
					flash_program_save( 2 );
					state = StateWaitRelease2;
				}
				else if( button.data1 )
				{
					flash_program_save( 1 );
					state = StateWaitRelease2;
				}
				else if( button.data0 )
				{
					flash_program_save( 0 );
					state = StateWaitRelease2;
				}
				else
					scan();

				break;

			///////////////////////////////////////////////////

			case StateRun:
				if( !button.run_stop )
					state = StateRun2;

				else if( !RUN_LED )				// if CPU halted
				{
					ADDRESS_LEDS = pc;
					DATA_LEDS = RAM[pc];
					state = StateWaitRelease;
				}

				break;

			case StateRun2:
				if( button.run_stop )
				{
					RUN_LED = 0;
					ADDRESS_LEDS = pc;
					DATA_LEDS = RAM[pc];
					state = StateWaitRelease;
				}
				else if( button.goto_ )
				{
					address_blanking ^= 1;
					state = StateRun3;
				}
				else if( !RUN_LED )				// if CPU halted
				{
					ADDRESS_LEDS = pc;
					DATA_LEDS = RAM[pc];
					state = StateStop;
				}			

				break;

			case StateRun3:
				if( !button.goto_ )
					state = StateRun2;

				else if( !RUN_LED )				// if CPU halted
				{
					ADDRESS_LEDS = pc;
					DATA_LEDS = RAM[pc];
					state = StateWaitRelease;
				}

				break;

			///////////////////////////////////////////////////

			case StateWaitRelease:
				if( button.word == 0 )			// if all buttons released
					state = StateStop;

				break;
				
			case StateWaitRelease2:
				if( button.word == 0 )			// if all buttons released
				{
					ADDRESS_LEDS = pc;
					DATA_LEDS = RAM[pc];
					state = StateStop;
				}

				break;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// private functions

static void scan( void )
{
	static const uint8 lut[] =
	{
		0b10000000,
		0b01000000,
		0b00100000,
		0b00010000,
		0b00001000,
		0b00000100,
		0b00000010,
		0b00000001,
		0b00000010,
		0b00000100,
		0b00001000,
		0b00010000,
		0b00100000,
		0b01000000
	};
	
	if( ++scan_timer == 50 )
	{
		DATA_LEDS = lut[scan_index];

		if( ++scan_index == ELEMENTSOF( lut ) )
			scan_index = 0;
		
		scan_timer = 0;
	}
}
