#include <string.h>
#include <errno.h>
#include <getdns/getdns.h>
#include <getdns/getdns_ext_libevent.h>

static int debug = 0;

static getdns_return_t
dump_reply(FILE *fp, getdns_dict *reply, const char *section_name)
{
	getdns_list *section = NULL;
	size_t section_len = -1;
	getdns_return_t r;
	uint8_t *res = NULL;
	size_t res_len;

	r = getdns_dict_get_list(reply, section_name, &section);
	if (r) {
		fprintf(stderr,
			"unable to get section \"%s\" from reply\n",
			section_name);
		return r;
	}

	r = getdns_list_get_length(section, &section_len);
	if (r) {
		fprintf(stderr, "unable to get length of section\n");
		return r;
	}

	for (size_t j = 0; j < section_len; j++) {
		getdns_dict *rr = NULL;

		r = getdns_list_get_dict(section, j , &rr);
		if (r) {
			fprintf(stderr, "unable to get rr from entry "
				"%d: %d\n", j, r);
			return r;
		}

		r = getdns_rr_dict2wire(rr, &res, &res_len);
		if (r) {
			fprintf(stderr,
				"unable to convert entry %d "
				"to wire format: %d\n", j, r);
			return r;
		}

		if (0 && debug) {
			char *s = getdns_pretty_print_dict(rr);
			puts(s);
			free(s);
		}

		if (fwrite(res, 1, res_len, fp) != res_len) {
			fprintf(stderr,
				"unable to write buffer to file: %s\n",
				strerror(errno));
			return -errno;
		}

		free(res);
	}

	return 0;
}

int
dump_tree(FILE *fp, const getdns_dict *response, const char *tree_name, const char *section_name)
{
	getdns_return_t r;
	getdns_list *tree = NULL;
	size_t n_replies = -1;

        r = getdns_dict_get_list(response, tree_name, &tree);
	if (r) {
          fprintf(stderr, "unable to get tree %s\n", tree_name);
          return r;
	}

        if (section_name == NULL) {
          size_t tree_len = 0;
          r = getdns_list_get_length(tree, &tree_len);
          if (r) {
            fprintf(stderr, "unable to get tree length\n");
            return r;
          }
          for (size_t i = 0; i < tree_len; i++) {
            getdns_dict *rr = NULL;
            r = getdns_list_get_dict(tree, i , &rr);
            if (r) {
              fprintf(stderr, "unable to get rr from entry %d: %d\n", i, r);
              return r;
            }
            uint8_t *res = NULL;
            size_t res_len = 0;
            r = getdns_rr_dict2wire(rr, &res, &res_len);
            if (r) {
              fprintf(stderr,
                      "unable to convert entry %d "
                      "to wire format: %d\n", i, r);
              return r;
            }
            if (fwrite(res, 1, res_len, fp) != res_len) {
              fprintf(stderr,
                      "unable to write buffer to file: %s\n",
                      strerror(errno));
              return errno;
            }
            free(res);
          }
        } else {
          r = getdns_list_get_length(tree, &n_replies);
          if (r) {
            fprintf(stderr, "unable to get number of replies\n");
            return r;
          }

          for (size_t i = 0; i < n_replies; i++) {
            getdns_dict *reply = NULL;

            r = getdns_list_get_dict(tree, i, &reply);
            if (r) {
              fprintf(stderr, "unable to get reply %d from tree\n", i);
              return r;
            }

            if (debug) {
              char *s = getdns_pretty_print_dict(reply);
              printf("Pretty-printing reply #%d:%s\n", i, s);
              free(s);
            }

            dump_reply(fp, reply, section_name);
          }
        }

	return 0;
}

#define CHUNKSIZE 4096

size_t
read_buffer(FILE *infp, uint8_t **bufp_out, size_t size_hint)
{
  size_t nread = 0;
  uint8_t *wirebuf = NULL;
  size_t chunksize = CHUNKSIZE;
  int chunks = 1;

  if (size_hint > 0)
    chunksize = size_hint;
  wirebuf = malloc(chunksize);

  if (wirebuf == NULL)
    goto out;

  while (1)
    {
      size_t n = fread(wirebuf + nread, 1, chunksize, infp);
      nread += n;
      if (n < chunksize)
        break;                  /* Done. */

      wirebuf = realloc(wirebuf, ++chunks * chunksize);
      if (wirebuf == NULL)
        break;
    }

 out:
  if (bufp_out != NULL)
    *bufp_out = wirebuf;
  return nread;
}

getdns_return_t
wire_rrs2list(const uint8_t *buf, size_t buf_len, getdns_list **list_out)
{
  getdns_return_t r = GETDNS_RETURN_GOOD;
  getdns_list *list = getdns_list_create();
  getdns_dict *dict = NULL;
  size_t rr_count = 0;

  if (list == NULL)
    return GETDNS_RETURN_MEMORY_ERROR;
  while (buf_len > 0)
    {
      r = getdns_wire2rr_dict_scan(&buf, &buf_len, &dict);
      if (r)
        break;
      r = getdns_list_set_dict(list, rr_count, dict);
      getdns_dict_destroy(dict); /* The list has a copy. */
      if (r)
        break;
      rr_count++;
    }

  if (list_out)
    *list_out = list;
  return r;
}