//
#include <vcl.h>
#include <iostream>
#include <string>
#include <windows.h>
#include <winsock.h>
#include <time.h>

#pragma hdrstop

// Vlastni jednotky
#include "static.h"
#include "Unit1.h"
#include "Unit2.h"
#include "sendudp.h"
#include "recvapp.h"
#include "sendapp.h"
#include "saveLIST.h"
#include "testunit.h"

#pragma package(smart_init)
#pragma link "perfgrap"
#pragma link "Chart"
#pragma link "Series"
#pragma link "TeEngine"
#pragma link "TeeProcs"
#pragma resource "*.dfm"

using namespace std;

// ----------- VLAKNA ----------------
recvapp *rx_thread_app = NULL; // Vlakno pro recv.
sendudp *tx_thread_udp = NULL; // Vlakno pro odesilani UDP
sendapp *tx_thread_app = NULL; // Vlakno zabezpeceneho prenosu send.
savelist *save_thread = NULL; // Vlakno pro ukladani fronty do souboru
testunit *test = NULL; // Vlakno pro test pripojeni
TCriticalSection *locker; // Kritická sekce pro vlákna
// -----------------------------------
// ----------- SOCKETY ---------------
WSADATA data; // Struktura s info. o knihovně winsock
WORD wVersionRequested = MAKEWORD(1, 1); // Číslo verze
SOCKET socket_udp; // Globalni UDP socket
sockaddr_in sock_udp_name; // Jmeno socketu
// -----------------------------------
bool try_connect = false;
bool try_close = false;

// nastaveni cilove IP adresy
int __fastcall TForm1::SetIpAdress(void) {
	hostent *host;
	if ((host = gethostbyname(DEST_IP.t_str())) == NULL) {
	   //	ShowMessage("Neplatná IP adresa!");
		return -1;
	} else {
		memcpy(&(sock_udp_name.sin_addr), host->h_addr, host->h_length);
		return 0;
	}
}
// Inicializace socketu pro praci po siti - kompletni nastaveni
int __fastcall TForm1::InitSocket(void) {
	// ************** Inicializace socketu **************
	if (WSAStartup(wVersionRequested, &data) != 0) {
		// inicializace knihovny pro sockety
		ShowMessage("Nepodařilo se inicializovat sokety!");
		return -1;
	}
	if ((socket_udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
		ShowMessage("Nelze vytvořit socket!");
		return -1;
	}
	/* u_long arg = 1;
	 if (ioctlsocket(socket_udp, FIONBIO, &arg) == SOCKET_ERROR) {
	 ShowMessage("Chyba! Nelze zapnout neblokovacíi režim socketu.");
	 } */

	sock_udp_name.sin_family = AF_INET; // 1) Rodina protokolů
	sock_udp_name.sin_port = htons(UDP_PORT);
	// 2) Číslo portu, na kterém čekáme
	sock_udp_name.sin_addr.s_addr = INADDR_ANY;
	// Přiřadíme soketu jméno
	if (bind(socket_udp, (sockaddr*) & sock_udp_name, sizeof(sock_udp_name))
		== SOCKET_ERROR) {
		ShowMessage("Problém s pojmenováním soketu!");
		return -1;
	}
	// Nastaveni cilove IP adresy
	SetIpAdress();

	return 0;
}

// Funce pro konstrukci a odesilani paketu vlastniho protokolu.
// |operation (8bit)|   flags (8bit)   |    ID (16 bit)        |
// ID je poradove cislo paketu
int send_app(char operation, char flags, char data[], int len) {
	send_packet p;
	myUDP *myhdr = new(myUDP);
	unsigned char *hdr = (char*)myhdr;
	int r;
	// Vytvoreni nadstavby UDP paketu
	myhdr->operation = operation;
	myhdr->flags = flags;
	myhdr->id = GLOBAL_ID_TX;
	if (myhdr->operation == 0xCC) {
		GLOBAL_ID_TX++;
	}
	// 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];
	}
	r = sendto(socket_udp, p.data, p.len, 0, (sockaddr*) & sock_udp_name, sizeof
		(sock_udp_name));
	if (myhdr->operation == 0xCC) {
		// zapis datoveho paketu do odeslane fronty
		out_queue.push_front(p);
	}
	delete myhdr;
	delete hdr;
	return r;
};

// Funkce pro odeslani UDP paketu
int send_udp(char data[], int len) {
	return sendto(socket_udp, data, len, 0, (sockaddr*) & sock_udp_name, sizeof
		(sock_udp_name));
};

// Funkce pro ulozeni nastaveni programu do config.cfg
void SaveSettings(void) {
	FILE *conf;
	char *pom;

	conf = fopen("config.cfg", "w");
	// Pripojeni
	pom = IntToStr(int(SAVE_TRANSFER)).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);
	fwrite(DEST_IP.t_str(), 1, strlen(DEST_IP.t_str()), conf);
	fwrite("\n", 1, 1, conf);
	// UDP
	pom = IntToStr(UDP_PORT).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);
	pom = IntToStr(UDP_DLY).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);
	pom = IntToStr(UDP_SIZE).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);
	// APP
	pom = IntToStr(OUTBUFFER_SIZE).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);
	pom = IntToStr(OUTBLOCK_SIZE).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);
	// Fronta
	pom = IntToStr(QUEUE_SIZE).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);

	pom = IntToStr(int(TEXT_FLAG)).t_str();
	fwrite(pom, 1, strlen(pom), conf);
	fwrite("\n", 1, 1, conf);
	fclose(conf);
}

// Funkce pro nacteni nastaveni programu z config.cfg
void LoadSettings(void) {
	// nasteni nastaveni z config souboru
	FILE *conf;
	char pom[50];
	int a;
	if ((conf = fopen("config.cfg", "r")) != NULL) {
		fscanf(conf, "%d", &a);
		SAVE_TRANSFER = a;
		fscanf(conf, "%s", pom);
		DEST_IP = pom;

		fscanf(conf, "%i", &UDP_PORT);
		fscanf(conf, "%i", &UDP_DLY);
		fscanf(conf, "%i", &UDP_SIZE);

		fscanf(conf, "%i", &OUTBUFFER_SIZE);
		fscanf(conf, "%i", &OUTBLOCK_SIZE);

		fscanf(conf, "%i", &QUEUE_SIZE);

		fscanf(conf, "%d", &a);
		TEXT_FLAG = a;

		fclose(conf);
	}
	else
		defvalue();
}

// Formulář
TForm1 *Form1;

// ---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {

}

// ---------------------------------------------------------------------------
// Vytvoreni hlavniho formulare - nastaveni vsech pocatecnich parametru
void __fastcall TForm1::CloseAll(void) {
	closesocket(socket_udp);
	WSACleanup();
}

// ---------------------------------------------------------------------------
// Vytvoreni hlavniho formulare - nastaveni vsech pocatecnich parametru
void __fastcall TForm1::FormCreate(TObject *Sender) {
	//
	LoadSettings();
	// Inicializace socketu
	if (InitSocket() == 0) {
		Memo1->Lines->Add(GetTime().TimeString() +"> Inicializace programu proběhla úspěšně.");
	}
	else {
		Memo1->Lines->Add(GetTime().TimeString() +"> Chyba! Nepodařilo se inicializovat sockety.");
	};
	// Kriticka sekce pro vlakna
	locker = new TCriticalSection();
	// Vytvoreni prijmaciho vlakna
   	rx_thread_app = new recvapp(false);
}

// ---------------------------------------------------------------------------
// Ukonceni programu
void __fastcall TForm1::FormDestroy(TObject *Sender) {
	// Uzavreni spojeni
	if (CONNECTED) {
		try_close = true;
		send_app(0xDD, 2, "", 0);
	}
	// Ukonceni vlaken, socketu a uvolneni pameti
	CloseAll();
	// Ulozeni nastaveni do souboru
	SaveSettings();
	// vymazani fronty
	if (!fronta.empty())
		fronta.clear();
	// Kriticka sekce pro vlakna
	delete locker;
}

// ---------------------------------------------------------------------------
//
void __fastcall TForm1::FileListBox1Click(TObject *Sender) {
	FILE *soubor = NULL;
	int tx_file_size;
	if ((soubor = fopen(FileListBox1->FileName.t_str(), "r")) != NULL) {
		fseek(soubor, 0, SEEK_END);
		tx_file_size = ftell(soubor);
		fclose(soubor);
	}
	else
		ShowMessage("Nepodarilo se zjistit velikost souboru!");
	if (tx_file_size > 1048576) {
		Label10->Caption = IntToStr((int)tx_file_size / 1048576) + "MB";
	}
	else {
		if (tx_file_size > 1024) {
			Label10->Caption = IntToStr((int)tx_file_size / 1024) + "kB";
		}
		else {
			Label10->Caption = IntToStr(tx_file_size) + "B";
		}
	}
}

// ---------------------------------------------------------------------------
// Tlacitko na odeslani souboru
void __fastcall TForm1::SpeedButton1Click(TObject *Sender) {
	if (FileListBox1->FileName != "") {
		// vlakno pro odeslani souboru
		if (SAVE_TRANSFER) {
			tx_thread_app = new sendapp(false);
		}
		else {
			tx_thread_udp = new sendudp(false);
		}
		Label21->Caption = ExtractFileName(FileListBox1->FileName);
	}
	else
		ShowMessage("Neplatný název souboru!");
}
// ---------------------------------------------------------------------------

void __fastcall TForm1::SpeedButton2Click(TObject *Sender) {

	if ((tx_thread_app != NULL) && (!tx_thread_app->Finished)) {
		CONNECTED = false;
		tx_thread_app->Terminate();
	}
	if ((tx_thread_udp != NULL) && (!tx_thread_udp->Finished)) {
		tx_thread_udp->Terminate();
	}
}
// ---------------------------------------------------------------------------

void __fastcall TForm1::ToolButton3Click(TObject *Sender) {
	if (SaveDialog1->Execute()) {
		SAVE_PATH = SaveDialog1->FileName;
		save_thread = new savelist(false);
    }

}
// ---------------------------------------------------------------------------

//
void __fastcall TForm1::ToolButton5Click(TObject *Sender) {
	if (!fronta.empty()) fronta.clear();
	ProgressBar2->Position = 0;
}
// ---------------------------------------------------------------------------

//
void __fastcall TForm1::ToolButton7Click(TObject *Sender) {
	Form2->Visible = true;
}
// ---------------------------------------------------------------------------

void __fastcall TForm1::SpeedButton3Click(TObject *Sender) {
	if (SAVE_TRANSFER) {
		send_app(0xCC, 1, Memo2->Text.t_str(), Memo2->Text.Length());
	}
	else {
		send_udp(Memo2->Text.t_str(), Memo2->Text.Length());
	}
	Memo1->Lines->Add(GetTime().TimeString() + "> " +
		"Paket odeslán na adresu [" + DEST_IP + ":" + UDP_PORT + "]");

}
// ---------------------------------------------------------------------------

void __fastcall TForm1::Memo2Change(TObject *Sender) {
	UnicodeString hexdata;
	int i;
	Memo3->Clear();
	for (i = 1; i <= Memo2->Text.Length(); i++) {
		hexdata = hexdata + IntToHex(byte(Memo2->Text[i]), 2) + " ";
	}
	Memo3->Lines->Add(hexdata);
	Label1->Caption = IntToStr(Memo2->Text.Length()) + "B";
}
// ---------------------------------------------------------------------------
int start_time;
// Timer pro aktualizaci zobrazeni informaci v programu ---------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender) {

	if (fronta.empty()) {
		start_time = GetTickCount();
		StatusBar1->Panels->Items[2]->Text = "Fronta je prázdna.";
	} else {
		StatusBar1->Panels->Items[2]->Text = "Ve frontě jsou data.";
    }
	// typ přenosu
	if (SAVE_TRANSFER) {
		StatusBar1->Panels->Items[0]->Text = "Zabezpečený přenos";
	}
	else {
		StatusBar1->Panels->Items[0]->Text = "UDP přenos";
	}

	// Cílová ip + port
	StatusBar1->Panels->Items[1]->Text = "Cílová adresa: " + DEST_IP + ":" +
		IntToStr(UDP_PORT);

	// progress bar prijmaci fronty
	ProgressBar2->Position = (float(fronta.size()) / QUEUE_SIZE) * 100;
	if (QUEUE_SIZE >= 1000000)
		Label5->Caption = IntToStr(QUEUE_SIZE / 1000000) + "M";
	else {
		if (QUEUE_SIZE >= 1000)
			Label5->Caption = IntToStr(QUEUE_SIZE / 1000) + "k";
		else
			Label5->Caption = IntToStr(QUEUE_SIZE);
	}
	Label7->Caption = IntToStr(ProgressBar2->Position) + "%";
	Label2->Caption = IntToStr(ProgressBar1->Position) + "%";
	Label24->Caption = UDP_SIZE;

	// --------------------------------------
	// progress bar pro odeslani souboru ----
 	if (SAVE_TRANSFER) {
		if ((tx_thread_app != NULL) && (!tx_thread_app->Finished)) {
			ProgressBar1->Position = tx_thread_app->GetProgress();
			StatusBar1->Panels->Items[3]->Text = "Odesílám soubor ...";
		}
		else {
			ProgressBar1->Position = 0;
			if (CONNECTED) {
				StatusBar1->Panels->Items[3]->Text = "Připraveno k odeslání";
			}
			else {
				StatusBar1->Panels->Items[3]->Text = "Odpojeno";
			}
		}
	}
	else {
		if ((tx_thread_udp != NULL) && (!tx_thread_udp->Finished)) {
			ProgressBar1->Position = tx_thread_udp->GetProgress();
			StatusBar1->Panels->Items[3]->Text = "Odesílám soubor ...";
		}
		else {
			ProgressBar1->Position = 0;
			StatusBar1->Panels->Items[3]->Text = "Připraveno k odeslání";
		}
	}

	if (fronta.size() == QUEUE_SIZE) {
		Memo1->Lines->Add(QUEUE_SIZE * 950 /(GetTickCount()-start_time));
		Timer1->Enabled = false;
	}

}
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Navazani spojeni tlacitkem
void __fastcall TForm1::ToolButton14Click(TObject *Sender) {
	try_connect = true;
   //	GLOBAL_ID_TX = 0;
	//GLOBAL_ID_RX = 0;
	send_app(0xAA, 0, "", 0);
}
// ---------------------------------------------------------------------------

// Tlacitko pro ukonceni spojeni
void __fastcall TForm1::ToolButton15Click(TObject *Sender) {
	try_close = true;
	send_app(0xDD, 2, "", 0);
}
// ---------------------------------------------------------------------------

// Tlacitko pro PING
void __fastcall TForm1::Button1Click(TObject *Sender) {
	Series1->Clear();
	test = new testunit(false);
	test->PING_COUNT = StrToInt(ComboBox3->Text);
	test->PING_FLAG = true;
}


//---------------------------------------------------------------------------
// Konec programu
void __fastcall TForm1::Exit1Click(TObject *Sender)
{
	Form1->Close();
}
//---------------------------------------------------------------------------


