#include <stdint.h>

#include "bd3491.h" // to manipulate the chip further than the normal system ui does
#include "i2c.h"  // for the i2c handle

#include "main.h" // to enable control of the system

struct {
	char buffer[1000];
	volatile uint16_t begin, end;
	volatile uint16_t len;
} serial_buffer = {};

void serial_buffer_push(char data) {
	if (serial_buffer.len && serial_buffer.end == serial_buffer.begin)
		return; // OVF

	serial_buffer.buffer[serial_buffer.end] = data;
	serial_buffer.end = (serial_buffer.end + 1)%(sizeof(serial_buffer.buffer));
	serial_buffer.len++;
}
char serial_buffer_peek() {
	if (serial_buffer.len)
		return serial_buffer.buffer[serial_buffer.begin];

	return 0;
}
char serial_buffer_pop() {
	if (!serial_buffer.len)
		return 0; // UF

	char result = serial_buffer_peek();
	serial_buffer.begin = (serial_buffer.begin + 1)%(sizeof(serial_buffer.buffer));
	serial_buffer.len--;
	return result;
}

int serial_buffer_len() {
	return serial_buffer.len;
}

void CDC_Receive_callback(uint8_t * data, uint32_t len) {
	while(len--) {
		serial_buffer_push(*(data++));
	}
}

int skip_white() {
	while(serial_buffer_peek() == ' ' || serial_buffer_peek() == '\t')
		serial_buffer_pop();

	return !serial_buffer_len();
}

int iswhite(char in) {
	return in == ' ' || in == '\t' || in == '\n';
}

int skip_non_white() {
	while(serial_buffer_len() && !iswhite(serial_buffer_peek()))
		serial_buffer_pop();

	return !serial_buffer_len();
}
int skip_line() {
	while(serial_buffer_len() && serial_buffer_peek() != '\n') serial_buffer_pop();

	if (serial_buffer_peek() == '\n') serial_buffer_pop();

	return !serial_buffer_len();
}

int next_arg_signed(int16_t * arg) {
	*arg = 0;

	if (skip_white())
		return 1;

	int neg = 0;
	while(serial_buffer_len() && (serial_buffer_peek() == '-' || serial_buffer_peek() == '+')) {
		if(serial_buffer_pop() == '-')
			neg = !neg;
	}

	int count = 0;
	while(serial_buffer_len() && serial_buffer_peek() >= '0' && serial_buffer_peek() <= '9') {
		*arg *= 10;
		*arg += serial_buffer_pop() - '0';
		++count;
	}

	if (neg) {
		*arg *= -1;
	}

	return !count;
}
int next_arg_unsigned(uint16_t * arg) {
	*arg = 0;

	if (skip_white())
		return 1;

	int count = 0;
	while(serial_buffer_len() && serial_buffer_peek() >= '0' && serial_buffer_peek() <= '9') {
		*arg *= 10;
		*arg += serial_buffer_pop() - '0';
		++count;
	}

	return !count;
}

#include <ctype.h>

int parse_buffer() {
	if (skip_white()) {
		return 1;
	}

	char temp;
	switch((temp = toupper(serial_buffer_pop()))) {
	case 'C':
		// change channel
		{
			uint16_t channel;

			if (next_arg_unsigned(&channel))
				break;

			set_input(channel);
		}

		break;
	case 'G':
		// set gain
		{
			uint16_t gain;
			if (next_arg_unsigned(&gain)) break;

			printf("G%d\n", bd_set_gain(&hi2c1, gain));
		}
		break;
	case 'L':
	case 'R':
	{
		uint16_t att;
		if (next_arg_unsigned(&att)) break;

		printf("%c%d\n", temp, bd_set_attenuation(&hi2c1, temp == 'R', att&0xFF));
	}
		break;
	case 'B':
	case 'T':

	default:
		printf("E\n");
	}

	return skip_line();
}
