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

#include <vcl.h>
#include <winsock.h>
#include <vector>
#pragma hdrstop

#include "recvapp.h"
#include "Unit1.h"
#include "Static.h"
#pragma package(smart_init)

using namespace std;

extern SOCKET socket_udp;
extern sockaddr_in sock_udp_name;

__fastcall recvapp::recvapp(bool CreateSuspended)
	: TThread(CreateSuspended)
{
	FreeOnTerminate = true;                     // Uvolni paměť po ukončení
   	//Priority = tpHighest;
}

// Vypis do historie
void __fastcall recvapp::UpdateCaption() {

	Form1->Memo1->Lines->Add(GetTime().TimeString()+"> "+Message);
	if (SAVE_TRANSFER) {
		if (CONNECTED) {
			Form1->ToolButton14->Enabled = false;
			Form1->ToolButton15->Enabled = true;
		} else {
			Form1->ToolButton14->Enabled = true;
			Form1->ToolButton15->Enabled = false;
		}
	} else {
		Form1->ToolButton14->Enabled = false;
		Form1->ToolButton15->Enabled = false;
    }
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Funce pro konstrukci a odesilani paketu vlastniho protokolu.
// |operation (8bit)|   flags (8bit)   |    ID (16 bit)        |
// ID je poradove cislo paketu
void send_data(unsigned char operation, char flags, unsigned short int ID ,char data[], int len)
{
send_packet p;
myUDP *myhdr = new (myUDP);
unsigned char *hdr = (char *)myhdr;

	// Vytvoreni nadstavby UDP paketu
	myhdr->operation = operation;
	myhdr->flags = flags;
	myhdr->id = ID;
	// prepis do bajtoveho pole
	p.id = myhdr->id;
	p.operation = myhdr->operation;
	p.flags =  myhdr->flags;
	p.len = len + 4;
	p.data[0] = hdr[0];
	p.data[1] = hdr[1];
	p.data[2] = hdr[3];
	p.data[3] = hdr[2];
	// Doplneni o data k odeslani
	int i;
	for (i = 0; i < len; i++) {
		p.data[i+4] = data[i];
	}
	sendto(socket_udp, p.data, p.len, 0, (sockaddr *)&sock_udp_name, sizeof(sock_udp_name));
	if (operation == 0xCC) {
	// zapis datoveho paketu do odeslane fronty
		out_queue.push_front(p);
	}
	delete myhdr;
	delete hdr;
};

// Algoritmus vlakna
void __fastcall recvapp::Execute()
{
char received[1500];                            // maximalni velikost prichoziho paketu 1500B
sockaddr_in ClientInfo;                         // Informace o klientovi
int addrlen = sizeof(ClientInfo);               // Info o klientovi
paket info;                                     //Struktura paket obsahuje cas prijeti, zdrojovou IP a data
myUDP *recv_hdr = new (myUDP);                  // struktura hlavicky
int errcnt, size, len;    						// errcnt pocitadlo pro odesilani nepotvryeneho okna
bool full = false;                              // info o plne fronte

	CONNECTED = false;
	NameThreadForDebugging("recvapp");

	// Hlavni smycka
	while (!Terminated) {
		// Čtení přijatých dat
		if ( (len = recvfrom(socket_udp, received, 1499, 0, (sockaddr *)&ClientInfo, &addrlen)) > 0)
		{
			if (SAVE_TRANSFER) {
				// -------------------------------------------------------------
				// --------------- Část pro příjem APP dat ---------------------
				// -------------------------------------------------------------
				if (len >= 4) {   		// Overena minimalni velikost

					if (!CONNECTED) {  	// Sekce pro navazani spojeni

						recv_hdr = (myUDP *) received;
						AnsiString ClientAdr = inet_ntoa((in_addr)ClientInfo.sin_addr);
						switch (recv_hdr->operation) {
							case 0xAA: 	// SYNC paket (vytvoří spojení)
								locker->Acquire();
								CONNECTED = true;
								GLOBAL_ID_TX = 0;
								GLOBAL_ID_RX = 0;
								// Pri zadosti o pripojeni nastavy cilovou IP adresu na adresu odkud prisla data

								if (!try_connect) {
									send_data(0xAA, 2, 0, "", 0);
									Message = "Spojení navázáno ze strany FPGA.";
								} else {
									Message = "Spojení navázáno ze strany PC.";
								}
								try_connect = false;
								locker->Release();
								Synchronize(&UpdateCaption);
								break;
							default:;
						}
					} else {
						// Sekce pro stadium, kdyz je spojeni navazano (prijem vlastnich dat)
						recv_hdr = (myUDP *) received;
						recv_hdr->id = (recv_hdr->id >> 8) | (recv_hdr->id << 8);
						bool QEND = false;

						locker->Acquire();
						switch (recv_hdr->operation) {

							case 0xAA: 	// SYNC paket = refresh spojeni, nulovani ID
								GLOBAL_ID_TX = 0;
								GLOBAL_ID_RX = 0;
								break;

							case 0xBB: 	// ACK paket, potvrzeni prijeti dat FPGA modulem  - ID udava do ktereho cisla jsou data potvrzena
								while (!out_queue.empty() && !QEND)
								{
									if (recv_hdr->id != out_queue.back().id) {
										QEND = false;
									} else {
										QEND = true;
									}
									out_queue.pop_back();
								}
								break;

							case 0xCC: 	// Datovy paket
								if ((recv_hdr->id) == GLOBAL_ID_RX) {
								  	for (int i = 0; i < (len - 5); i++) {
										info.data[i] = received[i+4];
									}
                                    info.len = len - 4;
									// ulozit do prijmaci fronty
									if (fronta.size() < unsigned(QUEUE_SIZE)) {
										fronta.push_back(info);
									}
								   //	info.len = len - 4;
									if (recv_hdr->flags == 1) {
										// odeslat potvrzeni o prijeti
										Sleep(50);  // rizeni toku
										send_data(0xBB, 0, recv_hdr->id, "", 0);
									}
									GLOBAL_ID_RX++;
								// spatne poradi
								} else {
									if (recv_hdr->id > GLOBAL_ID_RX) {
										send_data(0xEE, 0, GLOBAL_ID_RX, "", 0);
									  	Sleep(50);
									}
							  }
								break;

							case 0xDD:		// ESYNC paket
								if (!try_close) {
								   send_data(0xDD, 2, 0, "", 0);
									Message = "Spojení ukončeno ze strany FPGA.";
								} else {
									Message = "Spojení ukončeno ze strany PC.";
								}
								CONNECTED = false;
								try_close = false;
								Synchronize(&UpdateCaption);
								break;

							case 0xEE:		// ERR paket - nesouhlasi poradi ID (data se predbihaji = odeslat znovu cele okno)
								// smazani uspesne prenesenych datagramu
								while (!out_queue.empty() && !QEND)
								{
									if (recv_hdr->id != out_queue.back().id) {
										out_queue.pop_back();
										QEND = false;
									} else {
										QEND = true;
									}
								}
								// odeslani zbytku bufferu
								size = out_queue.size();
								errcnt = 0;
								while ((!out_queue.empty()) && (size!=errcnt)) {
									sendto(socket_udp, out_queue.back().data, out_queue.back().len, 0, (sockaddr *)&sock_udp_name, sizeof(sock_udp_name));
									out_queue.push_front(out_queue.back());
									out_queue.pop_back();
									errcnt ++;
								}
								Message = "Error" + IntToStr(recv_hdr->id);
								Synchronize(&UpdateCaption);
								break;

							default: ;
						}
						locker->Release();
					}
				}
			} else {
				// -------------------------------------------------------------
				// --------------- Část pro příjem UDP dat ---------------------
				// -------------------------------------------------------------
				info.len = len;
				// ulozime strukturu do paměti
				if (fronta.size() < unsigned(QUEUE_SIZE)) {
					locker->Acquire();
					fronta.push_back(info);
					locker->Release();
					full = false;
				} else {
					// Ohlášení plné fronty
					if (!full) {
						Message = "Chyba příjmu! Fronta je plná. Data budou zahazována.";
						Synchronize(&UpdateCaption);
						full = true;
					}
				}
			}
		}
	}
}


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