#include <avr/io.h>
#include <avr/interrupt.h>
#include "adc.h"
#include "led.h"
#include "button.h"
#include "main.h"
#include "comm.h"

/* Communication driver */
#define SLAVE_ENABLE_DDR DDRA
#define SLAVE_ENABLE_REG PINA
#define SLAVE_ENABLE_PIN 1

#include <interface.h>

// the public instance of the app buffer
struct app app = {};

uint8_t channel_map[ADC_BUFFER_SIZE] = {
	1,
	0,
	2,
	3,
	7
};

// initalizes the application modules
void init() 
{
	// init button module (making the button light up on press)
	// module also updates app.buffer.button for us
	button_init();

	// init adc module
	// will sample 0x8F (CHANNEL 8 and 0-3) 
	// this module also updates app.buffer.poti and slider for us
	adc_init(channel_map);

	// init communication module
	comm_init();
	
	// start all modules
	adc_start();

	// activate interrupts (effectivly starting the application)
	sei();
}


// All states of the transaction state machine
volatile enum {
	COMM_WAIT       = 0,
	COMM_SEND_START ,
	COMM_SEND_WAIT  ,
	COMM_SEND_END   ,
} comm_state;

static inline void comm_service()
{
	static uint8_t count = 0;
	static uint16_t temp = 0xFFFF;

	if (USISR & (1<<USIOIF))
	{
		cli();

		switch(comm_state)
		{
		case COMM_WAIT:
			// only trigger on start of transmission
			if (IS_EOM(USIDR)) {
				USIDR &= ~0x80;
				comm_state = COMM_SEND_START;

				// sample first channel
				adc_start();
			}
			break;
		case COMM_SEND_START:
			// transmit LSByte of value
			temp = adc_value();

			// sample next channel
			adc_set_channel(channel_map[count+1]);
			adc_start();

			USIDR = temp & 0x7F;
			comm_state = COMM_SEND_WAIT;
			break;
		case COMM_SEND_WAIT:
			// transmit MSByte
			USIDR = (temp>>7)&0x7F;

			if (++count == ADC_BUFFER_SIZE) {
				comm_state = COMM_SEND_END;
			} else {
				comm_state = COMM_SEND_START;
			}
			break;
		case COMM_SEND_END:
			// cleanup the transaction
			count = 0;
			// transmit the button state and end of transaction
			USIDR = app.button | 0x80;
		default:
			comm_state = COMM_WAIT;
			// reset channel of adc back to 0
			adc_set_channel(channel_map[0]);
			break;
		}

		USISR |= (1<<USIOIF);

		sei();
	}
}

#include <util/delay.h>

// little debug helper function
// FIXME: remove when not needed
void delay(uint16_t d) 
{
	while(d--) 
	{
		_delay_ms(1);
	}
}

int main() 
{
	init();

	while(1) {
		// service serial interface
		// slightly more stable then interrupt
		comm_service();
	}
}
