#include <stdio.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "common.h"
#include <unistd.h>
#include <errno.h>

static void clean_obj(struct bpf_object *obj){
    printf("Cleaning\n");
    bpf_object__close(obj);
}   
int main(void){
    const char *fileObj = "tp_tcp.o";
    struct bpf_object *obj;
    struct bpf_program *program;
    struct bpf_map *map;
    struct reset s_reset;
    int err;
    int map_fd;
    int map_fd_filter_family;
    int map_fd_filter_sport;
    int map_fd_index;
    int keys = 0;
    int indexPackets = 0;
    int index = 0;

    obj = bpf_object__open_file(fileObj, NULL);
    if (!obj){
        printf("Failed to open %s\n", fileObj);
        return -1;
    }
    //LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE);
    //map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(struct reset), 4096, BPF_ANY);
    //map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(struct reset), 4096, BPF_ANY);

    err = bpf_object__load(obj);
    if (err){
        printf("%s\n", strerror(errno));
        printf("Failed to load object\n");
        clean_obj(obj);
        return -1;
    }

    program = bpf_object__find_program_by_name(obj, "tcp_retransmit");
    if (!program){
        printf("Failed to find the program\n");
        clean_obj(obj);
        return -1;
    }

    map = bpf_object__find_map_by_name(obj, "tcp_reset_stats");
    if (!map){
        printf("Failed to get the map\n");
        clean_obj(obj);
        return -1;
    }

    map_fd = bpf_object__find_map_fd_by_name(obj, "tcp_reset_stats");
    if (map_fd < 0){
        printf("Failed to find the map 'tcp_reset_stats'\n");
        clean_obj(obj);
        return -1;
    }

    map_fd_filter_family = bpf_object__find_map_fd_by_name(obj, "filter_family");
    if (map_fd_filter_family < 0){
        printf("Failed to find the map 'filter_family'\n");
        clean_obj(obj);
        return -1;
    }

    map_fd_index = bpf_object__find_map_fd_by_name(obj, "tcp_stats_index");
    if (map_fd_index < 0){
        printf("Failed to find the map 'tcp_stats_index'\n");
        clean_obj(obj);
        return -1;
    }

    struct bpf_link *link = bpf_program__attach(program);
    if (!link){
        printf("Failed to attach the program\n");
        return -1;
    }

    // Sepcify our filters
    /*
     * IPv4: AF_INET  -> 2
     * IPv6: AF_INET6 -> 10
     */
    __s16 f = AF_INET;
    err = bpf_map_update_elem(map_fd_filter_family, &keys, &f, BPF_ANY);


    printf("Waiting for new packets\n");    
    while(1){
        // Get the index
        // and we compare with the local variable
        // If it's different, we get the new variable
        err = bpf_map_lookup_elem(map_fd_index, &keys, &indexPackets);

        // We have a new packet
        if (indexPackets > index){
            index = indexPackets;
            printf("Index: %d %d\n", index, indexPackets);    
            err = bpf_map_lookup_elem(map_fd, &keys, &s_reset);
            if (err == 0){
                struct in_addr *src = (struct in_addr*)&s_reset.saddr;
                struct in_addr *dest = (struct in_addr*)&s_reset.daddr;
                char *s = inet_ntoa(*src);
                char tmp[35];
                memcpy(tmp, s, 35);
                char *d = inet_ntoa(*dest);

                printf("Sport: %d; dport: %d %d %d %s - %s\n", s_reset.sport, s_reset.dport, s_reset.family, s_reset.proto, tmp, d);

            }
            memset(&s_reset, 0, sizeof(struct reset));
            //sleep(1);
        }
    }

    return 0;
}