/*
 * This work is part of the White Rabbit project
 *
 * Copyright (C) 2012 CERN (www.cern.ch)
 * Author: Maciej Lipinski <maciej.lipinski@cern.ch>
 * - parts inspired/based on private code by Alessandro Rubini and Bartosz Bielawski
 * - network interface taken from ptp-noposix
 * 
 * Released according to the GNU GPL, version 2 or any later version.
 * 
 * Please, see README before using.
 * 
 */
#include "networkTool.h"


void delay(int d)
{
	while (d--)
		asm volatile ("nop");
}

my_socket *create_socket(wr_sockaddr_t *bind_addr,RunTimeOpts *rtOpts)
{
	struct sockaddr_ll sll;
	struct my_socket *s;
	struct ifreq f;

	int fd;

	fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));

	if(fd < 0)
	{
		perror("socket()");
		return NULL;
	}

	fcntl(fd, F_SETFL, O_NONBLOCK);

	// Put the controller in promiscious mode, so it receives everything
	strcpy(f.ifr_name, bind_addr->if_name);
	if(ioctl(fd, SIOCGIFFLAGS,&f) < 0) { perror("ioctl()"); return NULL; }
	f.ifr_flags |= IFF_PROMISC;
	if(ioctl(fd, SIOCSIFFLAGS,&f) < 0) { perror("ioctl()"); return NULL; }

	// Find the inteface index
	strcpy(f.ifr_name, bind_addr->if_name);
	ioctl(fd, SIOCGIFINDEX, &f);


	sll.sll_ifindex = f.ifr_ifindex;
	sll.sll_family   = AF_PACKET;
	sll.sll_protocol = htons(bind_addr->ethertype);
	sll.sll_halen = 6;

	memcpy(sll.sll_addr, bind_addr->mac, 6);

	if(bind(fd, (struct sockaddr *)&sll, sizeof(struct sockaddr_ll)) < 0)
	{
		close(fd);
		perror("bind()");
		return NULL;
	}

	s=(struct my_socket*)calloc(sizeof(struct my_socket), 1);
	s->if_index = f.ifr_ifindex;
	memcpy(s->local_mac, f.ifr_hwaddr.sa_data, 6);
	memcpy(&s->bind_addr, bind_addr, sizeof(wr_sockaddr_t));
	s->fd = fd;
	
	printf("Created socket at interface %s (number %d) with hardware address: %x%x%x%x%x%x\n",
	s->bind_addr.if_name, s->if_index, 
	s->local_mac[0],
	s->local_mac[1],
	s->local_mac[2],
	s->local_mac[3],
	s->local_mac[4],
	s->local_mac[5] );
	
	return (my_socket*)s;
}

int rxfrom(my_socket *sock, wr_sockaddr_t *from, void *data, size_t data_length)
{
	struct my_socket *s = (struct my_socket *)sock;
	struct bench_pkt pkt;
	unsigned char payload[ETHER_MTU];
	struct msghdr msg;
	struct iovec entry;
	struct sockaddr_ll from_addr;
	struct {
		struct cmsghdr cm;
		char control[1024];
	} control;
	struct cmsghdr *cmsg;
	size_t len = data_length;// + sizeof(struct ethhdr);

	memset(&msg, 0, sizeof(msg));
	msg.msg_iov = &entry;
	msg.msg_iovlen = 1;
	entry.iov_base = &payload;
	entry.iov_len = len;
	msg.msg_name = (caddr_t)&from_addr;
	msg.msg_namelen = sizeof(from_addr);
	msg.msg_control = &control;
	msg.msg_controllen = sizeof(control);

	int ret = recvmsg(s->fd, &msg, MSG_DONTWAIT);
	if(ret < 0 && errno == EAGAIN) return 0; // would be blocking
	if(ret == -EAGAIN) return 0;

	if(ret <= 0) return ret;
	
        memcpy(data, payload, data_length);
        printf("rx");
	return (ret);
}

int rx_seq_from(my_socket *sock, wr_sockaddr_t *from, int *data, size_t data_length, 
                RunTimeOpts *rtOpts, long *acc_latency, long *acc_interval, timeval_t *last_rx)
{
	struct my_socket *s = (struct my_socket *)sock;
	struct bench_pkt pkt;
	struct msghdr msg;
	struct iovec entry;
	struct sockaddr_ll from_addr;
	struct {
		struct cmsghdr cm;
		char control[1024];
	} control;
	struct cmsghdr *cmsg;
	size_t len = data_length;// + sizeof(struct ethhdr);

	memset(&msg, 0, sizeof(msg));
	msg.msg_iov = &entry;
	msg.msg_iovlen = 1;
	entry.iov_base = &pkt;
	entry.iov_len = len;
	msg.msg_name = (caddr_t)&from_addr;
	msg.msg_namelen = sizeof(from_addr);
	msg.msg_control = &control;
	msg.msg_controllen = sizeof(control);

	int ret = recvmsg(s->fd, &msg, MSG_DONTWAIT);
	gettimeofday(&pkt.rx, NULL);
	if (!pkt.size2) pkt.size2 = ret;	
	
	if(ret < 0 && errno==EAGAIN) return 0; // would be blocking
	if(ret == -EAGAIN) return 0;

	if(ret <= 0) return ret;
	
	if((pkt.rx.tv_usec-pkt.tx.tv_usec)<0) 
	{
	  pkt.xx.tv_usec = 1000*1000+pkt.rx.tv_usec-pkt.tx.tv_usec;
	  pkt.xx.tv_sec = pkt.rx.tv_sec-pkt.tx.tv_sec-1;
	}
	else
	{
	  pkt.xx.tv_usec = pkt.rx.tv_usec - pkt.tx.tv_usec;
	  pkt.xx.tv_sec  = pkt.rx.tv_sec  - pkt.tx.tv_sec;
	}
	(*acc_latency) = (*acc_latency) + (long)pkt.xx.tv_usec;
	
	if(last_rx->tv_sec == 0 && last_rx->tv_usec == 0) //first
	{
	  // first time, do nothing
	}
	else if((pkt.rx.tv_usec-last_rx->tv_usec)<0)
	{
	  *acc_interval = *acc_interval + (long)(1000*1000+pkt.rx.tv_usec-last_rx->tv_usec) 
	                                 + 1000*1000*(long)(pkt.rx.tv_sec-last_rx->tv_sec-1);
	}
	else
	{
	  *acc_interval = *acc_interval + (long)(pkt.rx.tv_usec-last_rx->tv_usec) 
	                                 + 1000*1000*(long)(pkt.rx.tv_sec-last_rx->tv_sec);
	}

	last_rx->tv_usec = pkt.rx.tv_usec;
	last_rx->tv_sec  = pkt.rx.tv_sec;
	
	if(rtOpts->show_rxtx_data == DEBUG_DETAIL)
	   printf("n %4i transmision time: %9li.%06li [s] tx_size: %i rx_size: %i\n",
	         pkt.seq,
	         pkt.xx.tv_sec, 
	         pkt.xx.tv_usec,
	         pkt.size,
	         pkt.size2);

       *data=(int)pkt.seq;
	return (ret);
}

int rx_fastest_burst_from(my_socket *sock, wr_sockaddr_t *from, int *data, size_t data_length, 
                          RunTimeOpts *rtOpts,  rx_data_t rx_data[])
{
	struct my_socket *s = (struct my_socket *)sock;
	struct bench_pkt pkt;
	struct msghdr msg;
	struct iovec entry;
	struct sockaddr_ll from_addr;
	struct {
		struct cmsghdr cm;
		char control[1024];
	} control;
	struct cmsghdr *cmsg;
	size_t len = data_length;// + sizeof(struct ethhdr);
	signed long timeout = 0;
	int ret ;
	int rx_msg_id=0;
	int total_msg_cnt=0;
	int i=0;
	int j=0;
	int size;
	timeval_t last_rx = {0,0};
	timeval_t current_rx;	
	
	memset(&msg, 0, sizeof(msg));
	printf("before while\n");
	while(rx_msg_id < rtOpts->pkt_number_in_burst)
	{
	   msg.msg_iov = &entry;  
	   msg.msg_iovlen = 1;    
	   entry.iov_base = &pkt; 
	   entry.iov_len = len;
	   msg.msg_name = (caddr_t)&from_addr;
	   msg.msg_namelen = sizeof(from_addr);
	   msg.msg_control = &control;
	   msg.msg_controllen = sizeof(control);
	   ret = recvmsg(s->fd, &msg, MSG_DONTWAIT);
	   if(ret > 0)
	   {	   
	      timeout = 0;
	      gettimeofday(&rx_data[total_msg_cnt].rx, NULL);
	      if (!pkt.size2) pkt.size2 = ret;
	      size = ret;	
	      rx_msg_id = pkt.seq;
	      rx_data[total_msg_cnt].rx_seq_id  = pkt.seq;
	      rx_data[total_msg_cnt].tx.tv_usec = pkt.tx.tv_usec;
	      rx_data[total_msg_cnt].tx.tv_sec  = pkt.tx.tv_sec;
// 	      last_rx.tv_sec        = rx_data[i].tx.tv_sec;
// 	      printf("rx_msg_id=%d, total_msg_cnt=%d, i=%d,j=%d\n",rx_msg_id,total_msg_cnt,i,j);
	      rx_msg_id++;
	      total_msg_cnt++;
	      i++;
	      j=j+1;
	   }
	   
	   if(timeout > 1000000) 
	   {
	     printf("timetout\n"); 
	     break;
	   }
	   else if(rx_msg_id != 0)
	     timeout++;
	}

       *data = size;//size
	return (total_msg_cnt);
}

int tx_burst_to(my_socket *sock, wr_sockaddr_t *to, int burst_id, int burst_number, 
                 size_t data_length, int pkt_time_interval, RunTimeOpts *rtOpts)
{

	struct bench_pkt *p;
	int i, delta, slen, pktn, pktt, pkts;	
	struct my_socket *s = (struct my_socket *)sock;
	struct sockaddr_ll sll;
	int rval;
	int promil  = burst_number/1000;
	int percent = burst_number/100;
	struct timeval tsend, tstamp;
        
	if(data_length > ETHER_MTU-8) return -EINVAL;
	if(data_length < sizeof(struct bench_pkt)) return -EINVAL;
	/* alloc packet */
	p = (bench_pkt*)calloc(1, data_length);
	if (!p) exit(5);
       
	p->burst_id = burst_id;
	p->size = data_length; 	
	
	if(data_length < 60) /* pad to the minimum allowed packet size */
		data_length = 60;

	memset(&sll, 0, sizeof(struct sockaddr_ll));

	sll.sll_ifindex = s->if_index;
	sll.sll_family = AF_PACKET;
	sll.sll_protocol = htons(to->ethertype);
	sll.sll_halen = 6;
	memcpy(sll.sll_addr, to->mac, sizeof(mac_addr_t));
	
	gettimeofday(&tsend, NULL); /* time of prev-than-first tx */
	for (i=0; i<burst_number; i++) 
	{
	  p->seq = i;
	  /* wait tx time */
	  if(pkt_time_interval)
	  {
	    tsend.tv_usec += pkt_time_interval;
	    if (tsend.tv_usec > 1000*1000) 
	    {
	      tsend.tv_usec -= 1000*1000;
	      tsend.tv_sec++;
	    }
	    gettimeofday(&tstamp, NULL);
	    delta = (tsend.tv_sec - tstamp.tv_sec)*1000*1000+tsend.tv_usec - tstamp.tv_usec;
	    if (delta < 0) delta = 0;
	    usleep(delta);
	  }
	  /* stamp packet and send it */
	  gettimeofday(&p->tx, NULL);	
	  rval =  sendto(s->fd, p, data_length, 0, (struct sockaddr *)&sll,sizeof(struct sockaddr_ll));
	  if(rval<0)
	  { 
	    printf("Sending burst failed at frame %d\n",i);
	  }
	  if((rtOpts->show_rxtx_data == DEBUG_LIGHT) && (i%percent) == 0) 
	     printf("send %d \%\n",i/percent);
	}
        
	return i;
}

/* 
 * send burst as fast as possible, record time of sending to calculate average frame
 * sending intervals
 */
int tx_fasters_burst_to(my_socket *sock, wr_sockaddr_t *to, int burst_id, int burst_number, 
                        size_t data_length, RunTimeOpts *rtOpts, timeval_t tx_timestamps[],
                        int pkt_time_interval)
{

	struct bench_pkt *p;
	int i, delta, slen, pktn, pktt, pkts;	
	struct my_socket *s = (struct my_socket *)sock;
	struct sockaddr_ll sll;
	int rval;
        
	if(data_length > ETHER_MTU-8) return -EINVAL;
	if(data_length < sizeof(struct bench_pkt)) return -EINVAL;
	/* alloc packet */
	p = (bench_pkt*)calloc(1, data_length);
	if (!p) exit(5);
       
	p->burst_id = burst_id;
	p->size = data_length; 	
	
	if(data_length < 60) /* pad to the minimum allowed packet size */
		data_length = 60;

	memset(&sll, 0, sizeof(struct sockaddr_ll));

	sll.sll_ifindex = s->if_index;
	sll.sll_family = AF_PACKET;
	sll.sll_protocol = htons(to->ethertype);
	sll.sll_halen = 6;
	memcpy(sll.sll_addr, to->mac, sizeof(mac_addr_t));
	
	for (i=0; i<burst_number; i++) 
	{
	  p->seq = i;
	  /* stamp packet and send it */
	  gettimeofday(&p->tx, NULL);	
	  rval =  sendto(s->fd, p, data_length, 0, (struct sockaddr *)&sll,sizeof(struct sockaddr_ll));
	  tx_timestamps[i].tv_usec = p->tx.tv_usec;
	  tx_timestamps[i].tv_sec  = p->tx.tv_sec;
	  if(rval<0)
	  { 
	    printf("Sending burst failed at frame %d\n",i);
	  }
	  delay(pkt_time_interval);
	}
        
	return i;
}

int txto(my_socket *sock, wr_sockaddr_t *to, void *data, size_t data_length)
{

	struct etherpacket pkt;
	struct my_socket *s = (struct my_socket *)sock;
	struct sockaddr_ll sll;
	int rval;
        
	if(data_length > ETHER_MTU) return -EINVAL;

	if(data_length < 60) /* pad to the minimum allowed packet size */
		data_length = 60;

	memset(&sll, 0, sizeof(struct sockaddr_ll));

	sll.sll_ifindex = s->if_index;
	sll.sll_family = AF_PACKET;
	sll.sll_protocol = htons(to->ethertype);
	sll.sll_halen = 6;
        memcpy(sll.sll_addr, to->mac, sizeof(mac_addr_t));
        
	rval =  sendto(s->fd, data, data_length, 0, (struct sockaddr *)&sll,
		       sizeof(struct sockaddr_ll));
        
	return rval;
}


int  startup(int argc, char **argv, RunTimeOpts *rtOpts)
{
   int c,nr;
   printf("start\n");
  
   /// default values
   rtOpts->rx_do             = 0;
   rtOpts->rx_burst_do       = 0;
   rtOpts->tx_burst_do       = 0;
   rtOpts->tx_do             = 0;
   rtOpts->ethertype         = ETHER_TYPE;
   rtOpts->pkt_time_interval = 0;
   rtOpts->pkt_number_in_burst = 100;
   rtOpts->pkt_payload_size   = 500;
   rtOpts->show_rxtx_data     =DEBUG_NO;
   memcpy(rtOpts->dst_mac, DEFAULT_UNICAST_MAC, sizeof(mac_addr_t));
   
   while( (c = getopt(argc, argv, "?r:t:f:s:n:u:mbe:v:z:h:j:q:g:")) != -1 ) {
     switch(c) {
     case '?':
       printf(
         "\nUsage:  networkTool [OPTION]\n\n"
         "\n"
         "-?                show this page\n"
         "\n"
         "-r if_name        receive frames on interface if_name\n"
         "-t if_name        transmit signle frame on interface if_name\n"
         "-f if_name        flood (burst) frames on interface if_name\n"
         "-s if_name        receive burst of frames  on interface if_name\n"
         "-n frame_number   number of frames in received/transmitted burst\n"
         "-j power          we send 10^power of frames\n"
         "-u number         send to unicast \n"
         "                  0:  %x:%x:%x:%x:%x:%x (default)\n"
         "                  1:  %x:%x:%x:%x:%x:%x (eth5)\n"
         "                  2:  %x:%x:%x:%x:%x:%x (eth4_rename)\n"
         "-m                send to default unicast mac:%x:%x:%x:%x:%x:%x\n"
         "-b                send to broadcast: FF:FF:FF:FF:FF:FF\n"
         "-g MAC            send to specified MAC (format: AA:BB:CC:DD:FF:11)\n"
         "-e ethertype      set ethertype\n"
         "-v time_interval  set time interval between frames in burts (not optimal, smallest 60[us])\n"
         "-q time_interval  set time interval between frames in burst (using optimized functions)"
         "                  can be used also during reception then rx_fasters_burst_from() used "
         "-z payload_size   set payload size\n"
         "-h                show rx/tx data (e.g.: time)\n"
         "\n",
         DEFAULT_UNICAST_MAC[0],DEFAULT_UNICAST_MAC[1],DEFAULT_UNICAST_MAC[2],
         DEFAULT_UNICAST_MAC[3],DEFAULT_UNICAST_MAC[4],DEFAULT_UNICAST_MAC[5],
         UNICAST_MAC_ETH_5[0],UNICAST_MAC_ETH_5[1],UNICAST_MAC_ETH_5[2],
         UNICAST_MAC_ETH_5[3],UNICAST_MAC_ETH_5[4],UNICAST_MAC_ETH_5[5],
         UNICAST_MAC_ETH_4_RENAME[0],UNICAST_MAC_ETH_4_RENAME[1],UNICAST_MAC_ETH_4_RENAME[2],
         UNICAST_MAC_ETH_4_RENAME[3],UNICAST_MAC_ETH_4_RENAME[4],UNICAST_MAC_ETH_4_RENAME[5],
         DEFAULT_MULTICAST_MAC[0],DEFAULT_MULTICAST_MAC[1],DEFAULT_MULTICAST_MAC[2],
         DEFAULT_MULTICAST_MAC[3],DEFAULT_MULTICAST_MAC[4],DEFAULT_MULTICAST_MAC[5]
         );
         return 0;
     case 'r':
       memset(rtOpts->rx_if_name, 0, IFACE_NAME_LEN);
       strncpy(rtOpts->rx_if_name, optarg, IFACE_NAME_LEN);
       printf("Receive on interface: %s \n",rtOpts->rx_if_name);
       rtOpts->rx_do = 1;
       break;
     case 't':
       memset(rtOpts->tx_if_name, 0, IFACE_NAME_LEN);
       strncpy(rtOpts->tx_if_name, optarg, IFACE_NAME_LEN);
       printf("Transmit on interface: %s \n",rtOpts->tx_if_name);
       rtOpts->tx_do = 1;
       break;
     case 'f':
       memset(rtOpts->tx_if_name, 0, IFACE_NAME_LEN);
       strncpy(rtOpts->tx_if_name, optarg, IFACE_NAME_LEN);
       printf("Transmit burst on interface: %s \n",rtOpts->tx_if_name);
       rtOpts->tx_burst_do = 1;
       break;  
     case 's':
       memset(rtOpts->rx_if_name, 0, IFACE_NAME_LEN);
       strncpy(rtOpts->rx_if_name, optarg, IFACE_NAME_LEN);
       printf("Receive burst on interface: %s \n",rtOpts->rx_if_name);
       rtOpts->rx_burst_do = 1;
       break;      
     case 'n':
       rtOpts->pkt_number_in_burst = strtol(optarg, &optarg, 0);
       printf("Number of frames in the burst: %d \n",rtOpts->pkt_number_in_burst);
       break;      
     case 'j':
       rtOpts->pkt_number_in_burst = pow(10,strtol(optarg, &optarg, 0));
       printf("Number of frames in the burst: %d \n",rtOpts->pkt_number_in_burst);
       break;         
     case 'u':
       nr= strtol(optarg, &optarg, 0);
       if(nr == 1)
         memcpy(rtOpts->dst_mac, UNICAST_MAC_ETH_5, sizeof(mac_addr_t));
       else if (nr == 2)
         memcpy(rtOpts->dst_mac, UNICAST_MAC_ETH_4_RENAME, sizeof(mac_addr_t));
       else
         memcpy(rtOpts->dst_mac, DEFAULT_UNICAST_MAC, sizeof(mac_addr_t));
       
       printf("Send/Receive at unicast mac: %x:%x:%x:%x:%x:%x \n",
         rtOpts->dst_mac[0],
         rtOpts->dst_mac[1],
         rtOpts->dst_mac[2],
         rtOpts->dst_mac[3],
         rtOpts->dst_mac[4],
         rtOpts->dst_mac[5]);
       break;      
     case 'm':
       memcpy(rtOpts->dst_mac, DEFAULT_MULTICAST_MAC, sizeof(mac_addr_t));
       printf("Send/Receive at multicast mac: %x:%x:%x:%x:%x:%x \n",
         rtOpts->dst_mac[0],
         rtOpts->dst_mac[1],
         rtOpts->dst_mac[2],
         rtOpts->dst_mac[3],
         rtOpts->dst_mac[4],
         rtOpts->dst_mac[5]);
       break;      
     case 'b':
       memcpy(rtOpts->dst_mac, BROADCAST_MAC, sizeof(mac_addr_t));
       printf("Send/Receive at broadcast mac: %x:%x:%x:%x:%x:%x \n",
         rtOpts->dst_mac[0],
         rtOpts->dst_mac[1],
         rtOpts->dst_mac[2],
         rtOpts->dst_mac[3],
         rtOpts->dst_mac[4],
         rtOpts->dst_mac[5]);
       break;      
     case 'e':
       rtOpts->ethertype         = strtol(optarg, &optarg, 0);
       printf("Ethertype: %d\n", rtOpts->ethertype);
       break;      
     case 'v':
       rtOpts->pkt_time_interval         = strtol(optarg, &optarg, 0);
       printf("Time interval between bursts: %d[us]\n", rtOpts->pkt_time_interval);
       break;      
     case 'q':
       rtOpts->pkt_time_interval         = strtol(optarg, &optarg, 0);
       if(rtOpts->tx_burst_do == 1)
       {
          rtOpts->tx_burst_do = 2; // fasters burst
          if(rtOpts->pkt_time_interval == 0)
             printf("Time interval between bursts[optimized]: as fast as possible\n");
	  else
	     printf("Time interval between bursts[optimized]: %d nops\n",rtOpts->pkt_time_interval);
       }
       else if(rtOpts->pkt_time_interval==0 && rtOpts->rx_burst_do == 1)
       {
          rtOpts->rx_burst_do = 2; // fasters burst
          printf("Optimized function for burst reception: as fast as possible\n");
       }       
       else
         printf("to use -q number, you need to specify -s or -f");
       break;        
     case 'z':
       rtOpts->pkt_payload_size         = strtol(optarg, &optarg, 0);
       printf("Payload size: %d\n", rtOpts->pkt_payload_size);
       break;      
     case 'h':
       rtOpts->show_rxtx_data = strtol(optarg, &optarg, 0);
       printf("Show rx/tx data (0=no debug, 1=light debug, 2=detail debug\n");  
       break;      
     case 'g':
//        optarg
       printf("test output %s\n",optarg);
       sscanf(optarg,"%x:%x:%x:%x:%x:%x",
	 &rtOpts->dst_mac[0],
         &rtOpts->dst_mac[1],
         &rtOpts->dst_mac[2],
         &rtOpts->dst_mac[3],
         &rtOpts->dst_mac[4],
         &rtOpts->dst_mac[5]);
       
       printf("Send/Receive at multicast mac: %x:%x:%x:%x:%x:%x \n",
         rtOpts->dst_mac[0],
         rtOpts->dst_mac[1],
         rtOpts->dst_mac[2],
         rtOpts->dst_mac[3],
         rtOpts->dst_mac[4],
         rtOpts->dst_mac[5]);       
       break;      
          
     default:
       printf("Wrong arguments\n"
       "\nUsage:  networkTool [OPTION]\n\n");
       
       return -1;
       break;
     }
   }  
   if(rtOpts->tx_burst_do == 2 && rtOpts->pkt_number_in_burst > MAX_BURST)
   {
      rtOpts->pkt_number_in_burst = MAX_BURST;
       printf("Cannot flood more then %d frames with the fasters burst\n",MAX_BURST);
   }
   return 0;
}

int initialize(my_socket_t *socket_tx, my_socket_t *socket_rx, RunTimeOpts *rtOpts)
{
///todo
}

int send_burst(my_socket_t *socket_tx, int burst_id, size_t payload_size, RunTimeOpts *rtOpts)
{
   wr_sockaddr_t bindaddr_tx;
   int ret;
   strcpy(bindaddr_tx.if_name, rtOpts->tx_if_name); //TODO
   bindaddr_tx.ethertype = rtOpts->ethertype; 	        // FEC's etherType, fecked
   memcpy(bindaddr_tx.mac, rtOpts->dst_mac, sizeof(mac_addr_t));

   printf("Burst of frames...:frame number = %d, frame payload = %d [bytes], interval = %d [us]\n",
           rtOpts->pkt_number_in_burst,payload_size,rtOpts->pkt_time_interval);
   
   ret = tx_burst_to(socket_tx,&bindaddr_tx,burst_id,rtOpts->pkt_number_in_burst, 
                     payload_size,rtOpts->pkt_time_interval, rtOpts);
     
   if(ret != rtOpts->pkt_number_in_burst)
   { 
     
     printf("Failed to send a burst of frames:\n");
     printf("\tframe number            : %d \n",ret);     
     return -1;
   }
   else
   {
     printf("Send a burst of frames:\n");
     printf("\tframe number            : %d \n",ret);
     printf("\tburst id                : %d \n",burst_id);
     printf("\tframe payload           : %d [bytes]\n",payload_size);
     printf("\tinterval between frames : %d [us]\n",rtOpts->pkt_time_interval);
   }  
   return 0;
  
}

int send_fasters_burst(my_socket_t *socket_tx, int burst_id, size_t payload_size, RunTimeOpts *rtOpts)
{
   wr_sockaddr_t bindaddr_tx;
//    timeval_t tx_timestamps[1000000];
   timeval_t *tx_timestamps;
   int ret,i;
   long acc_interval = 0;
  
   tx_timestamps = (timeval_t*)calloc(MAX_BURST,sizeof(timeval_t));
   if (!tx_timestamps) exit(5);   
   
   strcpy(bindaddr_tx.if_name, rtOpts->tx_if_name); //TODO
   bindaddr_tx.ethertype = rtOpts->ethertype; 	        // FEC's etherType, fecked
   memcpy(bindaddr_tx.mac, rtOpts->dst_mac, sizeof(mac_addr_t));

   if(rtOpts->pkt_time_interval == 0)
     printf("Burst of frames...:frame number = %d, frame payload = %d [bytes], interval = faster\n",
           rtOpts->pkt_number_in_burst,payload_size);
   else
     printf("Burst of frames...:frame number = %d, frame payload = %d [bytes], interval = %d [nops]\n",
           rtOpts->pkt_number_in_burst,payload_size, rtOpts->pkt_time_interval);
   
//    usleep(10000);
   ret = tx_fasters_burst_to(socket_tx,&bindaddr_tx,burst_id,rtOpts->pkt_number_in_burst, 
                             payload_size, rtOpts,tx_timestamps,rtOpts->pkt_time_interval);
     
   if(ret != rtOpts->pkt_number_in_burst)
   { 
     printf("Failed to send a burst of frames:\n");
     printf("\tframe number            : %d \n",ret);     
     return -1;
   }
   else
   {
     for(i=1;i<rtOpts->pkt_number_in_burst;i++)
     {

       if((tx_timestamps[i].tv_usec-tx_timestamps[i-1].tv_usec)<0)
       {
          acc_interval += (long)(1000*1000+tx_timestamps[i].tv_usec-tx_timestamps[i-1].tv_usec) 
                        + 1000*1000*(long)(tx_timestamps[i].tv_sec-tx_timestamps[i-1].tv_sec-1);          
       }
       else
       {
          acc_interval += (long)(tx_timestamps[i].tv_usec-tx_timestamps[i-1].tv_usec) 
                        + 1000*1000*(long)(tx_timestamps[i].tv_sec-tx_timestamps[i-1].tv_sec);           
       }
       
     if(rtOpts->show_rxtx_data == DEBUG_DETAIL)
	printf("n %4i transmision time: %9li.%06li [s], acc_interval: %ld\n",
	         i,
	         tx_timestamps[i].tv_sec, 
	         tx_timestamps[i].tv_usec,
	         acc_interval);       
     }
     printf("Send a burst of frames:\n");
     printf("\tframe number            : %d \n",ret);
     printf("\tburst id                : %d \n",burst_id);
     printf("\tframe payload           : %d [bytes]\n",payload_size);
     printf("\tinterval between frames : %d [us]\n",acc_interval/ret);
//      printf("\taccumulated   interval  : %d [us]\n",acc_interval);
   }  
   return 0;
  
}
int receive_burst(my_socket_t *socket_rx, RunTimeOpts *rtOpts)
{
   timeval_t last_rx = {0,0};
   timeval_t current_rx;
   long acc_latency   = 0;
   long acc_interval  = 0;
   int timeout = 0;
   wr_sockaddr_t bindaddr_rx;
   int ret, msg_cnt=0;
   int size;
   int total_msg_cnt = 0;
   long total_delay=0;
   int seq_id ;
   int promil  = rtOpts->pkt_number_in_burst/1000;
   int percent = rtOpts->pkt_number_in_burst/100;
   unsigned char 	msg_rx[PACKET_SIZE];
   strcpy(bindaddr_rx.if_name, rtOpts->rx_if_name); //TODO
   bindaddr_rx.ethertype = rtOpts->ethertype; 	        // FEC's etherType, fecked
   memcpy(bindaddr_rx.mac, rtOpts->dst_mac, sizeof(mac_addr_t));
   
   printf("Waiting to receive a burst of frames....\n");
      
   while((msg_cnt < rtOpts->pkt_number_in_burst) && 
         ( (msg_cnt == 0) || (current_rx.tv_sec < (last_rx.tv_sec+5))))
   {
     ret = rx_seq_from(socket_rx, &bindaddr_rx, &seq_id, PACKET_SIZE,rtOpts,
                       &acc_latency, &acc_interval, &last_rx);
     if(ret > 0)
     {
       size = ret;
       if((rtOpts->show_rxtx_data == DEBUG_LIGHT) && (msg_cnt%percent) == 0) 
	 printf("received %d \%\n",msg_cnt/percent);
       if(msg_cnt != seq_id)
       {
         printf("Lost %d frames [is rx_id = %d, should be seq_id=%d]\n",(seq_id-msg_cnt),seq_id, msg_cnt);
         msg_cnt = seq_id;
       }
       msg_cnt++;
       total_msg_cnt++;
     }
     gettimeofday(&current_rx, NULL);
   }
   printf("Received a burst of frames:\n");
   printf("frame number            : %10d \n",total_msg_cnt);
   printf("frame size              : %10d [bytes]\n",size);
   printf("avg interval            : %10d [us]\n",acc_interval/total_msg_cnt);
   printf("avg latency             : %10d [us]\n",acc_latency/total_msg_cnt);
   return 0;
}

int receive_fastest_burst(my_socket_t *socket_rx, RunTimeOpts *rtOpts)
{

   long acc_latency   = 0;
   long acc_interval  = 0;
   wr_sockaddr_t bindaddr_rx;
   int ret, msg_cnt=0;
   int size,i;
   int data;
   int total_msg_cnt = 0;
   long total_delay=0;
//    rx_data_t rx_data[1000];

   rx_data_t *rx_data;
   rx_data = (rx_data_t*)calloc(MAX_BURST,sizeof(rx_data_t));
   if (!rx_data) exit(5);
   
   strcpy(bindaddr_rx.if_name, rtOpts->rx_if_name); //TODO
   bindaddr_rx.ethertype = rtOpts->ethertype; 	        // FEC's etherType, fecked
   memcpy(bindaddr_rx.mac, rtOpts->dst_mac, sizeof(mac_addr_t));
   
   printf("Waiting to receive a burst of frames as fast as possible....:\n");
      
   ret = rx_fastest_burst_from(socket_rx, &bindaddr_rx, &size, PACKET_SIZE,rtOpts,rx_data);
   msg_cnt=1;
   for(i=1;i<ret;i++)
   {
      if((rx_data[i].rx.tv_usec-rx_data[i-1].rx.tv_usec)<0)
      {
         acc_interval += (long)(1000*1000+rx_data[i].rx.tv_usec-rx_data[i-1].rx.tv_usec) 
                      + 1000*1000*(long)(rx_data[i].rx.tv_sec-rx_data[i-1].rx.tv_sec-1);          
      }
      else
      {
         acc_interval += (long)(rx_data[i].rx.tv_usec-rx_data[i-1].rx.tv_usec) 
                      +  1000*1000*(long)(rx_data[i].rx.tv_sec-rx_data[i-1].rx.tv_sec);		       
      }
      if((rx_data[i].rx.tv_usec-rx_data[i].tx.tv_usec)<0) 
      {
         rx_data[i].xx.tv_usec = 1000*1000+rx_data[i].rx.tv_usec-rx_data[i].tx.tv_usec;
         rx_data[i].xx.tv_sec  = rx_data[i].rx.tv_sec-rx_data[i].tx.tv_sec-1;
      }
      else
      {
         rx_data[i].xx.tv_usec = rx_data[i].rx.tv_usec-rx_data[i].tx.tv_usec;
         rx_data[i].xx.tv_sec  = rx_data[i].rx.tv_sec-rx_data[i].tx.tv_sec;	 
      }      
      acc_latency = acc_latency + (long)rx_data[i].xx.tv_usec;
      
      if((msg_cnt != rx_data[i].rx_seq_id) || (rx_data[i].rx_seq_id-rx_data[i-1].rx_seq_id) != 1)
      {
	printf("n %4i lost %5d frame(s). Should be %10d, is %10d \n",i,
	           rx_data[i].rx_seq_id-rx_data[i-1].rx_seq_id, msg_cnt,rx_data[i].rx_seq_id);
	msg_cnt = rx_data[i].rx_seq_id;
      }
      msg_cnt++;
	
     if(rtOpts->show_rxtx_data == DEBUG_DETAIL)
	printf("n %4i transmision time: %9li.%06li [s], latendy: %ld [us]\n",
	         i,
	         rx_data[i].tx.tv_sec, 
	         rx_data[i].tx.tv_usec,
	         rx_data[i].xx.tv_usec);       	
   }
   
   printf("Received a burst of frames:\n");
   printf("frame number            : %10d \n",ret);
   printf("frame size              : %10d [bytes]\n",size);
   printf("avg interval            : %10d [us]\n",acc_interval/ret);
//    printf("acc interval            : %10d [us]\n",acc_interval);
   printf("avg latency             : %10d [us]\n",acc_latency/ret);
   return 0;
}


int receive_frames(my_socket_t *socket_rx, RunTimeOpts *rtOpts)
{
   wr_sockaddr_t bindaddr_rx;
   int ret, msg_cnt=0;
   unsigned char 	msg_rx[PACKET_SIZE];
   strcpy(bindaddr_rx.if_name, rtOpts->rx_if_name); //TODO
   bindaddr_rx.ethertype = rtOpts->ethertype; 	        // FEC's etherType, fecked
   memcpy(bindaddr_rx.mac, rtOpts->dst_mac, sizeof(mac_addr_t));
   
   printf("Waiting to receive a frames....\n");
   while(1)
   {
     ret = rxfrom(socket_rx, &bindaddr_rx, (unsigned char*)msg_rx, PACKET_SIZE);
     if(ret > 0)
     {
       printf("Received a frames:\n");
       printf("frame number            : %d \n",msg_cnt);
       printf("frame size              : %d \n",ret);
       msg_cnt++;
     }
   }
   return 0;
}


int send_frame(my_socket_t *socket_tx, void *data, size_t payload_size, RunTimeOpts *rtOpts)
{
   wr_sockaddr_t bindaddr_tx;
   int ret;
   strcpy(bindaddr_tx.if_name, rtOpts->tx_if_name); //TODO
   bindaddr_tx.ethertype = rtOpts->ethertype; 	        // FEC's etherType, fecked
   memcpy(bindaddr_tx.mac, rtOpts->dst_mac, sizeof(mac_addr_t));
   
   ret = txto(socket_tx,&bindaddr_tx,data,payload_size);
     
   if(ret != payload_size)
   { 
     
     printf("Failed to send a frames:\n");
     printf("\tframe payload size      : %d [bytes] \n",payload_size);     
     return -1;
   }
   else
   {
     printf("Send a frames:\n");
     printf("\tframe payload           : %d [bytes]\n",payload_size);
   }  
   return 0;
  
}

int main(int argc, char **argv)
{
  
   struct my_socket *socket_tx, *socket_rx;
   wr_sockaddr_t bindaddr_tx, bindaddr_rx;
   int 			recv_msg_size, tx_msg_size, i;
   unsigned char 	msg_rx[PACKET_SIZE];
   unsigned char 	msg_tx[PACKET_SIZE];

   int payload_size = 500;
   
   RunTimeOpts rtOpts;
   
   if(startup(argc,argv,&rtOpts)<0) 
     return -1;
     
   ///TODO: put it into function
   /// tx
   if(rtOpts.tx_do || rtOpts.tx_burst_do)
   {
     strcpy(bindaddr_tx.if_name, rtOpts.tx_if_name); //TODO
     bindaddr_tx.ethertype = rtOpts.ethertype; 	        // FEC's etherType, fecked
     memcpy(bindaddr_tx.mac, rtOpts.dst_mac, sizeof(mac_addr_t));
     socket_tx =  create_socket(&bindaddr_tx, &rtOpts);
     if(socket_tx == NULL)
     {
       printf("problem with socket_tx\n");
       return -1;
     }
     else
     {
       printf("created socket_tx: %d\n",socket_tx);
     }
   }
   /// rx
   if(rtOpts.rx_do || rtOpts.rx_burst_do)
   {
     strcpy(bindaddr_rx.if_name, rtOpts.rx_if_name); //TODO
     bindaddr_rx.ethertype = rtOpts.ethertype; 	        // FEC's etherType, fecked
     memcpy(bindaddr_rx.mac, rtOpts.dst_mac, sizeof(mac_addr_t));
     socket_rx = create_socket(&bindaddr_rx, &rtOpts);
     if(socket_rx  == NULL)
     {
       printf("problem with socket_rx\n");
       return -1;
     }
     {
       printf("created socket_rx: %d\n",socket_rx);
     }     
   }

   int cnt=1;

   
   if(rtOpts.tx_burst_do == 1)
       send_burst(socket_tx, 0, rtOpts.pkt_payload_size, &rtOpts);
   if(rtOpts.tx_burst_do == 2)
       send_fasters_burst(socket_tx, 0, rtOpts.pkt_payload_size, &rtOpts);

   if(rtOpts.tx_do)
   {
       printf("Single frame...\n");
       for(i=0;i<rtOpts.pkt_payload_size;i++)
       msg_tx[i] = i;
       send_frame(socket_tx, &msg_tx, rtOpts.pkt_payload_size, &rtOpts);
   }   
   
   if(rtOpts.rx_burst_do == 1)
     receive_burst(socket_rx, &rtOpts);
   if(rtOpts.rx_burst_do == 2)
   {
     printf("receive_fastest_burst\n");
     receive_fastest_burst(socket_rx, &rtOpts);
   }
   
   if(rtOpts.rx_do)
     receive_frames(socket_rx, &rtOpts);
     
 
  return 1;
}
