#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "dppfrag.h"

#include <dpp_api.h>
#include <dpputils.h>
#include <easy/bufutil.h>
#include <easy/utils.h>
#include <libubox/list.h>
#include <stdlib.h>
#include <string.h>
#include <timer.h>

#include "agent.h"
#include "dpp.h"
#include "timer_impl.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "wifi_ubus.h"

static struct dppfrag_rx *dppfrag_lookup(struct agent *a,
					    uint8_t id,
					    uint8_t *src)
{
	int idx = dppfrag_hash(id, src);
	struct dppfrag_rx *frag = NULL;

	hlist_for_each_entry(frag, &a->fragtable[idx], hlist) {
		if (frag->id == id &&
		    !memcmp(frag->src, src, 6)) {
			return frag;
		}
	}

	return NULL;
}

static struct dppfrag_rx *dppfrag_queue_lookup_fid(struct dppfrag_rx *frag, uint8_t fid)
{
	struct dppfrag_rx *f;

	for (f = frag; f; f = f->next) {
		if (f->fid == fid)
			return f;
	}

	return NULL;
}

static struct dppfrag_rx *dppfrag_lookup_fid(struct agent *a,
					    uint8_t id,
					    uint8_t fid,
					    uint8_t *src)
{
	struct dppfrag_rx *frag = NULL, *f = NULL;

	frag = dppfrag_lookup(a, id, src);
	if (!frag)
		return NULL;

	for (f = frag; f; f = f->next) {
		if (f->fid == fid)
			return f;
	}

	return NULL;
}

bool dppfrag_complete(struct agent *a, uint8_t id, uint8_t *src)
{
	struct dppfrag_rx *f;

	f = dppfrag_lookup(a, id, src);
	if (!f)
		return false;

	return (f->totfrags == f->numfrags);
}


uint8_t *dpp_build_gas_initial_response(uint16_t buflen, uint8_t *buf,
					uint16_t status, uint8_t dialog_token,
					bool fragment, uint16_t *outlen)
{
	uint8_t *frame, *frame_start;
	int len = buflen + DPP_GAS_INITIAL_REQ_HEADER_LEN;

	frame = calloc(1, len);
	if (!frame)
		return NULL;

	frame_start = frame;
	bufptr_put_u8(frame, WLAN_ACTION_PUBLIC);
	bufptr_put_u8(frame, DPP_PUB_AF_GAS_INITIAL_RESP);
	bufptr_put_u8(frame, dialog_token);
	bufptr_put_le16(frame, status);
	bufptr_put_le16(frame, 0x01);

	bufptr_put_u8(frame, WLAN_EID_ADV_PROTO);
	bufptr_put_u8(frame, 8); /* Length */
	bufptr_put_u8(frame, 0x7f);

	bufptr_put_u8(frame, WLAN_EID_VENDOR_SPECIFIC);
	bufptr_put_u8(frame, 5);
	bufptr_put(frame, "\x50\x6f\x9a", 3);
	bufptr_put_u8(frame, DPP_OUI_TYPE);
	bufptr_put_u8(frame, (fragment ? 0x01 : 0x00));
	bufptr_put_le16(frame, buflen);
	if (buflen && buf)
		bufptr_put(frame, buf, buflen);

	*outlen = len;
	return frame_start;
}

uint8_t *dpp_build_gas_comeback_response(uint16_t buflen, uint8_t *buf,
					 uint16_t status, uint8_t dialog_token,
					 uint8_t id)
{
	uint8_t *frame, *frame_start;

	if (buflen > DPP_FRAG_SIZE) {
		agnt_err(LOG_DPP, "%s: input buffer too large:%u -- max size:%d\n", __func__,
		    buflen, DPP_FRAG_SIZE);
		return NULL;
	}

	frame = calloc(1, buflen + DPP_GAS_INITIAL_RESP_HEADER_LEN);
	if (!frame)
		return NULL;

	frame_start = frame;

	bufptr_put_u8(frame, WLAN_ACTION_PUBLIC);
	bufptr_put_u8(frame, DPP_PUB_AF_GAS_COMEBACK_RESP);
	bufptr_put_u8(frame, dialog_token); /* dialogue token */
	bufptr_put_le16(frame, status);
	bufptr_put_u8(frame, id); /* frag id */
	bufptr_put_le16(frame, 0);

	bufptr_put_u8(frame, WLAN_EID_ADV_PROTO);
	bufptr_put_u8(frame, 8); /* Length */
	bufptr_put_u8(frame, 0x7f);
	bufptr_put_u8(frame, WLAN_EID_VENDOR_SPECIFIC);
	bufptr_put_u8(frame, 5);
	bufptr_put(frame, "\x50\x6f\x9a", 3);
	bufptr_put_u8(frame, DPP_OUI_TYPE);
	bufptr_put_u8(frame, 0x01);

	bufptr_put_le16(frame, buflen);
	bufptr_put(frame, buf, buflen);

	return frame_start;
}

/* fragments payload and sends in air as GAS comeback response frames */
int dpp_send_gas_comeback_responses(struct agent *a, struct netif_ap *ap,
				   uint8_t *enrollee, uint8_t *payload,
				   uint16_t len, uint8_t dialog_token)
{
	uint16_t rlen = 0;
	uint8_t idx = 0;
	uint8_t *buf;
	int ret = 0;

	while (rlen < len) {
		uint16_t flen = 0;
		uint8_t fragid = idx++;
		uint8_t *ptr;

		if ((len - rlen) > DPP_FRAG_SIZE) {
			fragid |= BIT(7); /* no more frames */
			flen = DPP_FRAG_SIZE;
		} else
			flen = len % DPP_FRAG_SIZE;

		ptr = payload + rlen;

		buf = dpp_build_gas_comeback_response(flen, ptr,
						      WLAN_STATUS_SUCCESS,
						      dialog_token, fragid);
		if (!buf) {
			ret = -1;
			break;
		}

		wifi_ubus_ap_send_actframe(a->ubus_ctx, ap->ifname, enrollee,
					   buf,
					   flen + DPP_GAS_INITIAL_RESP_HEADER_LEN,
					   0,
					   true);
		free(buf);
		rlen += flen;
	}

	return ret;
}

uint8_t *dppfrag_defrag_payload(struct agent *a, uint8_t hlen,
					      uint8_t id,
					      uint8_t *src,
					      uint16_t *outlen)
{
	uint8_t *payload, *payload_head;
	uint16_t len;
	int i;
	struct dppfrag_rx *firstfrag;

	firstfrag = dppfrag_lookup(a, id, src);
	if (!firstfrag)
		return NULL;


	len = firstfrag->totlen;
	len -= (hlen * firstfrag->numfrags);

	payload = calloc(1, len);
	if (!payload) {
		agnt_err(LOG_DPP, "%s: failed to alloc frame\n", __func__);
		return NULL;
	}

	payload_head = payload;
	for (i = 0; i < firstfrag->numfrags; i++) {
		struct dppfrag_rx *f;

		f = dppfrag_queue_lookup_fid(firstfrag, i);
		if (!f) {
			free(payload_head);
			return NULL;
		}

		bufptr_put(payload, (f->frame + hlen), (f->framelen - hlen));
	}

	*outlen = len;
	return payload_head;
}

static struct dppfrag_rx *dppfrag_rx_alloc(uint16_t frametype, uint8_t *frame,
					   uint16_t framelen,
					   uint8_t id, uint8_t fid,
					   uint8_t *src, bool lastfrag,
					   void *priv)
{
	struct dppfrag_rx *frag;

	frag = calloc(1, sizeof(*frag));
	if (!frag) {
		agnt_err(LOG_DPP, "calloc failed. err = NOMEM\n");
		return NULL;
	}

	frag->frame = calloc(1, framelen);
	if (!frag->frame)
		goto error;

	memcpy(frag->frame, frame, framelen);

	frag->frametype = frametype;
	frag->framelen = framelen;
	frag->fid = fid;
	frag->lastfrag = lastfrag;
	if (lastfrag)
		frag->totfrags = (fid + 1);
	frag->id = id;
	memcpy(frag->src, src, 6);
	frag->next = NULL;
	frag->last = frag;
	frag->numfrags = 1;
	frag->totlen = framelen;
	frag->priv = priv;
	agnt_dbg(LOG_DPP, "CREATE frag: id = %u framelen = %u fid = %u  lastfrag = %d  src = " MACFMT " ptr = %p\n",
		frag->id,
		frag->framelen,
		frag->fid,
		frag->lastfrag,
		MAC2STR(src),
		frag);

	return frag;
error:
	free(frag);
	return NULL;
}


static void dppfrag_queue_delete_chain(struct dppfrag_rx *frag)
{
	struct dppfrag_rx *ee = NULL, *e;

	if (!frag)
		return;

	timer_del(&frag->ageing_timer);

	if (frag->priv) {
		struct agent *a = frag->priv;
		int idx = dppfrag_hash(frag->id, frag->src);

		hlist_del(&frag->hlist, &a->fragtable[idx]);
	}

	for (e = frag->next; e; e = e->next) {
		if (ee) {
			agnt_dbg(LOG_DPP, "freeing ee ..fid = %d ....\n", ee->fid);
			free(ee->frame);
			free(ee);
		}
		ee = e;
	}

	if (ee) {
		agnt_dbg(LOG_DPP, "freeing ee ..fid = %d ...\n", ee->fid);
		free(ee->frame);
		free(ee);
	}

	free(frag->frame);
	free(frag);
}

static void dpp_fragqueue_ageout_timer(atimer_t *t)
{
	struct dppfrag_rx *f = container_of(t, struct dppfrag_rx, ageing_timer);

	dppfrag_queue_delete_chain(f);
}

int dppfrag_queue_enqueue(struct agent *a, uint16_t frametype, uint8_t *frame,
			  uint16_t framelen, uint8_t fragbyte, uint8_t id,
			  uint8_t *src)
{
	struct dppfrag_rx *frag;
	uint8_t fid, lastfrag;

	if (framelen < 5)
		return -1;

	fid = (fragbyte & 0x7f);
	lastfrag = !BIT1(7, fragbyte);
	frag = dppfrag_lookup_fid(a, fid, id, src);
	if (frag) {
		agnt_err(LOG_DPP, "DROP duplicate: fid = %u id = %u src = " MACFMT "\n",
			fid, id, MAC2STR(src));

		return -1;
	}

	frag = dppfrag_rx_alloc(frametype, frame, framelen, id, fid,
				src, lastfrag, (void *) a);
	if (frag) {
		uint8_t i = dppfrag_hash(id, src);

		if (!hlist_empty(&a->fragtable[i])) {
			struct dppfrag_rx *firstfrag;

			firstfrag = dppfrag_lookup(a, id, src);
			if (!firstfrag) {
				free(frag->frame);
				free(frag);
				return -1;
			}

			firstfrag->last->next = frag;
			firstfrag->last = frag;
			firstfrag->totlen += frag->framelen;
			firstfrag->numfrags++;
			if (lastfrag)
				firstfrag->totfrags = (fid + 1);

			agnt_dbg(LOG_DPP, "%s: totlen = %d   numfrags = %d totfrags = %d\n",
				__func__, firstfrag->totlen,
				firstfrag->numfrags, firstfrag->totfrags);

			if (firstfrag->numfrags == firstfrag->totfrags) {
				agnt_dbg(LOG_DPP, "%s: all fragments received for id:%u!\n", __func__, firstfrag->id);
				return 1;
			}
			return 0;
		}

		hlist_add_head(&frag->hlist, &a->fragtable[i]);

		timer_init(&frag->ageing_timer, dpp_fragqueue_ageout_timer);
		timer_set(&frag->ageing_timer, 1 * 1000);

		return 0;
	}

	return -1;
}

#endif
#endif