
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdbool.h>

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <json-c/json.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>

#include <easy/easy.h>

#include "map_module.h"


const char *objname;

#define map_plugin	IEEE1905_OBJECT_MULTIAP

struct mapclient_private {
	struct ubus_context *ctx;
	uint32_t oid;
	uint32_t map_oid;
	struct ubus_subscriber sub;
};


static int mapclient_sub_cb(struct ubus_context *ctx, struct ubus_object *obj,
			    struct ubus_request_data *req, const char *type,
			    struct blob_attr *msg)
{
	char *str;

	str = blobmsg_format_json(msg, true);
	fprintf(stderr, "Received notification '%s': %s\n", type, str);
	free(str);

	return 0;
}

static void mapclient_sub_remove_cb(struct ubus_context *ctx,
				    struct ubus_subscriber *sub,
				    uint32_t obj)
{
	fprintf(stderr, "Object 0x%x no longer present\n", obj);
}

static int mapclient_subscribe(struct mapclient_private *priv, uint32_t oid)
{
	int ret;

	/* register mapclient as a subscriber with ubus */
	priv->sub.cb = mapclient_sub_cb;
	priv->sub.remove_cb = mapclient_sub_remove_cb;
	ret = ubus_register_subscriber(priv->ctx, &priv->sub);
	if (ret)
		fprintf(stderr, "Failed to register sub: %s\n", ubus_strerror(ret));


	/* now subscribe to events from map plugin over passed oid */
	ret = ubus_subscribe(priv->ctx, &priv->sub, oid);
	if (ret)
		fprintf(stderr, "Failed to subscribe: %s\n", ubus_strerror(ret));

	return 0;
}

static void register_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
	struct mapclient_private *priv = (struct mapclient_private *)req->priv;
	const struct blobmsg_policy pol[1] = {
		[0] = { .name = "oid", .type = BLOBMSG_TYPE_INT32 },
	};
	struct blob_attr *tb[1];


	blobmsg_parse(pol, 1, tb, blob_data(msg), blob_len(msg));

	if (tb[0]) {
		uint32_t oid = blobmsg_get_u32(tb[0]);

		fprintf(stderr, "Response ID: %u\n", oid);
		mapclient_subscribe(priv, oid);
	}
}

static void mapclient_subscribe_for_cmdus(struct mapclient_private *priv)
{
	char data[2 * sizeof(struct map_module) + 1] = {0};
	int ret;
	uint32_t map_id;
	struct blob_buf bb = {};
	struct map_module m = {
		.id = 0xa5a5a5a5,
		.process_cmdu_funcname = "mapclient_process_cmdu",
	};

	ret = ubus_lookup_id(priv->ctx, map_plugin, &map_id);
	if (ret) {
		fprintf(stderr, "plugin '%s' lookup failed. %s\n",
			map_plugin, ubus_strerror(ret));
		return;
	}

	priv->map_oid = map_id;

	/* register as client to the map module */
	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "module", "mapclient");
	btostr((unsigned char *)&m, sizeof(struct map_module), data);
	blobmsg_add_string(&bb, "data", data);
	ret = ubus_invoke(priv->ctx, priv->map_oid, "register", bb.head, register_cb,
			  priv, 1000);
	if (ret) {
		fprintf(stderr, "Failed to 'register' with %s (err = %s)\n",
			map_plugin, ubus_strerror(ret));
	}

	blob_buf_free(&bb);

	return;
}

int main(int argc, char **argv)
{
	struct mapclient_private *priv;
	const char *ubus_socket = NULL;
	int ch;

	while ((ch = getopt(argc, argv, "s:o:")) != -1) {
		switch (ch) {
		case 's':
			ubus_socket = optarg;
			break;
		case 'o':
			objname = optarg;
			break;
		default:
			break;
		}
	}

	priv = calloc(1, sizeof(*priv));
	if (!priv)
		return -1;

	uloop_init();
	priv->ctx = ubus_connect(ubus_socket);
	if (!priv->ctx) {
		fprintf(stderr, "Failed to connect to ubus\n");
		free(priv);
		return -1;
	}

	ubus_add_uloop(priv->ctx);

	priv->oid = 0xdeadbeaf;

	mapclient_subscribe_for_cmdus(priv);

	uloop_run();

	ubus_free(priv->ctx);
	uloop_done();
	free(priv);

	return 0;
}
