#include "stdafx.h"
#include "service_main.h"

static ACL_AIO *__aio = NULL;
static ACL_HTABLE *__cache = NULL;

void ns_cache_init(ACL_AIO *aio)
{
	const char *myname = "ns_cache_init";

	if (__cache != NULL)
		acl_msg_fatal("%s(%d): __cache != NULL", myname, __LINE__);

	__aio = aio;
	__cache = acl_htable_create(10000, 0);
}

static void ns_cache_timeout_fn(int event_type acl_unused,
	ACL_EVENT *event acl_unused, void *context)
{
	NS_CACHE_ENTRY *entry = (NS_CACHE_ENTRY*) context;

	acl_htable_delete(__cache, entry->domain, NULL);
	ns_cache_entry_free(entry);
}

void ns_cache_add(NS_CACHE_ENTRY *entry)
{
	const char *myname = "ns_cache_add";
	NS_CACHE_ENTRY *old_entry;

	if (__cache == NULL)
		acl_msg_fatal("%s(%d): ns_cache_init not called",
			myname, __LINE__);

	old_entry = (NS_CACHE_ENTRY*) acl_htable_find(__cache, entry->domain);
	if (old_entry == NULL) {
		acl_htable_enter(__cache, entry->domain, (char*) entry);
		if (entry->ttl > 0)
			acl_aio_request_timer(__aio, ns_cache_timeout_fn,
				entry, entry->ttl, 0);
		return;
	}

	if (entry->ttl > 0) {
		acl_aio_cancel_timer(__aio, ns_cache_timeout_fn, old_entry);
		acl_aio_request_timer(__aio, ns_cache_timeout_fn,
			entry, entry->ttl, 0);
	}
	if (entry == old_entry)
		return;
	acl_htable_delete(__cache, old_entry->domain, NULL);
	ns_cache_entry_free(old_entry);
	acl_htable_enter(__cache, entry->domain, (char*) entry);
}

NS_CACHE_ENTRY *ns_cache_find(const char *domain)
{
	const char *myname = "ns_cache_find";
	NS_CACHE_ENTRY *entry;
	char  key[MAX_DOMAIN_LEN];

	if (__cache == NULL)
		acl_msg_fatal("%s(%d): ns_cache_init not called",
			myname, __LINE__);

	ACL_SAFE_STRNCPY(key, domain, sizeof(key));
	acl_lowercase(key);

	entry = (NS_CACHE_ENTRY*) acl_htable_find(__cache, key);
	return (entry);
}

int ns_cache_delete(const char *domain)
{
	const char *myname = "ns_cache_delete";
	char  key[MAX_DOMAIN_LEN];

	if (__cache == NULL)
		acl_msg_fatal("%s(%d): ns_cache_init not called",
			myname, __LINE__);

	ACL_SAFE_STRNCPY(key, domain, sizeof(key));
	acl_lowercase(key);

	return (acl_htable_delete(__cache, key,
			(void (*)(void*))ns_cache_entry_free));
}

void ns_cache_entry_free(NS_CACHE_ENTRY *entry)
{
	if (entry->ip_list)
		acl_argv_free(entry->ip_list);
	acl_myfree(entry);
}

NS_CACHE_ENTRY *ns_cache_entry_new(const char *domain, const ACL_ARGV *ip_list,
	const char *cache_buf, int dlen, int ttl)
{
	const char *myname = "ns_cache_entry_new";
	NS_CACHE_ENTRY *entry;
	int   i;

	if (ip_list == NULL && (cache_buf == NULL || dlen <= 0)) {
		acl_msg_error("%s(%d): invalid input args", myname, __LINE__);
		return (NULL);
	}
	if (ip_list != NULL && (cache_buf != NULL && dlen > 0)) {
		acl_msg_error("%s(%d): ip_list != NULL and cache_buf != NULL",
			myname, __LINE__);
		return (NULL);
	}

	entry = (NS_CACHE_ENTRY*) acl_mycalloc(1, sizeof(NS_CACHE_ENTRY));
	entry->idx = 0;
	entry->ttl = ttl;
	ACL_SAFE_STRNCPY(entry->domain, domain, sizeof(entry->domain));
	acl_lowercase(entry->domain);

	if (ip_list) {
		entry->ip_list = acl_argv_alloc(10);
		for (i = 0; i < ip_list->argc; i++) {
			acl_argv_add(entry->ip_list, ip_list->argv[i], NULL);
		}
		entry->cache_dlen = 0;
	} else if (cache_buf) {
		memcpy(entry->cache_buf, cache_buf, dlen);
		entry->cache_dlen = dlen;
	}

	return (entry);
}

static char __unknown_domain[MAX_DOMAIN_LEN];
static NS_CACHE_ENTRY *__unknown_cache_entry;

void ns_cache_add_unknown(const char *domain, const ACL_ARGV *ip_list)
{
	__unknown_cache_entry = ns_cache_entry_new(domain, ip_list, NULL, 0, 0);
	ACL_SAFE_STRNCPY(__unknown_domain, domain, sizeof(__unknown_domain));
}

NS_CACHE_ENTRY *ns_cache_unknown(void)
{
	const char *myname = "ns_cache_unknown";

	if (__unknown_cache_entry == NULL)
		acl_msg_fatal("%s(%d): __unknown_cache_entry null",
			myname, __LINE__);
	return (__unknown_cache_entry);
}

ACL_ARGV *ns_cache_ip_list(NS_CACHE_ENTRY *cache_entry)
{
	ACL_ARGV *argv = acl_argv_alloc(10);
	ACL_ARGV *ip_list = cache_entry->ip_list;
	int   i, n;

	n = ip_list->argc;
	if (cache_entry->idx >= n)
		cache_entry->idx = 0;
	i = cache_entry->idx++;
	while (n-- > 0) {
		acl_argv_add(argv, ip_list->argv[i++], NULL);
		if (i >= ip_list->argc)
			i = 0;
	}

	return (argv);
}
