#include "mybool.h"
#include "packet.h"

#include <stdio.h>				// sprintf
#include <string.h>				// praca so stringom

/* FreeRTOS includes */
#include "FreeRTOS.h"
#include "timers.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "priorities.h"

/* Standard Stellaris includes */
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/rom.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "driverlib/timer.h"

// vsetko pre pracu s displejom
#include "grlib/grlib.h"
#include "grlib/widget.h"

#include "MainTask.h"
#include "com_RS485.h"
#include "display_task.h"

#include "displayScreens/MenuScreen.h"		// conzola

// definicia hlaviciek 
	xTaskHandle xHComTask;					// handle tasku
	xQueueHandle	xQueue_in_com_RS485;	// handle fronty

// globalne premenne pre RX/TX
	volatile	bool 			RS485_mode_TX;			// oznacuje ci je v rezime primania alebo vysielania
	volatile	bool			RS485_transmit_en;
	volatile	unsigned char	TX_buff_out[MAX_PACKET_LENGHT];	// bufer pre ulozenie vysielaneho packetu
	volatile	unsigned char	TX_len_send_packet;				// dlzka odosielaneho packetu
	volatile	unsigned char	TX_pointer_send_bite; 			// pozicia odosielaneho bite

// vytvorim TX RQS buffer	
	volatile	t_TX_buff		TX_buff[TX_BUFF_SIZE];
	
// slucka tasku
void	ComTask( void *pvParameters )
{
	t_com_queue_struct	s_mesage_in;
	t_packet	*s_packet;	
	s_packet= (t_packet *)s_mesage_in.Data;

	for(;;)
	{
		if( xQueueReceive( xQueue_in_com_RS485, &s_mesage_in, portMAX_DELAY ) )	// bol bola priata niaka sprava/packet treba ho spracovat
		{
			switch( s_mesage_in.Mesage_ID )
			{
				case C_QUEUE_RX_MES:				// fronta s datami od RX
					// kontrola crc MIN a MAX dlzka sa kontroluje uz v preruseni
					if(CalcCrc( s_mesage_in.Data, (s_packet->lenght + 1) ) ) break;	// xorujem vypocitane crc s crc packetu vyslesok by mal bit nula
					
					// kontrola ci je premna 
					if ( (s_packet->dest_ID == MY_ID) || (s_packet->dest_ID == BROADCAST_ID) )		// packet je premna spracujem ho
					{
						// kontrola ci je odpoved 
						unsigned char	i;			
						for( i=0 ; i < TX_BUFF_SIZE ; i++ )
						{
							if( (TX_buff[i].is_use == true) && (s_packet->packet_number == TX_buff[i].packet_number) ) 	// je to odpoved na rqs 
							{
								xTimerStop( TX_buff[i].xTimer, 100 );			// vypinam casovac	
								TX_buff[i].is_use= false;						// uvolnujem bunku v TX rqs buffery
								s_mesage_in.Mesage_ID= C_QUEUE_ANSWER_PACKET;	// odosle packet dalej 
								sendToMainQueue( (t_MainQueueStruct*)&s_mesage_in, 0 );
								break;
							}
						}
						if( i == TX_BUFF_SIZE )		// nebola to odpoved
						{
							s_mesage_in.Mesage_ID= C_QUEUE_NEW_PACKET;
							sendToMainQueue( (t_MainQueueStruct*)&s_mesage_in, 0 );
						}
					}
					// else packet sa zahadzuje 
					break;
				case C_QUEUE_TX_SEND:				// foronta s datamy na odoslatie
					s_mesage_in.Data[(s_packet->lenght)-1]= CalcCrc( s_mesage_in.Data, s_packet->lenght);
					if( SendPacket( s_mesage_in.Data, s_packet->lenght) )	// nepodarilo sa odoslat skus znovu
					{								
						xQueueSendToBack( xQueue_in_com_RS485, &s_mesage_in, 0 );		// odosielam packet znova ak je RS485 obsadena
					}
					// else	// odoslalo sa vsetko ok
					break;
				case C_QUEUE_TX_SEND_RQS:			// sprava pre odoslanie request packetu
					s_packet->packet_number= GenPacketNumber();
					if( SendRqsPacket(&s_mesage_in) )						// uklada spravu do REQ fronty 
					{
						// nepodarilo sa pridat spravu do req fronty 
					}
					break;
			}
		}		
	}	
}

// Timer SW pre TX request 
void vTimerRQSrepeat( xTimerHandle pxTimer )
{
	long	timerID;
	timerID= ( long ) pvTimerGetTimerID( pxTimer );
	
	if( TX_buff[timerID].send_count < TX_MAX_NUM_RQS_REPEAT )	// ak nebol prekroceny maximalny pocet pokusov oo odslatie skus znova 
	{
		if( xTimerStart( TX_buff[timerID].xTimer, 100) == pdPASS )		// iba ak sa spusti casovac tak ma zmisle cosi tobit 
		{
		sendToComRS485Queue( ( void* )&TX_buff[timerID].mesage, 10 );
		TX_buff[timerID].send_count++;
		}
		else TX_buff[timerID].is_use= false;		// ak sa nespusti je cosi zele 
	}
	else		// bol prekroceny max pocet pokusov uvolnujem TXREQbuff bunku a odosielam ze sa nepodarilo odoslat
	{
		TX_buff[timerID].is_use= false;
		
		// odpoved nadriadenemu tasku ze sa nepodarilo zhnat odpoved
		t_com_queue_struct	PacketMesage;
		PacketMesage.Mesage_ID= C_QUEUE_ANSWER_PACKET;	// odosiela sa ako request
		t_packet	*s_Spacket;
		t_packet	*s_RQSpacket;
		s_RQSpacket= (t_packet *)TX_buff[timerID].mesage.Data;
		s_Spacket= (t_packet *)PacketMesage.Data;
		
		s_Spacket->start_of_packet= START_OF_PACKET;
		s_Spacket->dest_ID= MY_ID;
		s_Spacket->sorce_ID= SPINACI_MODUL_ID;
		s_Spacket->comand= C_ERR_PACKET;
		s_Spacket->sign= s_RQSpacket->comand;
		unsigned char temp;
		temp= snprintf((char *)s_Spacket->data, (PACKET_DATA_LENGHT - 1), "Modul not response" );
		s_Spacket->lenght= PACKET_LENGHT_NO_DATA + temp + 1;
		sendToMainQueue( (t_MainQueueStruct*)&PacketMesage, 0 );
	}
	
	
}

// inicializacia pre tuto kniznicu 
unsigned long	ComRS485Init( void )
{	
	// Enable the peripherals used by this lib
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

	// nastavenie riadenia RX/TX pre RS485 
	ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_2);    
    
    // Set GPIO A0 and A1 as UART pins.
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
    // Configure the UART for 115,200, 8-N-1 operation.
    ROM_UARTConfigSetExpClk(UART0_BASE, ROM_SysCtlClockGet(), 115200,
                            (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                             UART_CONFIG_PAR_NONE));
    // nastavenie poctu dat v fifo pri ktorom vznikne prerusenie na RX 7/8 z fifo co je 14 bite a na tx 2/8																	 
	ROM_UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX2_8, UART_FIFO_RX7_8); 
    
    // nastavenie TX int. ktore je generovane po odoslani vsetkych dat
	//UARTTxIntModeSet(UART0_BASE,UART_TXINT_MODE_EOT);
	
    // Enable the UART TX interrupt, RX a RX timeout.
    ROM_IntEnable(INT_UART0);
    ROM_UARTIntEnable(UART0_BASE, UART_INT_TX | UART_INT_RX | UART_INT_RT);		// TX int. pri odoslani vsetkych dat vo fifo
    																			// RX int. pri prekroceni fifo levelu 
    																			// RT int. pri timeoute ak je nieco vo fifo a nic nepride najbliksie mozne 4bite   																				
    // nastavenie HW timeru pre timeout po odosielani/primani

	// Enable the peripheral
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    
    // Configure the 32-bit one shot timer
    ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_32_BIT_OS);
    ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, ROM_SysCtlClockGet()/TRANSMIT_EN_TIMEOUT);

    // Setup the interrupts for the timer timeouts.
    ROM_IntEnable(INT_TIMER0A);   
	ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);  
	
    //Enable processor interrupts.
	ROM_IntMasterEnable();
	
// pociatocne nastavenia 

	// rezim primania
	RS485_RX;
	
	RS485_transmit_en= false;		// vysielanie bude povolene az po uplinuti TRANSMIT_EN_TIMEOUT
	
    // spustim casovac pre povolenie RS485_transmit_enable
	ROM_TimerEnable(TIMER0_BASE, TIMER_A);	
	
	// vytvorim frontu pre posielanie dat Do tohto tasku
	xQueue_in_com_RS485= xQueueCreate( lenght_Queue_in_com_RS485, sizeof( t_com_queue_struct ) );
	
// inicializacia TX buffera a spustenie jeho casovacou
	long i;
	for( i= 0 ; i < TX_BUFF_SIZE ; i++ )
	{
		TX_buff[i].is_use= false;
		TX_buff[i].xTimer= xTimerCreate( "TX_Timer",  ( TIME_RQS_REPEAT / portTICK_RATE_MS ),  pdFALSE, (void *) i, vTimerRQSrepeat );		
	}	
	
// vytvorenie tasku	
	if(xTaskCreate( ComTask, "ComTask", STACK_SIZE_COM_TASK, 0, PRIORITY_COM_TASK,  xHComTask ) != pdPASS)
	{
		return(1);
	}
	
	return (0);	// vsetko OK
}

// prerusenia od USATRU
void	UART0IntHandler( void )
{	
	// static premenne		
	static	t_com_queue_struct	s_RX_in_buffer;		// pravdepodobde to moze bit static
	static	unsigned long		RX_accepted_data_counter= 0;				// pravdepodobde to moze bit static
	
    unsigned long ulStatus;

    // Get the interrrupt status.
    ulStatus = ROM_UARTIntStatus(UART0_BASE, true); 
    // Clear the asserted interrupts.
    ROM_UARTIntClear(UART0_BASE, ulStatus);	
    
	ROM_TimerDisable(TIMER0_BASE, TIMER_A);		// vypnem casovac lebo je niaka aktivita na RS485
    RS485_transmit_en= false;					// namoze sa vysielat lebo je niaka aktivita na RS485
    
    if( RS485_mode_TX )		// je v mode vysielania
    {
    	if( UART_TXINT_MODE_FIFO == UARTTxIntModeGet(UART0_BASE) )		// usat int TX je v mode fifo
		{
			while( UARTCharPutNonBlocking( UART0_BASE, (unsigned char)TX_buff_out[TX_pointer_send_bite]) )
			{
				TX_pointer_send_bite++;					//uspesne odoslane mozem ist na dalsi
				if( TX_pointer_send_bite >= TX_len_send_packet )		// bol ulozeny cely buffer do fifa
				{
					UARTTxIntModeSet( UART0_BASE,UART_TXINT_MODE_EOT );		// prepnem TX int na prerusenie pri dokonceny vysielania
					break;
				}
			}
		}
		else				// bolo dokoncene vysielanie				// usat int TX je v mode UART_TXINT_MODE_EOT
		{
    		ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, ROM_SysCtlClockGet() / TRANSMIT_EN_TIMEOUT);
			ROM_TimerEnable(TIMER0_BASE, TIMER_A);									// spustim casovac po vyprsani sa povoli RS485_transmit_enable
			
    		UARTTxIntModeSet(UART0_BASE,UART_TXINT_MODE_FIFO);		// prerusenie TX je v mode s fifo
    		RS485_RX;		// prepnem sa spat do rezimu primania			
		}
    }
    else					// je v mode primania
    {
    	if(ulStatus & UART_INT_RX)		// bolo pritich minimalne 14 znakov do fifa
    	{								// vycitam iba 13 aby spolahlivo fungoval UART_INT_RT
    		unsigned long i;
    		for( i= 0 ; i < 13 ; i++ )
    		{
    			if( ROM_UARTCharsAvail(UART0_BASE) )	// su niake znaky vo fifo?
    			{
	     			s_RX_in_buffer.Data[RX_accepted_data_counter]= ROM_UARTCharGetNonBlocking(UART0_BASE);
	    			if( RX_accepted_data_counter <= MAX_PACKET_LENGHT ) RX_accepted_data_counter++;   		// jak pretiekol buffer tak zahadzujem dalsie bite
    			}
    			// else nieco je zle  
    		}
    	}
    	else if(ulStatus & UART_INT_RT)		// RX timeout prerusenie bolo dokoncene primanie
    	{
    		while(ROM_UARTCharsAvail(UART0_BASE))	// vycitam vsetky znaky 
    		{
     			s_RX_in_buffer.Data[RX_accepted_data_counter]= ROM_UARTCharGetNonBlocking(UART0_BASE);
    			if( RX_accepted_data_counter <= MAX_PACKET_LENGHT ) RX_accepted_data_counter++;   		// jak pretiekol buffer tak zahadzujem dalsie bite
    		}
    		
    		ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, ROM_SysCtlClockGet() / TRANSMIT_EN_TIMEOUT_NO_PT);
			ROM_TimerEnable(TIMER0_BASE, TIMER_A);									// spustim casovac po vyprsani sa povoli RS485_transmit_enable
			
    		s_RX_in_buffer.Mesage_ID= C_QUEUE_RX_MES;
    			// odoslem priaty packet na spracovanie ak nieje vedsi ako MAX_PACKET_LENGHT
    		if( (RX_accepted_data_counter <= MAX_PACKET_LENGHT) && (RX_accepted_data_counter >= PACKET_LENGHT_NO_DATA) )
    		{
    			xQueueSendToBackFromISR( xQueue_in_com_RS485, &s_RX_in_buffer, pdFALSE );
    		}
    		RX_accepted_data_counter= 0;									// nulujem pre primanie dalsieho packetu
    	}
    	else		// ak sa cosi dojebalo
    	{
    		ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, ROM_SysCtlClockGet() / TRANSMIT_EN_TIMEOUT_NO_PT);
			ROM_TimerEnable(TIMER0_BASE, TIMER_A);									// spustim casovac po vyprsani sa povoli RS485_transmit_enable
    	}
    }    
}

// prerusenie od timer0 nastavuje RS485_transmit_en
void	Timer0IntHandler( void )
{
	unsigned long ulStatus;
	ulStatus= ROM_TimerIntStatus(TIMER0_BASE, true);

    // Clear the timer interrupt.
    ROM_TimerIntClear(TIMER0_BASE, ulStatus);
	
	RS485_transmit_en= true;
}



// odosiela packet
unsigned long	SendPacket( const char * packet, unsigned char lenght )
{
// ****************** KONTROLA CI MOZEM VYSIELAT ***********************************
	if ( RS485_mode_TX == true )	// prave sa nieco odosiela
	{
		return 1;	// nemozem odosielat
	}
	if ( RS485_transmit_en == false )	// ak je zakazane vysielanie vrat Neodoslalo
	{
		return 1;	// nemozem odosielat
	}

	taskENTER_CRITICAL();				//kriticka cast
	if( UARTBusy(UART0_BASE) )			// ak sa nieco deje s usartom
	{
		taskEXIT_CRITICAL();			// konec kritickej casti
		return 1;
	}
	RS485_TX;						// nastavym RS485 na odosielanie
	RS485_transmit_en= false;		// dalsi transmit je povoleny az po odoslati 
	//vTaskPrioritySet( xHComTask, PRIORITY_COM_TASK_MAX);			// zvysim prioritu tasku aby neboli medzery medzy odosielanymy bajtmi
	taskEXIT_CRITICAL();			// konec kritickej sekcie
	
//******************** KOPIROVANIE DO TX VYSIELACIEHO BUFERA************************
	TX_len_send_packet= lenght;		// celkova dlzka packetu
	TX_pointer_send_bite= 0;		// odosielam prvy bite 
	memcpy( ( void* )TX_buff_out, packet, TX_len_send_packet );
	
// **************** PRVE PLNENIE FIFA***********************************************
	taskENTER_CRITICAL();			//kriticka cast by sa naraz naplnilo fifo
	
	ROM_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);			// vypnem TX preusenie

	while( UARTCharPutNonBlocking( UART0_BASE, (unsigned char)TX_buff_out[TX_pointer_send_bite]) )
	{
		TX_pointer_send_bite++;					//uspesne odoslane mozem ist na dalsi
		if( TX_pointer_send_bite >= TX_len_send_packet )		// bol ulozeny cely buffer do fifa
			{
				UARTTxIntModeSet( UART0_BASE,UART_TXINT_MODE_EOT );		// prepnem TX int na prerusenie pri dokonceny vysielania
				ROM_UARTIntEnable(UART0_BASE, UART_INT_TX | UART_INT_RX | UART_INT_RT);		// zapnem TX int
				taskEXIT_CRITICAL();			// konec kritickej sekcie
				return 0;
			}
	}

	UARTTxIntModeSet(UART0_BASE,UART_TXINT_MODE_FIFO);		// prerusenie TX je v mode s fifo
	ROM_UARTIntEnable(UART0_BASE, UART_INT_TX | UART_INT_RX | UART_INT_RT);		// zapnem TX int
	taskEXIT_CRITICAL();			// konec kritickej sekcie
	
	return 0;						// uspesne odoslany packet
//************************************************************************************
}

// ulozi spravu s packetom do REQ buffera a nuluje timer a odosiela ho do ffronty ako C_QUEUE_TX_SEND
unsigned long	SendRqsPacket( const t_com_queue_struct * mesage )
{
	unsigned char	i, temp;
	t_packet *p_packet;

	temp= TX_BUFF_SIZE + 1;							// ak nenajde volnu bunku bude v temp cislo vedsia ako TX_BUFF_SIZE
	p_packet= ( t_packet* )mesage->Data;
	for( i=0 ; i < TX_BUFF_SIZE ; i++ )
	{
		if( TX_buff[i].is_use == false)			// dana bunka je volna mozem tam ulozit packet
		{
			temp= i;
			break;
		}
	}
	if( temp > TX_BUFF_SIZE ) return 1;				// buffer je plny
	
	TX_buff[temp].is_use= true;
	TX_buff[temp].packet_number= p_packet->packet_number;
	TX_buff[temp].send_count= 1;

	memcpy( ( void* )&TX_buff[temp].mesage, mesage, MAX_PACKET_LENGHT + 1 );					// ulozim packet do tx bufera
	
	TX_buff[temp].mesage.Mesage_ID= C_QUEUE_TX_SEND;
	
	if( xTimerStart( TX_buff[temp].xTimer, 100) != pdPASS )	// spustim casovac pre dany packet maximalna doba cakania na frontu pre timere je 100 tickou 
	{														// ak sa ja tak nepodati odoslat do fronty tak je to chyba 
		TX_buff[temp].is_use= false;
		return 1;
	}
	
	sendToComRS485Queue( ( void* )&TX_buff[temp].mesage, 10 );		// dam packet do fronty na odoslanie mzx doba cakanie na frontu je 10 tikov
	return 0;
}

// vypocet CRC pre dlzku packetu to vrati ctc spocitane okrem bunky crc
unsigned char	CalcCrc( const char * packet, unsigned char lenght )
{
	unsigned char crc;
	unsigned char i;
	
	crc= 0;
	for( i=0 ; i < ( lenght - 1) ; i++ )
	{
		crc ^= packet[i];
	}	
	return crc;
}

// generuje packet number
unsigned char	GenPacketNumber(void)
{	
	static unsigned char my_packet_number= 10;
	my_packet_number++;
	if ( my_packet_number < 10 ) my_packet_number= 10;
	return  my_packet_number;
}

// odosle spravu do fronty pre ComRS485 task
unsigned long	sendToComRS485Queue( const t_com_queue_struct *mesage, unsigned long xTicksToWait )
{
	if( xQueueSendToBack( xQueue_in_com_RS485, mesage, xTicksToWait ) == pdPASS) return 0;		// odoslalo do fronty
	
	else return 1;		// neodoslalo
}
