#ifdef EASYMESH_VENDOR_EXT

#include <stdio.h>
#include <string.h>
#include <cmdu.h>
#include "cntlr.h" 
#include "cntlr_tlv.h" 
#include "cntlr_ubus.h"
#include "cntlr_extension.h"
#include "utils/debug.h"


#ifdef PROPAGATE_PROBE_REQ
int handle_vendor_specific_probe_req(void *cntlr, struct cmdu_buff *rx_cmdu,
				     struct tlv_vendor_probe_req *tlv)
{
	struct controller *c = (struct controller *) cntlr;
	char evt_data[120];

	/* process probe request from agent */
	snprintf(evt_data, sizeof(evt_data),
	    "{\"macaddr\":\"" MACFMT "\", "
	    "\"almacaddr\":\"" MACFMT "\", "
	    "\"bssid\":\"" MACFMT "\", "
	    "\"rcpi\":%" PRIu8 "}",
	    MAC2STR(tlv->macaddr),
	    MAC2STR(rx_cmdu->origin),
	    MAC2STR(tlv->bssid),
	    tlv->rcpi);
	cntlr_notify_event(c, CNTLR_EVENT_PROBE_REQ, evt_data);

	return 0;
}
#endif

int handle_vendor_extension(void *cntlr, struct cmdu_buff *rx_cmdu, struct node *n)
{
	struct tlv *tv[1][TLV_MAXNUM] = {0};
	struct tlv_vendor_specific *tlv;
	struct tlv_vendor_impl *tlv_impl;
	const uint8_t vendor_oui[4] = {0};
	uint32_t oui = 0;

	if (!map_cmdu_validate_parse(rx_cmdu, tv, ARRAY_SIZE(tv), n->map_profile)) {
		err("cmdu validation: [VENDOR_SPECIFIC] failed\n");
		return -1;
	}

	if (!tv[0][0]) {
		return -1;
	}

	tlv = (struct tlv_vendor_specific *)tv[0][0]->data;

	BUF_PUT_BE24(vendor_oui, EASYMESH_VENDOR_EXT_OUI_DEFAULT);
#ifdef EASYMESH_VENDOR_EXT_OUI
	oui = EASYMESH_VENDOR_EXT_OUI;
	BUF_PUT_BE24(vendor_oui, oui);
#endif

	if (memcmp(vendor_oui, tlv->oui, 3)) {
		warn("%s: unknown vendor TLV\n", __func__);
		return -1;
	}

	if (tv[0][0]->len < sizeof(struct tlv_vendor_impl)) {
		warn("%s: invalid length of vendor TLV\n", __func__);
		return -1;
	}

	tlv_impl = (struct tlv_vendor_impl *)tv[0][0]->data;

	switch (tlv_impl->vendor_type) {
#ifdef PROPAGATE_PROBE_REQ
	case TLV_VENDOR_TYPE_IMPL_PROBE_REQ:
		if (tv[0][0]->len < sizeof(struct tlv_vendor_probe_req)) {
			warn("%s: invalid length of vendor Probe Request TLV\n",
					__func__);
			return -1;
		}
		return handle_vendor_specific_probe_req(cntlr, rx_cmdu,
				(struct tlv_vendor_probe_req *)tlv_impl);
#endif
	default:
		warn("%s: unknown type of vendor TLV\n", __func__);
		break;
	}

	return -1;
}

int cntlr_gen_vendor_specific_disassociate_sta_tlv(struct controller *c,
		struct cmdu_buff *frm, uint8_t *sta, uint16_t reason)
{
	struct tlv *t;
	struct tlv_vendor_disassociate_sta *data;
	int ret;
	const uint8_t vendor_oui[4] = {0};
	uint32_t oui = 0;

	BUF_PUT_BE24(vendor_oui, EASYMESH_VENDOR_EXT_OUI_DEFAULT);
#ifdef EASYMESH_VENDOR_EXT_OUI
	oui = EASYMESH_VENDOR_EXT_OUI;
	BUF_PUT_BE24(vendor_oui, oui);
#endif

	/* prepare TLVs */
	t = cmdu_reserve_tlv(frm, 32);
	if (!t) {
		err("%s: -ENOMEM\n", __func__);
		return -1;
	}

	t->type = TLV_TYPE_VENDOR_SPECIFIC;
	t->len = sizeof(struct tlv_vendor_disassociate_sta);
	data = (struct tlv_vendor_disassociate_sta *)t->data;

	memcpy(data->vendor_impl.vendor.oui, vendor_oui, 3);
	data->vendor_impl.vendor_type = TLV_VENDOR_TYPE_IMPL_DISASSOCIATE_STA;

	memcpy(data->macaddr, sta, 6);
	BUF_PUT_BE16(data->reason, reason);

	ret = cmdu_put_tlv(frm, t);
	if (ret) {
		err("%s: error: cmdu_put_tlv()\n", __func__);
		return -1;
	}

	return 0;
}

struct cmdu_buff *cntlr_gen_vendor_specific_disassociate_sta(struct controller *c,
		uint8_t *sta, uint16_t reason)
{
	struct cmdu_buff *frm;
	int ret;
	uint16_t mid = 0;

	frm = cmdu_alloc_simple(CMDU_TYPE_VENDOR_SPECIFIC, &mid);
	if (!frm) {
		err("%s: -ENOMEM\n", __func__);
		return NULL;
	}

	ret = cntlr_gen_vendor_specific_disassociate_sta_tlv(c, frm, sta, reason);
	if (ret)
		goto out;

	cmdu_put_eom(frm);
	return frm;
out:
	cmdu_free(frm);
	return NULL;
}

int cntlr_gen_vendor_specific_reset_agent_tlv(struct controller *c, struct cmdu_buff *frm)
{
	struct tlv *t;
	struct tlv_vendor_reset_agent *data;
	int ret;
	const uint8_t vendor_oui[4] = {0};
	uint32_t oui = 0;

	BUF_PUT_BE24(vendor_oui, EASYMESH_VENDOR_EXT_OUI_DEFAULT);
#ifdef EASYMESH_VENDOR_EXT_OUI
	oui = EASYMESH_VENDOR_EXT_OUI;
	BUF_PUT_BE24(vendor_oui, oui);
#endif

	/* prepare TLVs */
	t = cmdu_reserve_tlv(frm, 32);
	if (!t) {
		err("%s: -ENOMEM\n", __func__);
		return -1;
	}

	t->type = TLV_TYPE_VENDOR_SPECIFIC;
	t->len = sizeof(struct tlv_vendor_reset_agent);
	data = (struct tlv_vendor_reset_agent *)t->data;

	memcpy(data->vendor_impl.vendor.oui, vendor_oui, 3);
	data->vendor_impl.vendor_type = TLV_VENDOR_TYPE_IMPL_RESET_AGENT;

	ret = cmdu_put_tlv(frm, t);
	if (ret) {
		err("%s: error: cmdu_put_tlv()\n", __func__);
		return -1;
	}

	return 0;
}

struct cmdu_buff *cntlr_gen_vendor_specific_reset_agent(struct controller *c)
{
	struct cmdu_buff *frm;
	int ret;
	uint16_t mid = 0;

	frm = cmdu_alloc_simple(CMDU_TYPE_VENDOR_SPECIFIC, &mid);
	if (!frm) {
		err("%s: -ENOMEM\n", __func__);
		return NULL;
	}

	ret = cntlr_gen_vendor_specific_reset_agent_tlv(c, frm);
	if (ret)
		goto out;

	cmdu_put_eom(frm);
	return frm;
out:
	cmdu_free(frm);
	return NULL;
}

int cntlr_gen_vendor_specific_reload_wifi_tlv(struct controller *c, struct cmdu_buff *frm)
{
	struct tlv *t;
	struct tlv_vendor_reload_wifi *data;
	int ret;
	const uint8_t vendor_oui[4] = {0};
	uint32_t oui = 0;

	BUF_PUT_BE24(vendor_oui, EASYMESH_VENDOR_EXT_OUI_DEFAULT);
#ifdef EASYMESH_VENDOR_EXT_OUI
	oui = EASYMESH_VENDOR_EXT_OUI;
	BUF_PUT_BE24(vendor_oui, oui);
#endif

	/* prepare TLVs */
	t = cmdu_reserve_tlv(frm, 32);
	if (!t) {
		err("%s: -ENOMEM\n", __func__);
		return -1;
	}

	t->type = TLV_TYPE_VENDOR_SPECIFIC;
	t->len = sizeof(struct tlv_vendor_reload_wifi);
	data = (struct tlv_vendor_reload_wifi *)t->data;

	memcpy(data->vendor_impl.vendor.oui, vendor_oui, 3);
	data->vendor_impl.vendor_type = TLV_VENDOR_TYPE_IMPL_RELOAD_WIFI;

	ret = cmdu_put_tlv(frm, t);
	if (ret) {
		err("%s: error: cmdu_put_tlv()\n", __func__);
		return -1;
	}

	return 0;
}

struct cmdu_buff *cntlr_gen_vendor_specific_reload_wifi(struct controller *c)
{
	struct cmdu_buff *frm;
	int ret;
	uint16_t mid = 0;

	frm = cmdu_alloc_simple(CMDU_TYPE_VENDOR_SPECIFIC, &mid);
	if (!frm) {
		err("%s: -ENOMEM\n", __func__);
		return NULL;
	}

	ret = cntlr_gen_vendor_specific_reload_wifi_tlv(c, frm);
	if (ret)
		goto out;

	cmdu_put_eom(frm);
	return frm;
out:
	cmdu_free(frm);
	return NULL;
}
#endif
