#ifndef _CECILIE_H_
#define _CECILIE_H_

#include "cecilie.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <malloc.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <ifaddrs.h>



struct cecilie_rdma_app_reg_t cecilie_registers[] = {
	{REG_DST_IP,		"DST_IP_REG",		"Destination IP of RoCE packet"},
	{REG_DST_MAC_HIGH,	"DST_MAC_HIGH",		"Destination MAC of RoCE packet - higher 32 bits"},
	{REG_DST_MAC_LOW,	"DST_MAC_LOW",		"Destination MAC of RoCE packet - lower 32 bits"},
	{REG_DST_UDP,		"DST_UDP",		"Destination UDP port of RoCE packet"},
	{REG_DST_QP,		"DST_QP",		"Destination QP number of RoCE packet"},
	{REG_FIRST_PSN,		"FIRST_PSN",		"Sequence number of first RoCE packet"},
	{REG_Q_KEY,		"Q_KEY",		"Q Key of RoCE packet"},
	{REG_CONTROL,		"CONTROL",		"Control register (0 bit = run/stop; 1 bit = clear)"},
	{REG_STATUS,		"STATUS",		"Status regiseter (0 bit = overflow)"},
	{REG_WORD_CNT_HIGH, 	"WORD_CNT_HIGH",	"Counter counting already sent 64 byte words - higher 32 bits"},
	{REG_WORD_CNT_LOW,	"WORD_CNT_LOW",		"Counter counting already sent 64 byte words - lower 32 bits"},
	{REG_ID,		"ID",			"Identification register = 0xDFC0CEC0"}
};



void print_register_description()
{
	int num_of_regs = sizeof(cecilie_registers) / sizeof(struct cecilie_rdma_app_reg_t);

	printf("Description of Cecilie registers:\n");

	for (int i = 0; i < num_of_regs; i++)
		printf("%-20s" "0x%08x\t" "%-20s\n", cecilie_registers[i].name, cecilie_registers[i].reg_addr, cecilie_registers[i].description);
}

int insert_cecilie_to_list(cecilie_t *cecilie, cecilie_t **list)
{
	cecilie_t *plist = NULL;

	if(list == NULL){
		return 1;
	}
	
	plist = *list;

	if(plist == NULL){
		*list = cecilie;
	}else{
		while(plist->next){
			plist = plist->next;
		} 
		plist->next = cecilie;
	}	

	return 0;
}

cecilie_t * get_cecilie_from_list(cecilie_t *list, int num)
{
	cecilie_t *dev = list;
	int cnt = 0;

	while(dev){
		if(cnt == num)
			break;
		dev = dev->next;
		cnt++;
	}
	
	return dev;
}

void clear_cecilie_list(cecilie_t **list)
{
	cecilie_t *plist, *ptr = NULL;

	if(list == NULL){
		return;
	}

	plist = *list;
	*list = NULL;

	while(plist){
		ptr = plist->next;
		free(plist);
		plist = NULL;
		plist = ptr;
	}
}

void print_cecilie(cecilie_t *dev)
{
	if(dev == NULL)
	{
		printf("Device is empty\n");
		return;
	}

	printf("Cecilie:\t");
	printf("%s\t", dev->adapter_name);
	printf("%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\t",
		dev->net.mac_addr[5], dev->net.mac_addr[4], dev->net.mac_addr[3],
		dev->net.mac_addr[2], dev->net.mac_addr[1], dev->net.mac_addr[0]);
	printf("%s\t\t", inet_ntoa(dev->net.ip_addr));
	printf("%20.s\n", inet_ntoa(dev->net.net_mask));
}

void print_cecilie_list(cecilie_t *list)
{
	int cnt = 0;

	printf("Cecilie board list:\n");

	if(list == NULL)
	{
		printf("\tList is empty - no board found\n");
	}

	while(list){
		printf("\tCecilie %d:\t", cnt);
		printf("%s\t", list->adapter_name);
		printf("%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\t",
			list->net.mac_addr[5], list->net.mac_addr[4], list->net.mac_addr[3],
			list->net.mac_addr[2], list->net.mac_addr[1], list->net.mac_addr[0]);
		printf("%s\t\t", inet_ntoa(list->net.ip_addr));
		printf("%20.s\n", inet_ntoa(list->net.net_mask));
		list = list->next;
		cnt++;
	}
}

int get_mac_ip(char *if_name, unsigned char *mac, struct in_addr *ip)
{
        int s;
        struct ifreq ifreq_buf;
        struct sockaddr_in *addr;

	if(if_name == NULL)
		return 1;

        /* open socket and copy adapter name */
        s = socket(PF_INET, SOCK_DGRAM, 0);
        memset(&ifreq_buf, 0x00, sizeof(ifreq_buf));
        strcpy(ifreq_buf.ifr_name, if_name);

	if(mac != NULL) {
        /* Get MAC */
        ioctl(s, SIOCGIFHWADDR, &ifreq_buf);
        memcpy(mac, ifreq_buf.ifr_hwaddr.sa_data, 6);
	}

	if(ip != NULL) {
        	/* Get IP */
        	ioctl(s, SIOCGIFADDR, &ifreq_buf);
        	addr = (struct sockaddr_in *)&ifreq_buf.ifr_addr;
        	memcpy(ip, &addr->sin_addr, sizeof(struct in_addr));
	}
        /* close socket */
        close(s);

        return 0;        
}

int get_adapter_names(char ***if_names)
{
        struct ifaddrs *addrs,*tmp;
        getifaddrs(&addrs);
        tmp = addrs;
	char **if_name_list = NULL;
        int cnt = 0;
        int if_cnt = 0;

	if(if_names == NULL)
		return 0;

        /* Get number of valid iterfaces */
        while (tmp)
        {
                if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET && strcmp(tmp->ifa_name, "lo") != 0)
                {
                        cnt++;
                }
            tmp = tmp->ifa_next;
        }

        tmp = addrs;

        /* Allocate buffer for interface names */
        if_name_list = malloc(cnt*sizeof(char *));
        if(if_name_list == NULL)
                return 1;

        if_cnt = cnt;
        cnt = 0;

        /* Alocate buffer for each interface name and copy it */
        while (tmp)
        {
                if (cnt < if_cnt && tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET && strcmp(tmp->ifa_name, "lo") != 0)
                {
                        if_name_list[cnt] = malloc(100*sizeof(char));
                        if(if_name_list[cnt] == NULL)
                        {
                                for(int i = 0; i < cnt; i++)
                                        free(if_name_list[i]);
                                free(if_name_list);
                                return 0;
                        }
                        sprintf(if_name_list[cnt], "%.99s",tmp->ifa_name);
                        cnt++;
                }
            tmp = tmp->ifa_next;
        }

        freeifaddrs(addrs);

        if(cnt != if_cnt)
        {
                for(int i = 0; i < cnt; i++)
                        free(if_name_list[i]);
                free(if_name_list);
                return 0;
        }

	*if_names = if_name_list;
        return cnt;
}




#endif
