#include "server.h"

static FILE *urandom;

AVL_TREE(rings, udebug_id_cmp, true, NULL);

struct client_ring *client_ring_get_by_id(struct client *cl, uint32_t id)
{
	struct client_ring *r;

	list_for_each_entry(r, &cl->bufs, list)
		if (r->id == id)
			return r;

	return NULL;
}

static uint32_t gen_ring_id(void)
{
	uint32_t val = 0;

	if (!urandom && (urandom = fopen("/dev/urandom", "r")) == NULL)
		return 0;

	if (fread(&val, sizeof(val), 1, urandom) != 1)
		return 0;

	return val;
}

struct client_ring *client_ring_alloc(struct client *cl)
{
	enum {
		RING_ATTR_NAME,
		RING_ATTR_FLAGS,
		__RING_ATTR_MAX,
	};
	static const struct blobmsg_policy policy[__RING_ATTR_MAX] = {
		[RING_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
		[RING_ATTR_FLAGS] = { "flags", BLOBMSG_TYPE_ARRAY },
	};
	struct udebug_client_msg *msg = &cl->rx_buf.msg;
	struct blob_attr *tb[__RING_ATTR_MAX], *meta;
	struct client_ring *r;
	size_t meta_len;

	if (cl->rx_fd < 0) {
		DC(2, cl, "missing file descriptor");
		return NULL;
	}

	meta_len = blob_pad_len(&cl->rx_buf.data);
	r = calloc_a(sizeof(*r), &meta, meta_len);
	memcpy(meta, cl->rx_buf.buf, meta_len);

	blobmsg_parse_attr(policy, __RING_ATTR_MAX, tb, meta);
	if (!tb[RING_ATTR_NAME]) {
		DC(2, cl, "missing ring name");
		close(cl->rx_fd);
		free(r);
		return NULL;
	}

	r->name = blobmsg_get_string(tb[RING_ATTR_NAME]);
	r->flags = tb[RING_ATTR_FLAGS];

	r->cl = cl;
	r->id = msg->id;
	r->fd = cl->rx_fd;
	cl->rx_fd = -1;
	r->ring_size = msg->ring_size;
	r->data_size = msg->data_size;
	list_add_tail(&r->list, &cl->bufs);

	r->node.key = (void *)(uintptr_t)gen_ring_id();
	avl_insert(&rings, &r->node);
	udebug_ubus_ring_notify(r, true);
	DC(2, cl, "add ring %d [%x] ring_size=%x data_size=%x", r->id, ring_id(r), r->ring_size, r->data_size);

	return r;
}

void client_ring_free(struct client_ring *r)
{
	DC(2, r->cl, "free ring %d [%x]", r->id, ring_id(r));
	udebug_ubus_ring_notify(r, false);
	avl_delete(&rings, &r->node);
	list_del(&r->list);
	close(r->fd);
	free(r);
}
