/*
  
  trojan - klasicky UDP klient - linux verze 
  2006 (L) cZokL, mr [dot] czokl [at] gmail [dot] com

  popis fci najdete v komentech a taky v manu
  
*/

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

using namespace std ;

/* 
  BUFSIZE velikost BUFSIZE musi byt u serveru i klienta stejna
  aby nedcochazelo ke ztratam, u tehle kodu je zbytecny delat 
  header file pac by mel tak tri radky proto se tohle define 
  objevuje v obou kodech. 

*/

#define BUFSIZE 1024

inline void usage() {

  cout << "Usage: udp_klient_lin <hostname> <port>" ; 
  
}


int main(int argc, char *argv[]) {
  
  /*
  
  prebrani vstupnich parametru
  
  */
  
  if ( argc != 3 ) {
  
    cerr << "chybny pocet argumentu" << endl ;
    usage() ;
    exit(-1) ;
    
  }
  
  string hostname = argv[1] ;
  
  /* atoi() prevadi retezec na int cislo */
  
  int port = atoi(argv[2]) ;
  
  if ( port < 1 || port > 65536 ) {
  
    cerr << "port neni v rozsahu 1-65536" << endl ;
    usage() ;
    exit(-1) ;
  
  }  
       
      
  
  /*
  
  Struktura hostent se naplni po zavolani fci gethostby* 
  informacema jako je ip adresa nebo nazev hosta
  my pomoci toho prevedeme hostname na ip adresu
  viz. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/hostent_2.asp
  */
  
  
  hostent *H = gethostbyname(hostname.c_str())  ;
  
  if ( H ==  NULL  ) {

    cerr << "hostname neexistuje" << endl ;
    usage() ;
    exit(-1) ;

  }
  
  string ip = inet_ntoa(*(in_addr *)H->h_addr_list[0]) ; 
      
  /*
  
  struktura SOCKADDR_IN urcuje rodinu protokolu (AF_INET pro IPV4), 
  IP adresu a port, se kterym bude socket pracovat
  htons() prevadi int hodnotu na network byte order -  prevod je nutny kuli little endian vs big endian
  inet_addr() prevadi const *char na network byte order ze stejneho duvodu 
  a inet_ntoa() dela presne obracenou vec
  
  
  */

  

  sockaddr_in service ;
  service.sin_family = AF_INET ;
  service.sin_addr.s_addr = inet_addr(ip.c_str()) ; 
  service.sin_port = htons(port) ;

  /*
  
  definice typu SOCKET vypada asi takto : #define SOCKET int - imho zbytecnost
  fce socket vytvari socket descriptor(popisovac) - podobny jako prace se soubory, 
  pomoci neho cteme a zapisujeme z/do socketu
  AF_INET - urcuje rodinu protokolu ipv4, 
  konst. SOCK_DGRAM urcuje ze budeme pouzivat UDP protokol
  treti parametr nechame nulovy 
       
  */

  int sock = socket(AF_INET, SOCK_DGRAM, 0)  ;

  if( sock == -1) {
  
    cerr << "Nepodarilo se vytvorit socket descriptor " ;

    exit(-1) ;

  }

  

  /*
  
  sento odesila packet, vlastne zapise data na socket aby si je mohla druha strana
  vyzvednout, 
  prebira soket deskrtiptor, data, ktera se maji poslat a pak urceni kam ze se maji odeslat 
  
  nejdrive precteme z inputu nejaky komend ktery pak server u sebe provede
  
  */
  
  char buffer[BUFSIZE] ;
  cin.getline(buffer, BUFSIZE) ;
    
  if( sendto(sock, buffer, strlen(buffer) , 0, (sockaddr*) &service, sizeof(service) )  == -1 ) {

    cerr << "Chyba pri odesilani dat" ;

    /*
    
    fce closetsocket() uzavira socket
    
    */
    close(sock) ;
   
    exit(-1) ;

  }

  /*
  
    recvfrom prijima data ze socketu, parametry sou prakticky stejne jako u sendto()
     
  */

  int bytes ;
  socklen_t iSize = sizeof(service) ;

  if ( (bytes = recvfrom(sock, buffer, BUFSIZE, 0, (sockaddr*) &service, &iSize)) == -1) {

    cerr << "data nelze prijmout" << endl ;

    close(sock) ;
    
    exit(-1);

  }

  /* 
  
    dulezita vec - fce recvfrom() vraci pocet prijatych bitu, 
    a protoze buffer je zpocatku zaplnenej nesmyslama a potom 
    prikazem, ktery odesilame (viz. radek cin.getline(buffer, BUFSIZE))
    tak nastavime konec retezce za prijate bity aby se vypsalo jenom
    to co opravdu prislo     
  
  */

  buffer[bytes] = '\0' ;

  cout << buffer << endl ;
  
  close(sock) ;
  
  return 0 ;

}
