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

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

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

extern SOCKET socket_udp;
extern sockaddr_in sock_udp_name;

// Fce pro vypocet checksumy
unsigned short int checksum(unsigned char *addr, int count) {
	register long sum = 0;

	while (count > 1) {
		// vypocet
		sum += *((unsigned short*)addr)++;
		count -= 2;
	}
	/* Add left-over byte, if any */
	if (count > 0)
		sum += *(unsigned char*)addr;
	// prevod na 16bit
while ( sum >> 16);
		sum = (sum & 0xffff) + (sum >> 16);
	return ~sum;
}
// konstruktor
__fastcall testunit::testunit(bool CreateSuspended)
	: TThread(CreateSuspended)
{
	Form1->Button1->Enabled = false;
}

// update VCL
void __fastcall testunit::UpdateCaption()
{
	if (PING_FLAG) {
		// Vypis statistik
		if (pingcount == PING_COUNT) {
            Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + Message);
			Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + "STATISTIKA PRO PING NA ["+DEST_IP.t_str()+"]");
			Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + "Pakety: Odeslané = "+IntToStr(PING_COUNT)+", Přijaté = "+
				IntToStr(PING_COUNT-pingerror)+", Ztracené = "+IntToStr(pingerror)+"("+(int)(pingerror*100.f/PING_COUNT)+"%)");
			if (PING_COUNT - pingerror != 0) {
				latence_avr = (float)latence_sum / (PING_COUNT - pingerror);
				Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + "Doba do přijetí odezvy v ms: Minimum = "+IntToStr(latence_min)+", Maximum = "+IntToStr(latence_max)+", Průměr = "+IntToStr(latence_avr));
			} else {
				Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + "Doba do přijetí odezvy v ms: Minimum = -"+", Maximum = -"+", Průměr = -");
			}
			Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + "----------------------------------------------------------");
		} else {
			Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + Message);
        }
	} else {
		Form1->Memo1->Lines->Add(GetTime().TimeString()+"> " + Message);
	}
}

// kresleni do grafu
void __fastcall testunit::UpdatePingGraf()
{
	Form1->Series1->AddXY(pingcount,time_real,"",clTeeColor);
}
// kresleni do grafu
void __fastcall testunit::UpdatePingGrafError()
{
	Form1->Series1->AddXY(pingcount,0,"lost",clTeeColor);
}

// destructor
void __fastcall testunit::Terminate()
{
	Form1->Button1->Enabled = true;
}


//---------------------------------------------------------------------------
void __fastcall testunit::Execute()
{
icmphdr *icmpsend, *icmprecv;           // struktura ICMP paketu
iphdr *ip;                 			    // struktura IP  hlavicky
hostent *host;					 		//
char pingbuf[500];                      // prijmaci buffer
sockaddr_in PingSockAddr, RecvAddr;   	// info o odesilately
int addrlen = sizeof(sockaddr_in);     	// velikost adresy
SOCKET PingSock;                        // RAW socket
int time_before, time_after;
bool recv = true;


	NameThreadForDebugging("testing");

	latence_sum = 0;

	// -----------------------------------------------------------------
	// PING test -------------------------------------------------------
	// -----------------------------------------------------------------
	if (PING_FLAG && !Terminated) {

		// Vytvoreni RAW socketu pro ICMP
		if ((PingSock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) == INVALID_SOCKET) {
			Message = "Chyba! Nelze vytvorit soket. Pravděpodobně nemáte dostatečná oprávnění.";
			Synchronize(&UpdateCaption);
			Terminate();
		}
		u_long arg = 1;
		if (!Terminated) {
			if (ioctlsocket(PingSock, FIONBIO, &arg) == SOCKET_ERROR) {
				Message = "Chyba! Nelze zapnout neblokovacíi režim socketu.";
				Synchronize(&UpdateCaption);
				Terminate();
			}
		}
		if (!Terminated) {
			int ttl = 255;
			setsockopt(PingSock, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(ttl));
			if ((host = gethostbyname(DEST_IP.t_str())) == NULL) {
				Message = "Chyba! Neplatná IP adresa.";
				Synchronize(&UpdateCaption);
				Terminate();
			}
			PingSockAddr.sin_family = AF_INET;
			PingSockAddr.sin_port = 0;
			// konec inicializace RAW socketu
			// vytvoření echo packetu
			memcpy(&(PingSockAddr.sin_addr), host->h_addr, host->h_length);
			icmpsend = (icmphdr *)malloc(sizeof(icmphdr));
			icmpsend->type = ICMP_ECHO;
			icmpsend->code = 0;
			icmpsend->un.echo.id = 13;
			Message = "Příkaz PING na [" + DEST_IP + "]:";
			Synchronize(&UpdateCaption);
			pingcount = 0;
			pingerror = 0;
			latence_min = 5000;
			latence_max = -1;
		}

		// hlavni smycka
		while (!Terminated && (pingcount < PING_COUNT)) {
			// odeslani echo dotazu
			if (recv) {
				icmpsend->checksum = 0;
				icmpsend->un.echo.sequence = pingcount;
				icmpsend->checksum = checksum((unsigned char *)icmpsend, sizeof(icmphdr));
				sendto(PingSock, (char *)icmpsend, sizeof(icmphdr), 0, (sockaddr *)&PingSockAddr, sizeof(sockaddr));
				recv = false;
				time_before = GetTickCount();
			}

			// zachyceni odpovedi
			if ((recvfrom(PingSock, pingbuf, 500, 0, (sockaddr *)&RecvAddr, &addrlen)) != SOCKET_ERROR) {
				ip = (iphdr *) pingbuf;
				icmprecv = (icmphdr *) (pingbuf + ip->ihl * 4);
				if ((icmprecv->un.echo.id == 13) && (icmprecv->type == ICMP_ECHOREPLY) && (unsigned(icmprecv->un.echo.sequence) == pingcount)) {
					time_after = GetTickCount();
					time_real = time_after - time_before;
					AnsiString IPaddr = inet_ntoa((in_addr)PingSockAddr.sin_addr);
					Message = "Odpověd od ["+ IPaddr + "]: icmp_seq=" + IntToStr(icmprecv->un.echo.sequence) + " TTL="+IntToStr(ip->ttl)+" čas="+IntToStr(time_real)+"ms";
					latence_sum = latence_sum + (time_after - time_before);
					if (time_after - time_before > latence_max) {
						latence_max = time_after - time_before;
					}
					if (time_after - time_before < latence_min) {
						latence_min = (time_after - time_before);
					}
					pingcount ++;
					Synchronize(&UpdateCaption);
					Synchronize(&UpdatePingGraf);
					Sleep(500);
					recv = true;
				}
			} else {
				// Kontrola časového limitu
				if ((GetTickCount() - time_before) > 5000) {
					pingcount ++;
					recv = true;
					pingerror ++;
					Message = "Vypršel časový limit.";
					Synchronize(&UpdateCaption);
					Synchronize(&UpdatePingGrafError);
				}
			}
		}
		closesocket(PingSock);
		PING_FLAG = false;
	}

	Terminate();
}
//---------------------------------------------------------------------------
