#ifndef _usartcom_
#define _usartcom_    1

#include <stdlib.h>
#include "definice.h"
#include "_eeprom_wear.h"

#define C16MHzBaud9600 207
#define C16MHzBaud115200 16
#define C1MHzBaud9600 12


#define txbuf_size 32	//musi byt mocniny 2.
#define rxbuf_size 128	//musi byt mocniny 2.

#define USART_TX_INT_Enable UCSR0B|=(1<<TXCIE0)
#define USART_TX_INT_Disable UCSR0B&=~(1<<TXCIE0)
#define USART_RX_INT_Enable UCSR0B|=(1<<RXCIE0)
#define USART_RX_INT_Disable UCSR0B&=~(1<<RXCIE0)

#define timeout_0 210  // 1.5 ms - 20 bytu na 400000 baud
#define timeout_0a 1100  // 8 ms - 100 bytu na 400000 baud
#define timeout_0b 11000  // 80 ms - 1000 bytu na 400000 baud


volatile static uint8_t txbuf[txbuf_size];		//vysilaci buffer
volatile static uint8_t txbuf_in, txbuf_out;	//ukazatele
volatile static uint8_t transmit_flag=0x01; 	//priznak vysilani
volatile static uint8_t rxbuf[rxbuf_size];		//primaci buffer
volatile static uint8_t rxbuf_in, rxbuf_out;	//rxbuff ukazatele


void USdelay_50us(void)
{
	volatile uint16_t wcntr1a=0;
	volatile uint16_t wcntr2a=0;
	for (wcntr1a=0;wcntr1a<29;wcntr1a++)
	{
		wcntr2a=wcntr1a;
	}	
	wcntr1a=wcntr2a;
}


ISR(USART0_TX_vect)
// obsluha preruseni od uart tx
{
	if ((txbuf_in - txbuf_out) == 0) 
	{
		transmit_flag=1; // v pripade, ze neni co vysilat nastavi flag 1
	}
	else
		UDR0 = (txbuf[(txbuf_out++) & (txbuf_size-1)]);
}


void USART_Transmit_char(uint8_t tdata)
//vraci 0 v pripade ok, jinak cokoli v pripade plneho bufferu
{
	uint8_t navrat_hod=0xff;
	while (navrat_hod)
	{
		cli();
		if ((txbuf_in - txbuf_out) < txbuf_size ) {
				navrat_hod=0;
				txbuf[(txbuf_in & (txbuf_size-1))] = tdata;
				txbuf_in++;
			}
		else
			navrat_hod=0xff;			//plny buffer -neodeslano
		if (transmit_flag==0x01)		//v pripade ze se nevysila zahaji vysilani
			{
//				UCSR0B |= (1<<TXEN0);	//povoli vysilac
				UDR0 = (txbuf[(txbuf_out++) & (txbuf_size-1)]);
				transmit_flag=0;		// v pripade, ze je co vysilat nastavi flag na 0
			}
		sei();
	}	
}

// poslani 16bitove hodnoty
void USART_Transmit_16bit(uint16_t t16bit)
{
	USART_Transmit_char(t16bit>>8);
	USART_Transmit_char(t16bit);
}


ISR(USART0_RX_vect)
// obsluha preruseni od uart rx
{
	if ((rxbuf_in - rxbuf_out) < rxbuf_size) 
	{
		rxbuf [(rxbuf_in & (rxbuf_size-1))] = UDR0;
		rxbuf_in++;
	}
}

// ziskani znaku z usartu
static uint16_t USART_get_char( void )
{
	uint16_t tempdatar=0xff00;
	cli();

	if ((rxbuf_in - rxbuf_out) == 0x00)
		tempdatar=0xffff; 
	else{
			tempdatar = (uint16_t)(rxbuf[(rxbuf_out & (rxbuf_size-1))]);
			rxbuf_out++;
		}

	sei();
	return(tempdatar);
}


void clear_buffer(void)
{
	while(USART_get_char()<0x0100);
}


void USART_Init( uint16_t baud )
{
	cli();
	// Nastaveni baud rate 
	UBRR0H = (uint8_t)(baud>>8);
	UBRR0L = (uint8_t)baud;
	// dvojnasobna rychlost, snizeni chyby
	UCSR0A |= (1<<U2X0);
	// povoleni vysilace a prijmace
	UCSR0B |= (1<<TXEN0)|(1<<RXEN0);
	// nastaveni formatu: 8dat, 1stop bit 
	UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01);

	txbuf_in=0;
	txbuf_out=0; 
	rxbuf_in=0;
	rxbuf_out=0; 

	USART_TX_INT_Enable;	// povolení USART TX přerušení
	USART_RX_INT_Enable;	// povolení USART RX přerušení
	
	sei();
}


//------------------------------------------------------------------
//komunikacni funkce
//------------------------------------------------------------------
void delay_1ms(void)
{
	volatile uint16_t wcntr1=0;
	volatile uint16_t wcntr2=0;
	for (wcntr1=0;wcntr1<610;wcntr1++)
	{
		wcntr2=wcntr1;
	}
	wcntr1=wcntr2;	
}

void delay_250us(void)
{
	volatile uint16_t wcntr1=0;
	volatile uint16_t wcntr2=0;
	for (wcntr1=0;wcntr1<151;wcntr1++)
	{
		wcntr2=wcntr1;
	}
	wcntr1=wcntr2;
}

void delay_ms(uint16_t kolik)
{
	volatile uint16_t wcntr3=0;
	for (wcntr3=0;wcntr3<kolik;wcntr3++)
	{
		delay_1ms();
	}
	
}

void send_OK(void)
{
	USART_Transmit_char('O');
	USART_Transmit_char('K');
	USART_Transmit_char(0x0d);
	USART_Transmit_char(0x0a);
}

void send_ERROR(void)
{
	USART_Transmit_char('E');
	USART_Transmit_char('r');
	USART_Transmit_char('r');
	USART_Transmit_char(0x0d);
	USART_Transmit_char(0x0a);
}

void send_test(void)
{
	char radek1[]="OK";

	for(uint8_t a=0; a<strlen(&radek1[0]); a++)
		USART_Transmit_char(radek1[a]);
	USART_Transmit_char(0x0d);
	USART_Transmit_char(0x0a);
}



//----------------------------------------------------
//----------------------------------------------------

#define delka_znakovy_buffer 25
volatile uint8_t trigger_cmd_notdone=0xff;		// pro ucely kdy neni dekodovana zprava
volatile uint16_t znak16=0;						// promenna znaku z uartu
char znakovy_buffer[delka_znakovy_buffer];		// buffer prichozich znaku - plneno odpredu

volatile uint16_t ADC_val = 0;
volatile uint8_t voltage=0;		// hodnota napeti pro vyslani v desetinach

#define index_cmd_error 255
#define index_cmd_test 0
char cmd_test[] = "tset";		// kontrola komunikace

#define index_cmd_gsm_on 1
char cmd_gsm_on[] = "no msg";	// zapnuti gsm

#define index_cmd_gsm_off 2
char cmd_gsm_off[] = "ffo msg";	// vypnuti gsm

#define index_cmd_napeti 3
char cmd_napeti[] = "itepan";	// mereni napeti

#define index_cmd_vypnout 4
char cmd_vypnout[] = "tuonpyv";	// vypnuti mereni

#define index_cmd_dc_set 5
char cmd_dc_set[] = "tes_cd";	// nastaveni hodnoty dc pro dobu spanku

uint32_t command_decode_numb(void);


void send_uart_u16(uint16_t u16cis)
{
	char vstr[13];
	
	ltoa(u16cis,vstr,10);
	for(uint8_t a=0; a<strlen(&vstr[0]); a++)
		USART_Transmit_char(vstr[a]);
	USART_Transmit_char(0x0d);
	USART_Transmit_char(0x0a);
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
void send_double(double dcis)
{
	char kamsnim[30];
	
	dtostrf(dcis,1,0,&kamsnim[0]);
	for(uint8_t a=0; a<strlen(&kamsnim[0]); a++)
		USART_Transmit_char(kamsnim[a]);
	USART_Transmit_char(0x0d);
	USART_Transmit_char(0x0a);
}


// funkce ze znakoveho bufferu urci double cislo - bere v uvahu delku temdel
double get_double_number(uint8_t tempdel)
{
	volatile char znakbufnum[10];	// inverze cisla znakoveho bufferu

	//	tempdel=length_number();
	
	if ((tempdel>0)&&(tempdel<8))
	{
		for (uint8_t ajh=0; ajh<tempdel; ajh++)
		{
			znakbufnum[ajh]=znakovy_buffer[2+tempdel-ajh-1];
			if (znakbufnum[ajh]==',')
			znakbufnum[ajh]='.';
			znakbufnum[ajh+1]=0;
		}
		return(atof(znakbufnum));
	}
	else
		return(0);
}

// funkce urci delku cisla ve znakovem bufferu, akceptuje take jedno minus na zacatku cisla - max delka cisla s minusem je 8 znaku
// vraci delku, 0-error
uint8_t length_number(void)
{
	volatile uint8_t indexstr=0;
	
	for (uint8_t pcd=2; pcd<20; pcd++)
	{
		if ((znakovy_buffer[pcd]>0x2f)&&(znakovy_buffer[pcd]<0x3a))
		{
			indexstr++;
		}
		else
		{
			if (znakovy_buffer[pcd]=='-')
				indexstr++;
			return(indexstr); // nepovoleny znak
		}
	}
	return(indexstr);
}


//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// funkce pro provedeni prislusneho prikazu
void do_command(uint8_t cmd)
{
	
	switch(cmd)
	{
		case index_cmd_test:
			send_test();
			trigger_cmd_notdone=0x00;
		break;
		case index_cmd_gsm_on:
			SW_GSM_on();
			send_OK();
			trigger_cmd_notdone=0x00;
		break;
		case index_cmd_gsm_off:
			SW_GSM_off();
			send_OK();
			trigger_cmd_notdone=0x00;
		break;
		case index_cmd_napeti:
			SW_VOLT_on();
			_delay_us(1000)
			;
			ADMUX&=~((1<<MUX0)|(1<<MUX1)|(1<<MUX2)|(1<<MUX3)|(1<<MUX4));	// ADC0
			ADMUX |= ((1<<REFS0)|(1<<REFS1));								// 2.56 V refefence
			ADCSRA|=(1<<ADEN)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2);				// clk/128, povoleni ADC
			ADCSRA|=(1<<ADSC);												// zacatek prevodu
			while ((ADCSRA&(1<<ADIF))==0);
			ADCSRA|=(1<<ADIF);												// clear flag
			voltage = ADC/6.58;
			
			SW_VOLT_off();
			send_uart_u16(voltage);
			trigger_cmd_notdone=0x00;
		break;
		case index_cmd_vypnout:
			systime8 = 0x00;
			while(((uint8_t)(get_systime()-off_last_service))>=off_service_period)
			SW_GSM_off();
			SW_VOLT_off();
			SW_5V_off();
			sleep = 1;
			trigger_cmd_notdone=0x00;
		break;
		case index_cmd_dc_set:
			for(uint8_t popoc=0; popoc<delka_znakovy_buffer; popoc++)		// aby nedochazelo k opakovanemu zpracovani
				znakovy_buffer[popoc]=0xff;
			while(1)
			{
				if (((uint8_t)(get_systime()-usart_last_service))>=usart_service_period)	// pravidelna kontrola usart
				{
					usart_last_service=get_systime();

					znak16=USART_get_char();
					for(uint8_t popoc=delka_znakovy_buffer-1; popoc>0; popoc--)
						znakovy_buffer[popoc]=znakovy_buffer[popoc-1];
					znakovy_buffer[0]=znak16;
					if ((znakovy_buffer[0]==0x0a)&&(znakovy_buffer[1]==0x0d))
					{
						dc_service_period = command_decode_numb();
						for(uint8_t popoc=0; popoc<delka_znakovy_buffer; popoc++)		// smazeme znakovy buffer
							znakovy_buffer[popoc]=0xff;									// aby nedochazelo k opakovanemu zpracovani
						break;
					}

				}
			}	
			eeprom_safe_write_32bit(eeprom_dc_adress, dc_service_period);				// zapis hodnoty do eeprom
			trigger_cmd_notdone=0x00;
		break;

		default:
			send_ERROR();
		break;
	}
}




//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// funkce dekoduje prichozi prikaz

uint32_t command_decode_numb(void)
{
	char znakove_cislo[delka_znakovy_buffer];
	volatile uint8_t znpoc = 0;
	volatile uint32_t dc_cislo = 300;
	trigger_cmd_notdone=0xff;	// trigger zda byl dekodovan prikaz	
	
	for(uint8_t popoc=2; popoc<delka_znakovy_buffer; popoc++)			// pocet platnych cislic
	{
		if ((znakovy_buffer[popoc]>0x2f)&&(znakovy_buffer[popoc]<0x3a))
			znpoc++;
		else if(znpoc>0)
			break;
		
	}
	for(uint8_t popoc=2; popoc<delka_znakovy_buffer; popoc++)		// otoceni cisla zpet
	{
		if ((znakovy_buffer[popoc]>0x2f)&&(znakovy_buffer[popoc]<0x3a))
		{
			znakove_cislo[znpoc-1] = znakovy_buffer[popoc];
			znpoc--;
		}
	}
						
	dc_cislo = atol(znakove_cislo);		// prevedeni char na cislo
	
	return(dc_cislo);
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// funkce dekoduje prichozi prikaz
void command_decode(void)
{	
	char *pointer_str;
	trigger_cmd_notdone=0xff;	// trigger zda byl dekodovan prikaz	

	pointer_str=strstr(&znakovy_buffer[2],&cmd_test[0]);	//hledani prikazu ve znakovem bufferu
	if (pointer_str==&znakovy_buffer[2])					//urceni, jestli byl prikaz nalezen
		do_command(index_cmd_test);
	pointer_str=strstr(&znakovy_buffer[2],&cmd_gsm_off[0]);
	if (pointer_str==&znakovy_buffer[2])
		do_command(index_cmd_gsm_off);
	pointer_str=strstr(&znakovy_buffer[2],&cmd_gsm_on[0]);
	if (pointer_str==&znakovy_buffer[2])
		do_command(index_cmd_gsm_on);
	pointer_str=strstr(&znakovy_buffer[2],&cmd_napeti[0]);
	if (pointer_str==&znakovy_buffer[2])
		do_command(index_cmd_napeti);
	if (trigger_cmd_notdone)
		do_command(index_cmd_error);						// nebyl rozpoznan prikaz
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// funkce zpracuje znak
void zpracuj_znak(uint8_t znak8)
{
	for(uint8_t popoc=delka_znakovy_buffer-1; popoc>0; popoc--)	// zapis prichozi zpravy obracene
		znakovy_buffer[popoc]=znakovy_buffer[popoc-1];
	znakovy_buffer[0]=znak8;
	//				posledni			// predposledni
	if ((znakovy_buffer[0]==0x0a)&&(znakovy_buffer[1]==0x0d))
	{
		command_decode();
		for(uint8_t popoc=0; popoc<delka_znakovy_buffer; popoc++)	// smazeme znakovy buffer
			znakovy_buffer[popoc]=0xff;								// aby nedochazelo k opakovanemu zpracovani
	}
}

#endif /* _usarcom_ */
