/**
 *
 * /brief getdns core functions for synchronous use
 *
 * Originally taken from the getdns API description pseudo implementation.
 *
 */

/*
 * Copyright (c) 2013, NLnet Labs, Verisign, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the names of the copyright holders nor the
 *   names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <string.h>
#include <unbound.h>
#include "getdns/getdns.h"
#include "config.h"
#include "context.h"
#include "general.h"
#include "types-internal.h"
#include "util-internal.h"
#include "dnssec.h"

#include "stub.h"
#include "gldns/wire2str.h"

typedef struct getdns_sync_loop {
	getdns_mini_event      loop;
	getdns_eventloop_event ub_event;
	getdns_context        *context;
	int                    to_run;
	getdns_dict           *response;
} getdns_sync_loop;

static getdns_return_t
getdns_sync_loop_init(getdns_context *context, getdns_sync_loop *loop)
{
	getdns_return_t r;
	getdns_eventloop *ext = &loop->loop.loop;

	loop->response = NULL;
	loop->to_run   = 1;
	loop->context  = context;

	if ((r = getdns_mini_event_init(context, &loop->loop)))
		return r;

	loop->ub_event.userarg    = loop->context;
	loop->ub_event.read_cb    = priv_getdns_context_ub_read_cb;
	loop->ub_event.write_cb   = NULL;
	loop->ub_event.timeout_cb = NULL;
	loop->ub_event.ev         = NULL;

	return ext->vmt->schedule(ext, ub_fd(context->unbound_ctx),
	    TIMEOUT_FOREVER, &loop->ub_event);
}

static void
getdns_sync_loop_cleanup(getdns_sync_loop *loop)
{
	getdns_eventloop *ext = &loop->loop.loop;

	ext->vmt->clear(ext, &loop->ub_event);
	ext->vmt->cleanup(ext);
}

static void
getdns_sync_loop_run(getdns_sync_loop *loop)
{
	getdns_eventloop *ext = &loop->loop.loop;

	while (loop->to_run)
		ext->vmt->run_once(ext, 1);
	
	getdns_sync_loop_cleanup(loop);
}

static void
getdns_sync_cb(getdns_context *context, getdns_callback_type_t callback_type,
    getdns_dict *response, void *userarg, getdns_transaction_t transaction_id)
{
	getdns_sync_loop *loop = (getdns_sync_loop *)userarg;

	assert(loop);

	loop->response = response;
	loop->to_run = 0;
}

getdns_return_t
getdns_general_sync(getdns_context *context, const char *name,
    uint16_t request_type, getdns_dict *extensions, getdns_dict **response)
{
	getdns_sync_loop loop;
	getdns_return_t r;

	if (!context || !name || !response)
		return GETDNS_RETURN_INVALID_PARAMETER;

	if ((r = getdns_sync_loop_init(context, &loop)))
		return r;

	if ((r = priv_getdns_general_loop(context, &loop.loop.loop, name,
	    request_type, extensions, &loop, NULL, getdns_sync_cb, NULL))) {

		getdns_sync_loop_cleanup(&loop);
		return r;
	}
	getdns_sync_loop_run(&loop);
	
	return (*response = loop.response) ?
	    GETDNS_RETURN_GOOD : GETDNS_RETURN_GENERIC_ERROR;
}

getdns_return_t
getdns_address_sync(getdns_context *context, const char *name,
    getdns_dict *extensions, getdns_dict **response)
{
	getdns_sync_loop loop;
	getdns_return_t r;

	if (!context || !name || !response)
		return GETDNS_RETURN_INVALID_PARAMETER;

	if ((r = getdns_sync_loop_init(context, &loop)))
		return r;

	if ((r = priv_getdns_address_loop(context, &loop.loop.loop, name,
	    extensions, &loop, NULL, getdns_sync_cb))) {

		getdns_sync_loop_cleanup(&loop);
		return r;
	}
	getdns_sync_loop_run(&loop);
	
	return (*response = loop.response) ?
	    GETDNS_RETURN_GOOD : GETDNS_RETURN_GENERIC_ERROR;
}

getdns_return_t
getdns_hostname_sync(getdns_context *context, getdns_dict *address,
    getdns_dict *extensions, getdns_dict **response)
{
	getdns_sync_loop loop;
	getdns_return_t r;

	if (!context || !address || !response)
		return GETDNS_RETURN_INVALID_PARAMETER;

	if ((r = getdns_sync_loop_init(context, &loop)))
		return r;

	if ((r = priv_getdns_hostname_loop(context, &loop.loop.loop, address,
	    extensions, &loop, NULL, getdns_sync_cb))) {

		getdns_sync_loop_cleanup(&loop);
		return r;
	}
	getdns_sync_loop_run(&loop);
	
	return (*response = loop.response) ?
	    GETDNS_RETURN_GOOD : GETDNS_RETURN_GENERIC_ERROR;
}

getdns_return_t
getdns_service_sync(getdns_context *context, const char *name,
    getdns_dict *extensions, getdns_dict **response)
{
	getdns_sync_loop loop;
	getdns_return_t r;

	if (!context || !name || !response)
		return GETDNS_RETURN_INVALID_PARAMETER;

	if ((r = getdns_sync_loop_init(context, &loop)))
		return r;

	if ((r = priv_getdns_service_loop(context, &loop.loop.loop, name,
	    extensions, &loop, NULL, getdns_sync_cb))) {

		getdns_sync_loop_cleanup(&loop);
		return r;
	}
	getdns_sync_loop_run(&loop);
	
	return (*response = loop.response) ?
	    GETDNS_RETURN_GOOD : GETDNS_RETURN_GENERIC_ERROR;
}

void
getdns_free_sync_request_memory(struct getdns_dict *response)
{
	getdns_dict_destroy(response);
}

/* getdns_core_sync.c */
