#include <arduino.h>
#include <sr_74hc165.h>
#include <stdio.h>

#define BAUD 38400
#include <util/setbaud.h>

#define TICK_US 1000
#define BUT_CHECK_TICK 3000UL / TICK_US
#define BUT_CHECKS 3
#define DIG_CNT 15


struct but_t but1;

struct sr_74hc165 shreg1;
uint8_t beep_pin, led_ok_pin;
/* 0xed is A, 0x80 is - */
uint8_t dig2sym[10] = {0x6f, 0x0c, 0xab, 0xae, 0xcc, 0xe6, 0xe7, 0x2c, 0xef, 0xee};
uint16_t dig_snd[10] = {784, 880, 988, 1047, 1175, 1319, 1397, 1568, 1720, 1976};
//uint16_t dig_snd[10] = {392, 440, 494, 523, 587, 659, 698, 784, 880, 988};
//uint16_t dig_snd[10] = {196, 220, 247, 261, 293, 329, 349, 392, 440, 494};
uint8_t code[DIG_CNT], code_need[DIG_CNT] = {0x6f, 0x2c, 0xae, 0xcc, 0xe6, 0x0c, 0x2c, 0xab, 0xab, 0xae, 0xcc, 0xef, 0xcc, 0xee, 0x0c};


/*
static int
usart_getchar(void)
{
	if (c == '\n')
		usart_putchar('\r', stream);
	loop_until_bit_is_set(UCSR0A, UDRE0);
	UDR0 = c;
	return 0;
}
*/

static int
usart_putchar(char c, FILE *stream)
{
	if (c == '\n')
		usart_putchar('\r', stream);
	loop_until_bit_is_set(UCSR0A, UDRE0);
	UDR0 = c;
	return 0;
}
static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar, NULL, _FDEV_SETUP_WRITE);

/*
 * @param baud_rate 38400
 */
void
usart_init(unsigned int baud_rate)
{
	UBRR0H = 0;
	UBRR0L = 25;

	/* enable tx and rx */
	UCSR0B = (1 << RXEN0) | (1 << TXEN0);
	/* data size 8 bit, 1 stop bit */
	UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

/*
 * (us * F_CPU) / 1000000us / 4ticks,
 * Where:
 *  4ticks - duration of _delay_loop_2()
 */
uint32_t
delay_us2calls(uint16_t us)
{
	uint32_t calls = F_CPU / 1000;

	if (calls > 65535)
		calls = calls / 1000 * us / 4;
	else
		calls = calls * us / 1000 / 4;
//	printf("calls=%lu\n", calls);
	return calls;
}

void
delay_calls(uint32_t calls)
{
	uint16_t cnt;

	if (calls > 65535) {
		for(cnt = (calls & 0xffff0000) >> 16; cnt; cnt--)
			_delay_loop_2(65535);
	}
	_delay_loop_2(calls & 0xffff);
}

#define delay_us(US) delay_calls(delay_us2calls(US))

void
beep_init(uint8_t pin)
{
	pin_mode(pin, OUTPUT);
}

void
beep_const(uint8_t pin, uint16_t freq, uint16_t ms)
{
	uint32_t delay, cnt = ms;

	delay = 1000000 / freq;
	cnt = cnt*1000 / delay;
	delay = delay >> 1;
	printf("BEEP: delay=%lu, cnt=%lu\n", delay, cnt);
	while (cnt) {
		pin_write(pin, 1);
		_delay_us(125);
		pin_write(pin, 0);
		_delay_us(125);
		cnt--;
	}
}

void
beep(uint8_t pin, uint16_t freq, uint16_t ms)
{
	uint32_t delay, cnt = ms;

	delay = 1000000 / freq;
	cnt = cnt*1000 / delay;
	delay = delay >> 1;
	delay = delay_us2calls(delay);
//	printf("BEEP: delay=%lu, cnt=%lu\n", delay, cnt);
	while (cnt) {
		pin_write(pin, 1);
		delay_calls(delay);
		pin_write(pin, 0);
		delay_calls(delay);
		cnt--;
	}
}

int8_t
get_dig_by_code(uint8_t code)
{
	uint8_t i;

	for(i = 0; i < 10; i++)
		if (dig2sym[i] == code)
			return i;
	return -1;
}

void
read_code(void)
{
	uint8_t i;
	int8_t d;

	sr_74hc165_load(&shreg1);
	for(i = 0; i < DIG_CNT; i++) {
		code[i] = sr_74hc165_read_nextbyte(&shreg1);
		printf("%d=%x, ", i, code[i]);
		d = get_dig_by_code(code[i]);
		if (d >= 0)
			beep(beep_pin, dig_snd[d], 200);
		else {
			beep(beep_pin, 131, 150);
			_delay_ms(10);
			beep(beep_pin, 131, 150);
		}
		_delay_ms(50);
	}
	printf("\n");
}

uint8_t
is_code_right(void)
{
	uint8_t i;

	/* The cheat code. */
	if (code[0] == 0x80)
		return 1;

	for(i = 0; i < DIG_CNT; i++) {
		if (code[i] != code_need[i])
			return 0;
	}

	return 1;
}

void
play_wait(void)
{
	uint8_t i;

	_delay_ms(800);
	for(i = 0; i < 7; i++) {
		beep(beep_pin, 494, 200);
		_delay_ms(800);
	}
	beep(beep_pin, 494, 1000);
	_delay_ms(400);
}

void
play_ok(void)
{
	uint8_t i;

	for(i = 0; i < 10; i++) {
		_delay_ms(1);
		beep(beep_pin, 262, 326);
		_delay_ms(1);
		beep(beep_pin, 262, 326);
		_delay_ms(1);
		beep(beep_pin, 392, 326);
		_delay_ms(1);
		beep(beep_pin, 262, 326);
		_delay_ms(1);
		beep(beep_pin, 415, 326);
		_delay_ms(1);
		beep(beep_pin, 262, 326);
		_delay_ms(1);
		beep(beep_pin, 392, 326);
		_delay_ms(1);
		beep(beep_pin, 262, 326);

		_delay_ms(1);
		beep(beep_pin, 207, 326);
		_delay_ms(1);
		beep(beep_pin, 207, 326);
		_delay_ms(1);
		beep(beep_pin, 311, 326);
		_delay_ms(1);
		beep(beep_pin, 349, 326);

		_delay_ms(1);
		beep(beep_pin, 196, 326);
		_delay_ms(1);
		beep(beep_pin, 196, 326);
		_delay_ms(1);
		beep(beep_pin, 294, 326);
		_delay_ms(1);
		beep(beep_pin, 311, 326);
	}

	/* Chorus */
	for(i = 0; i < 13; i++) {
		_delay_ms(1);
		beep(beep_pin, 207, 326);
	}
	_delay_ms(1);
	beep(beep_pin, 196, 326);
	_delay_ms(1);
	beep(beep_pin, 196, 326*2);
	for(i = 0; i < 8; i++) {
		_delay_ms(1);
		beep(beep_pin, 262, 326);
	}
	for(i = 0; i < 7; i++) {
		_delay_ms(1);
		beep(beep_pin, 207, 326);
	}
	_delay_ms(1);
	beep(beep_pin, 196, 326);
	for(i = 0; i < 8; i++) {
		_delay_ms(1);
		beep(beep_pin, 207, 326);
	}
	for(i = 0; i < 8; i++) {
		_delay_ms(1);
		beep(beep_pin, 196, 326);
	}
}

void
play_fail(void)
{
	beep(beep_pin, 392, 200);
	beep(beep_pin, 330, 200);
	beep(beep_pin, 262, 200);
	return;
	beep(beep_pin, 196, 200);
	beep(beep_pin, 165, 200);
	beep(beep_pin, 131, 200);
	return;
	beep(beep_pin, 98, 200);
	beep(beep_pin, 82, 200);
	beep(beep_pin, 65, 200);

}

void
check_code(void)
{
	uint8_t d = 0, i;

	printf("But is pressed\n");
	pin_write(led_ok_pin, 0);
	read_code();
	play_wait();
	if (is_code_right()) {
		pin_write(led_ok_pin, 1);
		play_ok();
	} else {
		play_fail();
	}
//	beep_const(beep_pin, 4000, 1000);
//	_delay_ms(1000);
/*
	beep(beep_pin, 523, 500);
	beep(beep_pin, 587, 500);
	beep(beep_pin, 659, 500);
	beep(beep_pin, 698, 500);
	beep(beep_pin, 784, 500);
	beep(beep_pin, 880, 500);
	beep(beep_pin, 988, 500);

	beep(beep_pin, 261, 1000);
	beep(beep_pin, 293, 1000);
	beep(beep_pin, 329, 1000);
	beep(beep_pin, 349, 1000);
	beep(beep_pin, 392, 1000);
	beep(beep_pin, 440, 1000);
	beep(beep_pin, 494, 1000);
*/
/*
	beep(beep_pin, 7, 1000);
	_delay_ms(500);
	beep(beep_pin, 85, 82);
	_delay_ms(500);
	beep(beep_pin, 182, 1000);
*/
	return;
}

void
main(void)
{
	int run = 1;
	uint8_t c, but_check_tick_timer;

	usart_init(3);
	stdout = &mystdout;

	sr_74hc165_init(&shreg1, PIN_D3, PIN_D4, PIN_D5);

	beep_pin = PIN_D6;
	beep_init(beep_pin);

	but1.pin = PIN_D2;
	but_init(&but1, BUT_CHECKS, 1);

	but_check_tick_timer = 0;

	led_ok_pin = PIN_D8;
	pin_mode(led_ok_pin, OUTPUT);

	printf("Started\n");
	while (1) {
		if (but_check_tick_timer == 0) {
			but_check(&but1);
			if (but_is_pressed(&but1))
				check_code();
			but_check_tick_timer = BUT_CHECK_TICK;
		}
        _delay_us(TICK_US);
		but_check_tick_timer--;
  	}
}
