#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "usart.h"
#include "rfchannel.h"
#include "klavesnice.h"
#include "crc7.h"

/*
 * JiriMachatyPrijimac.c
 *
 * Created: 25.3.2012 17:32:26
 *  Author: Kain
 *
 *  Vsechny prijate data se zpracovavaji v ramci obsluhy preruseni USARTu - USART_RXC_vect.
 *  Pokud neprichazi data, procesor je v nekonecne smycce a jen ceka az se z obsluhy preruseni rekne, ze je prijaty ramec pripraven ke zpracovani.
 */

uint8_t oldData = 0;

#define DISP_DDR    DDRC
#define DISP_PORT   PORTC
#define DISP_MASK   0x3f            // Bity ktere jsou pouzity v portu pro 7segment
#define DISP_SPOLECNA_ANODA 1       // =1 pokud je 7segment typ se SA, =0 pokud SK
#define DISP_DP_PORT    PORTD
#define DISP_DP_DDR     DDRD
#define DISP_DP         PD3
#define REPRO_DDR   DDRD
#define REPRO_PORT  PORTD
#define REPRO       PD4
#define REPRO_PULPERIODA   500            // 500us = 1/(2*500)  = 1kHz
const uint8_t DISP_cislo[10] PROGMEM  = {
	// nastaveni odpovida 7segmentovce, jsou to cisla 0-9
	// Cele pole je ulozeno v PROGRAMOVE pameti (PROGMEM)
	// Musi odpovidat zapojeni
	// PD2 PC5 PC4 PC3 PC2 PC1 PC0
	// g   f   e   d   c   b   a
	0b01000000, //0
	0b01111001, //1
	0b00100100, //2....
	0b00110000,
	0b00011001,
	0b00010010,
	0b00000010,
	0b01111000,
	0b00000000,
	0b00011000
};

// Nastavi 7segment na pozadovanou hodnotu (num)
void printVal7Seg(uint8_t num) {
	if(num > 9) return;

	uint8_t val = (DISP_MASK & pgm_read_byte(&DISP_cislo[num]));
	DISP_PORT &= ~(DISP_MASK);  // Vynulovat bity v portu pro 7segment
#if DISP_SPOLECNA_ANODA
	DISP_PORT |= val;
	// 6.bit je mimo port 7segmentovky DISP_PORT (PORTC)
	if(val & (1<<6)) {
		PORTD &= ~(1<<PD2);
	} else {
		PORTD |= (1<<PD2);
	}
#else
	DISP_PORT |= ~val;
	if((~val) & (1<<6)) {
		PORTD &= ~(1<<PD2);
	} else {
		PORTD |= (1<<PD2);
	}
#endif
}

// Pipne po cca 4999*(1/(1/(REPRO_PULPERIODA/500000))) sekundy   ~5sec
void beep() {
    for(uint16_t i = 0; i < 4999; i ++) {
		REPRO_PORT |= (1<<REPRO);
		_delay_us(REPRO_PULPERIODA);
		REPRO_PORT &= ~(1<<REPRO);
		_delay_us(REPRO_PULPERIODA);
	}
}

// 10x zablika 2Hz rychlosti
void blinkDp() {
	for(uint8_t i = 0; i < 10; i ++) {
	    DISP_DP_PORT &= ~(1<<DISP_DP);
		_delay_ms(250);
		DISP_DP_PORT |= (1<<DISP_DP);
		_delay_ms(250);
	}
}

/* Vola funkce na zaklade nejakeho kodu v "data" - ty kody jsou vetsinou kody stisknutych klaves 0-9 ve vysilaci */
void doTheJob(const uint8_t data) {
	switch(data) {
		case KB_0: {
			beep();
			break;
		}
		case KB_1: {
			blinkDp();
			break;
		}
		case KB_2: {
			break;
		}
		case KB_3: {
			break;
		}
		case KB_4: {
			break;
		}
		case KB_5: {
			break;
		}
		case KB_6: {
			break;
		}
		case KB_7: {
			break;
		}
		case KB_8: {
			break;
		}
		case KB_9: {
			break;
		}
	}
}

/* Vsechen prijem znaku zajistuje obsluha preruseni, v hlavni smycce se pouze
 * kontroluji priznaky - zda jiz jsou k dispozici data
 */
int main(void)
{
	// USART
	USART_init();
	// Init 7seg
	DISP_DDR |= DISP_MASK;      // PORT jako vystupni
	DISP_PORT |= DISP_MASK;     // Spolecna anoda, nastavime bity portu na 1 = led zhasnou
	DDRD |= (1<<PD2);           // 6.bit z 7segmentovky
	PORTD |= (1<<PD2);
	DISP_DP_DDR |= (1<<DISP_DP);    // decimal point
	DISP_DP_PORT |= (1<<DISP_DP);
	//

	// Povolit interrupty
	sei();

    while(1)
    {
		if(USART_novaDataVBufferu) {
			// Mame nove prijata data v bufferu
            if(USART_rxbuffer[0] == RF_ADDRESS) {
				// Adresa souhlasi s lokalni, overime CRC
				uint8_t crc = CRC7_get((const uint8_t * )(&USART_rxbuffer[0]), 2);
				if(crc == USART_rxbuffer[2]) {
					uint8_t data = USART_rxbuffer[1];
					if(oldData != data) {
						// Spocitane CRC souhlasi s prijatym, vykoname prijatou instrukci					
						printVal7Seg(data);
						doTheJob(data);     // Na zaklade prijatych dat vykoname urcitou instrukci
						oldData = data;
					}					
				}
			}
			USART_novaDataVBufferu = 0;
		}
    }
}