#include <LDNS.h>

#define RR_CLASSNAME_MAX_LEN 34

#ifdef USE_ITHREADS
#define RESOLVER_HASH_NAME "Zonemaster::LDNS::__resolvers__"
#define RR_HASH_NAME "Zonemaster::LDNS::__rrs__"
#define RRLIST_HASH_NAME "Zonemaster::LDNS::__rrlists__"
#define PACKET_HASH_NAME "Zonemaster::LDNS::__packets__"

void
net_ldns_forget()
{
    int i;

    const char *names[] = {
       RESOLVER_HASH_NAME,
       RR_HASH_NAME,
       RRLIST_HASH_NAME,
       PACKET_HASH_NAME,
       NULL
    };

    for(i=0; names[i]; i++)
    {
        HV *hash;
        HE *entry;

        hash = get_hv(names[i], GV_ADD);
        while ( (entry = hv_iternext(hash)) != NULL )
        {
            SV *val = hv_iterval(hash, entry);
            if(!SvOK(val))
            {
                SV *key = hv_iterkeysv(entry);
                hv_delete_ent(hash, key, G_DISCARD, 0);
            }
        }
    }
}

void
net_ldns_remember_resolver(SV *rv)
{
    net_ldns_remember(rv, RESOLVER_HASH_NAME);
}

void
net_ldns_remember_rr(SV *rv)
{
    net_ldns_remember(rv, RR_HASH_NAME);
}

void
net_ldns_remember_rrlist(SV *rv)
{
    net_ldns_remember(rv, RRLIST_HASH_NAME);
}

void
net_ldns_remember_packet(SV *rv)
{
    net_ldns_remember(rv, PACKET_HASH_NAME);
}

void
net_ldns_remember(SV *rv, const char *hashname)
{
    HV *hash;
    SV *val;
    STRLEN keylen;
    char *keystr;

    hash = get_hv(hashname, GV_ADD);
    val = newRV_inc(SvRV(rv));
    keystr = SvPV(val,keylen);
    sv_rvweaken(val);
    hv_store(hash, keystr, keylen, val, 0);
}

void
net_ldns_clone_resolvers()
{
    HV *hash;
    HE *entry;

    hash = get_hv(RESOLVER_HASH_NAME, GV_ADD);
    hv_iterinit(hash);
    while ( (entry = hv_iternext(hash)) != NULL )
    {
        SV *val = hv_iterval(hash, entry);
        if(SvOK(val))
        {
            ldns_resolver *old = INT2PTR(ldns_resolver *, SvIV((SV *)SvRV(val)));
            ldns_resolver *new = ldns_resolver_clone(old);
            sv_setiv_mg(SvRV(val), PTR2IV(new));
        }
        else
        {
            SV *key = hv_iterkeysv(entry);
            hv_delete_ent(hash, key, G_DISCARD, 0);
        }
    }
}

void
net_ldns_clone_rrs()
{
    HV *hash;
    HE *entry;

    hash = get_hv(RR_HASH_NAME, GV_ADD);
    hv_iterinit(hash);
    while ( (entry = hv_iternext(hash)) != NULL )
    {
        SV *val = hv_iterval(hash, entry);
        SV *key = hv_iterkeysv(entry);
        if(SvOK(val))
        {
            ldns_rr *old = INT2PTR(ldns_rr *, SvIV((SV *)SvRV(val)));
            ldns_rr *new = ldns_rr_clone(old);
            sv_setiv_mg(SvRV(val), PTR2IV(new));
        }
        else
        {
            hv_delete_ent(hash, key, G_DISCARD, 0);
        }
    }
}

void
net_ldns_clone_rrlists()
{
    HV *hash;
    HE *entry;

    hash = get_hv(RRLIST_HASH_NAME, GV_ADD);
    hv_iterinit(hash);
    while ( (entry = hv_iternext(hash)) != NULL )
    {
        SV *val = hv_iterval(hash, entry);
        if(SvOK(val))
        {
            ldns_rr_list *old = INT2PTR(ldns_rr_list *, SvIV((SV *)SvRV(val)));
            ldns_rr_list *new = ldns_rr_list_clone(old);
            sv_setiv_mg(SvRV(val), PTR2IV(new));
        }
        else
        {
            SV *key = hv_iterkeysv(entry);
            hv_delete_ent(hash, key, G_DISCARD, 0);
        }
    }
}

void
net_ldns_clone_packets()
{
    HV *hash;
    HE *entry;

    hash = get_hv(PACKET_HASH_NAME, GV_ADD);
    hv_iterinit(hash);
    while ( (entry = hv_iternext(hash)) != NULL )
    {
        SV *val = hv_iterval(hash, entry);
        if(SvOK(val))
        {
            ldns_pkt *old = INT2PTR(ldns_pkt *, SvIV((SV *)SvRV(val)));
            ldns_pkt *new = ldns_pkt_clone(old);
            sv_setiv_mg(SvRV(val), PTR2IV(new));
        }
        else
        {
            SV *key = hv_iterkeysv(entry);
            hv_delete_ent(hash, key, G_DISCARD, 0);
        }
    }
}

#endif

char *
randomize_capitalization(char *in)
{
#ifdef RANDOMIZE
    char *str;
    str = in;
    while(*str) {
        if(Drand01() < 0.5)
        {
            *str = tolower(*str);
        }
        else
        {
            *str = toupper(*str);
        }
        str++;
    }
#endif
    return in;
}

SV *
rr2sv(ldns_rr *rr)
{
    char rrclass[RR_CLASSNAME_MAX_LEN];
    char *type;

    type = ldns_rr_type2str(ldns_rr_get_type(rr));
    snprintf(rrclass, RR_CLASSNAME_MAX_LEN, "Zonemaster::LDNS::RR::%s", type);

    SV* rr_sv = newSV(0);
    if (strncmp(type, "TYPE", 4)==0)
    {
        sv_setref_pv(rr_sv, "Zonemaster::LDNS::RR", rr);
    }
    else
    {
        sv_setref_pv(rr_sv, rrclass, rr);
    }

    free(type);

#ifdef USE_ITHREADS
    net_ldns_remember_rr(rr_sv);
#endif

    return rr_sv;
}