#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ip_to_geo.h"

#include "ip_data.c"

static const uint8_t* ip_to_geo_rec(uint32_t ipv4, unsigned level, const ip_level* root)
{
    unsigned cur_average;
    const ip_level* cur_ip = root;
    unsigned cur_addr = (ipv4 >> (level*8)) & 0xFF;

    // Optimistic search
    if (cur_addr && level != 2)
    {
        cur_average = cur_addr >> root->average;

        while (cur_average-- && cur_ip->next)
            cur_ip = cur_ip->next;
    }
    
#define IP_TEST                                                         \
    do {                                                                \
        if (cur_addr >= cur_ip->start && cur_addr <= cur_ip->end)       \
        {                                                               \
            if (cur_ip->childs)                                         \
                return ip_to_geo_rec(ipv4, level-1, cur_ip->childs);    \
            else                                                        \
                return &cur_ip->code;                                   \
        }                                                               \
    } while (0)
    
    if (cur_addr < cur_ip->start)
    {
        for (cur_ip = cur_ip->prev; cur_ip; cur_ip = cur_ip->prev)
            IP_TEST;
    }
    else if (cur_addr > cur_ip->end)
    {
        for (cur_ip = cur_ip->next; cur_ip; cur_ip = cur_ip->next)
            IP_TEST;
    }
    else
        IP_TEST;

    return NULL;
}


const uint8_t* ip_to_geo(uint32_t ipv4)
{
    const ip_level* first_level = s_root_ip[ipv4 >> 24];

    if (!first_level) return NULL;

    return ip_to_geo_rec(ipv4, 2, first_level);
}

const uint8_t* get_country_code(const uint8_t* idx)
{
    if (!idx || *idx >= sizeof(country_codes)/sizeof(country_codes[0]))
        return NULL;

    return country_codes[*idx];
}

/* int strip_to_int(char* strip_, uint32_t* ip) */
/* { */
/*     char* saveptr = NULL; */
/*     char* cur; */
/*     int i; */
/*     char* strip = strdup(strip_); */
    
/*     *ip = 0; */
    
/*     for(i=3; i>=0; i--) */
/*     { */
/*         cur = strtok_r(strip, ".", &saveptr); */
/*         if (!cur) goto end; */
/*         *ip += atoi(cur) << (8*i); */
/*         strip = NULL; */
/*     } */

/*     cur = strtok_r(strip, ".", &saveptr); */
/* end: */
/*     free(strip); */
/*     return (cur)?-1:0; */
/* } */

int interactive(struct gengetopt_args_info* params)
{
    const uint8_t* cc;
    int ret;

    ret = inet_addr(params->ip_arg);
    if (ret == INADDR_NONE)
    {
        if (!params->quiet_flag)
            fprintf(stderr, "Invalid IP %s\n", params->ip_arg);
        return -1;
    }

    cc = ip_to_geo((uint32_t)ret);

    if (params->quiet_flag)
        printf("%s\n", (cc)?(char*)get_country_code(cc):"<none>");
    else
        printf("IP %s : %s\n", params->ip_arg, (cc)?(char*)get_country_code(cc):"<none>");
    
    return 0;
}

int main(int argc, char** argv)
{
    int ret;
    struct gengetopt_args_info params;
    
    ret = cmdline_parser (argc, argv, &params);

    if (ret) return ret;

    //self_test();

    if (params.ip_given)
        return interactive(&params);
    else if (params.daemon_flag)
        return daemonize(&params);
    else
        cmdline_parser_print_help();

    return 0;
}
