/*
 * dump.c
 * WiFi Data Elements json objects dump functions
 *
 * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
 *
 * See AUTHORS file for author and contributors.
 *
 * See LICENSE file for license related information.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <libubox/blobmsg.h>
#include <libubox/utils.h>
#include <easy/easy.h>
#include <cmdu.h>

#include "debug.h"
#include "decollector.h"
#include "utils.h"
#include "wifi_dataelements.h"

#ifdef DECOLLECTOR_BUILD_TR181_PLUGIN
#include <libbbfdm-ubus/bbfdm-ubus.h>

void bbfdm_send_WiFiDataElementsAssociationEvent_Associated_event(struct wifi_assoc_event *e);
void bbfdm_send_WiFiDataElementsAssociationEvent_Disassociated_event(struct wifi_disassoc_event *e);
#endif

static int remove_spec_char(unsigned char *out, int out_len,
		const unsigned char *src, int src_len)
{
	int i;
	int copy_len = 0;

	if (!out && !src)
		return -1;

	memset(out, 0, out_len);
	copy_len = out_len > src_len ? src_len : out_len;

	for (i = 0; i < copy_len; i++) {
		if (src[i] == '\n') {
			out[i] = '\0';
			goto out;
		} else
			out[i] = src[i];
	}

	out[i] = '\0';
out:

	return 0;
}

int decollector_dump_station(struct wifi_sta_element *sta, struct blob_buf *bb)
{
	char macaddr[18] = { 0 };
	void *arr;
	void *t;
	int i;
	size_t out_len;
	unsigned char caps_str[32];
	unsigned char caps_str1[32];
	int str1_len = sizeof(caps_str1);
	int he_caps_len = 0;

	hwaddr_ntoa(sta->macaddr, macaddr);
	blobmsg_add_string(bb, "MACAddress", macaddr);
	blobmsg_add_string(bb, "TimeStamp", sta->tsp);
	blobmsg_add_u32(bb, "SignalStrength", sta->rcpi);

	//if (sta->caps.valid & HT_CAP_VALID) {
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		base64_encode(&sta->caps.ht, sizeof(sta->caps.ht), caps_str, &out_len);
		remove_spec_char(caps_str1, str1_len, caps_str, out_len);
		blobmsg_add_string(bb, "HTCapabilities", (char *)caps_str1);
	//}

	//if (sta->caps.valid & VHT_CAP_VALID) {
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		base64_encode(sta->caps.vht, sizeof(sta->caps.vht), caps_str, &out_len);
		remove_spec_char(caps_str1, str1_len, caps_str, out_len);
		blobmsg_add_string(bb, "VHTCapabilities", (char *)caps_str1);
	//}

	//if (sta->caps.valid & HE_CAP_VALID) {
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		if (sta->caps.valid & HE_CAP_VALID)
			he_caps_len = 2 + sta->caps.he[0];
		else
			he_caps_len = 4;	/* pass the usp-validator range test */
		base64_encode(&sta->caps.he[1], he_caps_len, caps_str, &out_len);
		remove_spec_char(caps_str1, str1_len, caps_str, out_len);
		blobmsg_add_string(bb, "HECapabilities", (char *)caps_str1);
	//}

	blobmsg_add_u32(bb, "EstMACDataRateDownlink", sta->dl_est_thput);
	blobmsg_add_u32(bb, "EstMACDataRateUplink", sta->ul_est_thput);
	blobmsg_add_u32(bb, "LastDataDownlinkRate", sta->dl_rate);
	blobmsg_add_u32(bb, "LastDataUplinkRate", sta->ul_rate);
	//blobmsg_add_string(bb, "Airtime", sta->dl_utilization);  //TODO
	blobmsg_add_u64(bb, "UtilizationTransmit", sta->dl_utilization);
	blobmsg_add_u64(bb, "UtilizationReceive", sta->ul_utilization);
	blobmsg_add_u32(bb, "LastConnectTime", sta->conn_time);
	//blobmsg_add_string(bb, "IPV4Address", sta->ipaddr.addr);
	//blobmsg_add_string(bb, "IPV6Address", sta->ipaddr.addr); //TODO
	blobmsg_add_string(bb, "Hostname", sta->hostname);
	blobmsg_add_u64(bb, "BytesSent", sta->tx_bytes);
	blobmsg_add_u64(bb, "BytesReceived", sta->rx_bytes);
	blobmsg_add_u32(bb, "PacketsSent", sta->tx_pkts);
	blobmsg_add_u32(bb, "PacketsReceived", sta->rx_pkts);
	blobmsg_add_u32(bb, "ErrorsSent", sta->tx_errors);
	blobmsg_add_u32(bb, "ErrorsReceived", sta->rx_errors);
	blobmsg_add_u32(bb, "RetransCount", sta->rtx_pkts);
	blobmsg_add_u32(bb, "NumberOfMeasureReports", sta->num_meas_reports);

	arr = blobmsg_open_array(bb, "Measurementreport");
	for (i = 0; i < sta->num_meas_reports; i++) {
		t = blobmsg_open_table(bb, "");
		//decollector_dump_sta_bcnreport(sta->bcn_reportlist, bb);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	return 0;
}

int decollector_dump_bss(struct wifi_bss_element *bss, struct blob_buf *bb)
{
	unsigned char est_str[16];
	unsigned char est_str1[16];
	int str1_len = sizeof(est_str1);
	struct wifi_sta_element *sta;
	char bssid[18] = { 0 };
	size_t out_len;
	void *arr;
	void *t;



	hwaddr_ntoa(bss->bssid, bssid);
	blobmsg_add_string(bb, "BSSID", bssid);
	blobmsg_add_string(bb, "SSID", (char *)bss->ssid);
	blobmsg_add_string(bb, "TimeStamp", bss->tsp);
	blobmsg_add_u8(bb, "Enabled", bss->enabled);
	blobmsg_add_u32(bb, "LastChange", bss->uptime);
	blobmsg_add_u32(bb, "NumberofSTA", bss->num_stations);
	out_len = sizeof(est_str);
	memset(est_str, 0, sizeof(est_str));
	base64_encode(bss->est_wmm_be, 3, est_str, &out_len);
	remove_spec_char(est_str1, str1_len, est_str, out_len);
	blobmsg_add_string(bb, "EstServiceParametersBE", (char *)est_str1);
	out_len = sizeof(est_str);
	memset(est_str, 0, sizeof(est_str));
	base64_encode(bss->est_wmm_bk, 3, est_str, &out_len);
	remove_spec_char(est_str1, str1_len, est_str, out_len);
	blobmsg_add_string(bb, "EstServiceParametersBK", (char *)est_str1);
	out_len = sizeof(est_str);
	memset(est_str, 0, sizeof(est_str));
	base64_encode(bss->est_wmm_vo, 3, est_str, &out_len);
	remove_spec_char(est_str1, str1_len, est_str, out_len);
	blobmsg_add_string(bb, "EstServiceParametersVO", (char *)est_str1);
	out_len = sizeof(est_str);
	memset(est_str, 0, sizeof(est_str));
	base64_encode(bss->est_wmm_vi, 3, est_str, &out_len);
	remove_spec_char(est_str1, str1_len, est_str, out_len);
	blobmsg_add_string(bb, "EstServiceParametersVI", (char *)est_str1);
	blobmsg_add_u64(bb, "UnicastBytesReceived", bss->rx_ucast_bytes);
	blobmsg_add_u64(bb, "UnicastBytesSent", bss->tx_ucast_bytes);
	blobmsg_add_u64(bb, "MulticastBytesReceived", bss->rx_mcast_bytes);
	blobmsg_add_u64(bb, "MulticastBytesSent", bss->tx_mcast_bytes);
	blobmsg_add_u64(bb, "BroadcastBytesReceived", bss->rx_bcast_bytes);
	blobmsg_add_u64(bb, "BroadcastBytesSent", bss->tx_bcast_bytes);

	arr = blobmsg_open_array(bb, "STAList");
	list_for_each_entry(sta, &bss->stalist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_station(sta, bb);  /* cppcheck-suppress uninitvar */
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	return 0;
}

int decollector_dump_scanres_nbr(struct wifi_scanres_neighbor_element *nbr,
				 struct blob_buf *bb)
{
	char nbr_bssid[18] = { 0 };

	hwaddr_ntoa(nbr->bssid, nbr_bssid);
	blobmsg_add_u32(bb, "StationCount", nbr->num_stations);
	blobmsg_add_u32(bb, "ChannelUtilization", nbr->utilization);
	blobmsg_add_string(bb, "BSSID", nbr_bssid);
	blobmsg_add_string(bb, "SSID", nbr->ssid);
	blobmsg_add_u32(bb, "SignalStrengh", nbr->rssi);
	blobmsg_add_u32(bb, "ChannelBandwidth", nbr->bw);

	return 0;
}

int decollector_dump_channel_scanres(struct wifi_scanres_channel_element *ch,
				     struct blob_buf *bb)
{
	struct wifi_scanres_neighbor_element *nbr;
	void *arr;
	void *t;


	blobmsg_add_string(bb, "TimeStamp", ch->tsp);
	blobmsg_add_u32(bb, "Channel", ch->channel);
	blobmsg_add_u32(bb, "NumberofNeighbors", ch->num_neighbors);
	blobmsg_add_u32(bb, "Noise", ch->anpi);
	blobmsg_add_u32(bb, "Utilization", ch->utilization);

	arr = blobmsg_open_array(bb, "NeighborList");
	list_for_each_entry(nbr, &ch->nbrlist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_scanres_nbr(nbr, bb);  /* cppcheck-suppress uninitvar */
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	return 0;
}

int decollector_dump_opclass_scanres(struct wifi_scanres_opclass_element *op,
				     struct blob_buf *bb)
{
	struct wifi_scanres_channel_element *ch;
	void *arr;
	void *t;


	blobmsg_add_u32(bb, "OperatingClass", op->opclass);
	blobmsg_add_u32(bb, "NumberOfChannelScans", op->num_channels_scanned);

	arr = blobmsg_open_array(bb, "ChannelScanList");
	list_for_each_entry(ch, &op->channel_scanlist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_channel_scanres(ch, bb);  /* cppcheck-suppress uninitvar */
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	return 0;
}

int decollector_dump_scanresult(struct wifi_scanres_element *sres, struct blob_buf *bb)
{
	struct wifi_scanres_opclass_element *op;
	void *arr;
	void *t;


	blobmsg_add_string(bb, "TimeStamp", sres->tsp);
	blobmsg_add_u32(bb, "NumberOfOpClassScans", sres->num_opclass_scanned);

	arr = blobmsg_open_array(bb, "OpClassScanList");
	list_for_each_entry_reverse(op, &sres->opclass_scanlist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_opclass_scanres(op, bb);  /* cppcheck-suppress uninitvar */
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	return 0;
}

int decollector_dump_unassoc_sta(struct wifi_unassoc_sta_element *usta,
							struct blob_buf *bb)
{
	char macaddr[18] = { 0 };

	hwaddr_ntoa(usta->macaddr, macaddr);
	blobmsg_add_string(bb, "MACAddress", macaddr);
	blobmsg_add_u32(bb, "SignalStrength", usta->rcpi);

	return 0;
}

int decollector_dump_backhaul_sta(struct wifi_backhaul_element *bksta,
						struct blob_buf *bb)
{
	char macaddr[18] = { 0 };

	hwaddr_ntoa(bksta->macaddr, macaddr);
	blobmsg_add_string(bb, "MACAddress", macaddr);
	return 0;
}

int decollector_dump_curr_opclass(struct wifi_radio_opclass_entry *curr, struct timespec *timestamp,
				  struct blob_buf *bb)
{
	struct tm *tm = localtime(&timestamp->tv_sec);
	char str_tm[64] = {0};

	strftime(str_tm, sizeof(str_tm), "%Y-%m-%dT%H:%M:%SZ", tm);

	blobmsg_add_string(bb, "TimeStamp", str_tm);
	blobmsg_add_u32(bb, "Channel", curr->channel->channel);
	blobmsg_add_u32(bb, "Class", curr->id);
	blobmsg_add_u32(bb, "TxPower", curr->max_txpower);

	return 0;
}

int decollector_dump_supp_opclass(struct wifi_radio_opclass_entry *supp,
				  struct blob_buf *bb)
{
	void *arr;
	int i;
	int num_excluded_channels = 0;

	blobmsg_add_u32(bb, "Class", supp->id);
	blobmsg_add_u32(bb, "MaxTxPower", supp->max_txpower);

	arr = blobmsg_open_array(bb, "NonOperable");
	for (i = 0; i < supp->num_channel; i++) {
		if (supp->channel[i].preference == WIFI_RADIO_OPCLASS_NON_OPERABLE) {
			++num_excluded_channels;
			blobmsg_add_u32(bb, "", supp->channel[i].channel);
		}
	}
	blobmsg_close_array(bb, arr);

	blobmsg_add_u32(bb, "NumberOfNonOperChan", num_excluded_channels);

	return 0;
}

int decollector_dump_radio(struct wifi_radio_element *radio, struct blob_buf *bb)
{
	struct wifi_unassoc_sta_element *usta = NULL;
	struct wifi_scanres_element *sres = NULL;
	struct wifi_bss_element *bss = NULL;
	unsigned char caps_str[32];
	unsigned char caps_str1[32];
	int str1_len = sizeof(caps_str1);
	int he_caps_len = 0;
	size_t out_len;
	void *t, *t2;
	void *arr;
	int i;


	out_len = sizeof(caps_str);
	memset(caps_str, 0, sizeof(caps_str));
	base64_encode(radio->macaddr, sizeof(radio->macaddr), caps_str, &out_len);
	remove_spec_char(caps_str1, str1_len, caps_str, out_len);
	blobmsg_add_string(bb, "ID", (char *)caps_str1);

	blobmsg_add_u32(bb, "Noise", radio->anpi);
	blobmsg_add_u32(bb, "NumberOfBSS", radio->num_bss);
	blobmsg_add_u8(bb, "Enabled", radio->enabled);
	blobmsg_add_u32(bb, "NumberOfCurrOpClass", radio->cur_opclass.num_opclass);
	blobmsg_add_u32(bb, "NumberOfUnassocSta", radio->num_unassoc_sta);
	blobmsg_add_u32(bb, "Utilization", radio->total_utilization);
	blobmsg_add_u32(bb, "Transmit", radio->tx_utilization);
	blobmsg_add_u32(bb, "ReceiveSelf", radio->rx_utilization);
	blobmsg_add_u32(bb, "RecieveOther", radio->other_utilization);

	/* bss */
	arr = blobmsg_open_array(bb, "BSSList");
	list_for_each_entry(bss, &radio->bsslist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_bss(bss, bb);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	/* capabilities { supp-opclass, ht/vht/he caps } */
	t = blobmsg_open_table(bb, "Capabilities");
	blobmsg_add_u32(bb, "NumberOfOpClass", radio->supp_opclass.num_opclass);
	arr = blobmsg_open_array(bb, "OperatingClasses");
	for (i = 0; i < radio->supp_opclass.num_opclass; i++) {
		t2 = blobmsg_open_table(bb, "");
		decollector_dump_supp_opclass(radio->supp_opclass.opclass + i, bb);
		blobmsg_close_table(bb, t2);
	}
	blobmsg_close_array(bb, arr);
	//if (radio->caps.valid & HT_CAP_VALID) {
		dbg("%s: %d: HT-cap = 0x%02x\n", __func__, __LINE__, radio->caps.ht);
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		base64_encode(&radio->caps.ht, sizeof(radio->caps.ht), caps_str, &out_len);
		remove_spec_char(caps_str1, str1_len, caps_str, out_len);
		blobmsg_add_string(bb, "HTCapabilities", (char *)caps_str1);
	//}

	//if (radio->caps.valid & VHT_CAP_VALID) {
		dbg("%s: %d: address = %p     VHT-cap = %02x %02x %02x %02x %02x %02x\n", __func__, __LINE__,
			radio->caps.vht,
			radio->caps.vht[0],
			radio->caps.vht[1],
			radio->caps.vht[2],
			radio->caps.vht[3],
			radio->caps.vht[4],
			radio->caps.vht[5]);
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		base64_encode(radio->caps.vht, sizeof(radio->caps.vht), caps_str, &out_len);
		remove_spec_char(caps_str1, str1_len, caps_str, out_len);
		blobmsg_add_string(bb, "VHTCapabilities", (char *)caps_str1);
	//}

	//if (radio->caps.valid & HE_CAP_VALID) {
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));

		/* start encoding from 1st index as first btye is using for he length calculation */
		if (radio->caps.valid & HE_CAP_VALID)
			he_caps_len = 2 + radio->caps.he[0];
		else
			he_caps_len = 4;
		base64_encode(&radio->caps.he[1], he_caps_len, caps_str, &out_len);
		remove_spec_char(caps_str1, str1_len, caps_str, out_len);
		blobmsg_add_string(bb, "HECapabilities", (char *)caps_str1);
	//}
	blobmsg_close_table(bb, t);

	/* current opclass */
	arr = blobmsg_open_array(bb, "CurrentOperatingClasses");
	for (i = 0; i < radio->cur_opclass.num_opclass; i++) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_curr_opclass(radio->cur_opclass.opclass + i, &radio->cur_opclass.entry_time, bb);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	/* scan results */
	arr = blobmsg_open_array(bb, "ScanResultList");
	list_for_each_entry(sres, &radio->scanlist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_scanresult(sres, bb);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	/* unassociated stas */
	arr = blobmsg_open_array(bb, "UnassociatedStaList");
	list_for_each_entry(usta, &radio->unassoc_stalist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_unassoc_sta(usta, bb);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	/* backhaul sta */
	t = blobmsg_open_table(bb, "BackhaulSta");
	decollector_dump_backhaul_sta(&radio->bsta, bb);
	blobmsg_close_table(bb, t);

	return 0;
}

int decollector_dump_device(struct wifi_network_device *dev, struct blob_buf *bb,
			    struct decollector_useropts *opts)
{
	struct wifi_radio_element *radio = NULL;
	unsigned char capstr[16] = { 0 };
	unsigned char caps[16] = { 0 };
	char dev_id[18] = { 0 };
	size_t slen = 16;
	void *arr;
	void *t;


	hwaddr_ntoa(dev->macaddr, dev_id);
	blobmsg_add_string(bb, "ID", dev_id);
	blobmsg_add_u32(bb, "NumberOfRadios", dev->num_radios);
	blobmsg_add_u32(bb, "CollectionInterval", opts->refresh_int);

	base64_encode(&dev->multiap_caps, sizeof(dev->multiap_caps), capstr, &slen);
	remove_spec_char(caps, sizeof(caps), capstr, slen);
	blobmsg_add_string(bb, "MultiAPCapabilities", (char *)caps);

	arr = blobmsg_open_array(bb, "RadioList");
	list_for_each_entry(radio, &dev->radiolist, list) {
		t = blobmsg_open_table(bb, "");
		decollector_dump_radio(radio, bb);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	return 0;
}

int decollector_dump_all(struct decollector_private *p, struct blob_buf *bb)
{
	struct wifi_network_device *dev = NULL;
	struct wifi_data_element *dm = p->dm;
	char network_id[37] = { 0 };
	char cntlr_id[18] = { 0 };
	struct wifi_network *net;
	char dstr[32] = { 0 };
	char tsp[32] = { 0 };
	void *t, *t2, *t3;
	void *arr, *arr2;
	int available_nodes;


	net = &dm->network;
	available_nodes = net->num_devices;

	list_for_each_entry(dev, &net->devicelist, list) {
		if (!is_network_device_alive(dev))
			available_nodes--;
	}

	blobmsg_add_string(bb, "date", get_date(NULL, dstr));
	blobmsg_add_string(bb, "version", WIFI_DATAELEMENTS_VER_1_0);
	blobmsg_add_string(bb, "description", "WFA Data Elements");
	blobmsg_add_string(bb, "TimeStamp", get_timestamp(NULL, tsp, sizeof(tsp)));
	blobmsg_add_string(bb, "name", "wifi");

	arr = blobmsg_open_array(bb, "data");
	t = blobmsg_open_table(bb, "");

	t2 = blobmsg_open_table(bb, "wfa-dataelements:Network");

	uuid_btostr((uint8_t *)net->id, network_id);
	blobmsg_add_string(bb, "ID", network_id);
	blobmsg_add_u32(bb, "NumberOfDevices", available_nodes);
	hwaddr_ntoa(net->cntlr_id, cntlr_id);
	blobmsg_add_string(bb, "ControllerID", cntlr_id);

	blobmsg_add_string(bb, "TimeStamp", net->tsp);

	arr2 = blobmsg_open_array(bb, "DeviceList");
	list_for_each_entry(dev, &net->devicelist, list) {
		if (!is_network_device_alive(dev))
			continue;

		t3 = blobmsg_open_table(bb, "");
		decollector_dump_device(dev, bb, &p->opts);
		blobmsg_close_table(bb, t3);
	}
	blobmsg_close_array(bb, arr2);
	blobmsg_close_table(bb, t2);

	blobmsg_close_table(bb, t);
	blobmsg_close_array(bb, arr);

	return 0;
}

int decollector_dump_assoc_event(struct wifi_assoc_event *e, struct blob_buf *bb)
{
	unsigned char caps_str[32];
	unsigned char caps_str1[32];
	int he_caps_len = 0;
	size_t out_len;
	void *t1;

	blobmsg_add_string(bb, "eventTime", e->tsp);
	t1 = blobmsg_open_table(bb, "wfa-dataelements:AssociationEvent");
	//t2 = blobmsg_open_table(bb, "AssocData");
	blobmsg_add_macaddr(bb, "BSSID", e->bssid);
	blobmsg_add_macaddr(bb, "MACAddress", e->macaddr);
	blobmsg_add_u32(bb, "StatusCode", e->status_code);
	//if (e->caps.valid & HT_CAP_VALID) {
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		base64_encode(&e->caps.ht, sizeof(e->caps.ht), caps_str, &out_len);
		remove_spec_char(caps_str1, sizeof(caps_str1), caps_str, out_len);
		blobmsg_add_string(bb, "HTCapabilities", (char *)caps_str1);
	//}

	//if (e->caps.valid & VHT_CAP_VALID) {
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		base64_encode(e->caps.vht, sizeof(e->caps.vht), caps_str, &out_len);
		remove_spec_char(caps_str1, sizeof(caps_str1), caps_str, out_len);
		blobmsg_add_string(bb, "VHTCapabilities", (char *)caps_str1);
	//}

	//if (e->caps.valid & HE_CAP_VALID) {
		out_len = sizeof(caps_str);
		memset(caps_str, 0, sizeof(caps_str));
		if (e->caps.valid & HE_CAP_VALID)
			he_caps_len = 2 + e->caps.he[0];
		else
			he_caps_len = 4;
		base64_encode(&e->caps.he[0], he_caps_len, caps_str, &out_len);
		remove_spec_char(caps_str1, sizeof(caps_str1), caps_str, out_len);
		blobmsg_add_string(bb, "HECapabilities", (char *)caps_str1);
	//}
	//blobmsg_close_table(bb, t2);
	blobmsg_close_table(bb, t1);

	return 0;
}

int decollector_dump_disassoc_event(struct wifi_disassoc_event *e, struct blob_buf *bb)
{
	void *t1, *arr;
	int i;

	blobmsg_add_string(bb, "eventTime", e->tsp);
	t1 = blobmsg_open_table(bb, "wfa-dataelements:DisassociationEvent.Disassociated");
	blobmsg_add_macaddr(bb, "BSSID", e->bssid);
	blobmsg_add_macaddr(bb, "MACAddress", e->macaddr);
	blobmsg_add_u32(bb, "ReasonCode", e->reason_code);
	blobmsg_add_u32(bb, "BytesSent", e->sta.tx_bytes);
	blobmsg_add_u32(bb, "BytesReceived", e->sta.rx_bytes);
	blobmsg_add_u32(bb, "PacketsSent", e->sta.tx_pkts);
	blobmsg_add_u32(bb, "PacketsReceived", e->sta.rx_pkts);
	blobmsg_add_u32(bb, "ErrorsSent", e->sta.tx_errors);
	blobmsg_add_u32(bb, "ErrorsReceived", e->sta.rx_errors);
	blobmsg_add_u32(bb, "RetransCount", e->sta.rtx_pkts);
	blobmsg_add_u32(bb, "LastDataDownlinkRate", e->sta.dl_rate);
	blobmsg_add_u32(bb, "LastDataUplinkRate", e->sta.ul_rate);
	blobmsg_add_u32(bb, "UtilizationReceive", e->sta.dl_utilization);
	blobmsg_add_u32(bb, "UtilizationTransmit", e->sta.ul_utilization);
	blobmsg_add_u32(bb, "EstMACDataRateDownlink", 0); // TODO unsignedInt
	blobmsg_add_u32(bb, "EstMACDataRateUplink", 0);	 // TODO unsignedInt
	blobmsg_add_u32(bb, "SignalStrength", e->sta.rcpi);
	blobmsg_add_u32(bb, "LastConnectTime", e->sta.conn_time);
	blobmsg_add_u32(bb, "Noise", 0); // TODO unsingedInt(:255)
	blobmsg_add_string(bb, "InitiatedBy", "TODO");

	/* Device.WiFi.DataElements.DisassociationEvent.Disassociated.DisassociationLinkStats.{i} */
	arr = blobmsg_open_array(bb, "DisassociationLinkStats");
	for (i = 0; i < e->num_affiliated_sta; i++) {
		blobmsg_add_macaddr(bb, "MACAddress", e->affsta[i].macaddr);
		blobmsg_add_u32(bb, "BytesSent", e->affsta[i].tx_bytes);
		blobmsg_add_u32(bb, "BytesReceived", e->affsta[i].rx_bytes);
		blobmsg_add_u32(bb, "PacketsSent", e->affsta[i].tx_packets);
		blobmsg_add_u32(bb, "PacketsReceived", e->affsta[i].rx_packets);
		blobmsg_add_u32(bb, "ErrorsSent", e->affsta[i].tx_errors);
	}
	blobmsg_close_array(bb, arr); // DisassociationLinkStats
	blobmsg_close_table(bb, t1);

	return 0;
}

int decollector_event_dump(struct decollector_private *p, struct blob_buf *bb)
{
	struct wifi_network_device *dev = NULL;
	struct wifi_data_element *dm = p->dm;
	uint32_t num_total_disassoc = 0;
	uint32_t num_total_assoc = 0;
	uint32_t num_total_fail = 0;
	void *arr;


	if (!p->dm)
		return -1;

	list_for_each_entry(dev, &dm->network.devicelist, list) {
		num_total_assoc += dev->ev.num_assoc;
		num_total_disassoc += dev->ev.num_disassoc;
		num_total_fail += dev->ev.num_fail;
	}

	blobmsg_add_u32(bb, "num_association", num_total_assoc);
	blobmsg_add_u32(bb, "num_disassociation", num_total_disassoc);
	blobmsg_add_u32(bb, "num_failedconnection", num_total_fail);
	arr = blobmsg_open_array(bb, "notification");
	list_for_each_entry(dev, &dm->network.devicelist, list) {
		struct wifi_disassoc_event *eb = NULL;
		struct wifi_assoc_event *ea = NULL;

		list_for_each_entry(ea, &dev->ev.assoclist, list) {
			decollector_dump_assoc_event(ea, bb);
		}

		list_for_each_entry(eb, &dev->ev.disassoclist, list) {
			decollector_dump_disassoc_event(eb, bb);
		}
	}
	blobmsg_close_array(bb, arr);

	return 0;
}

/* notify all non-pending events */
int decollector_notify_events(struct decollector_private *p, struct wifi_network_device *dev)
{
	struct wifi_sta_events *ev = &dev->ev;
	struct wifi_disassoc_event *eb = NULL;
	struct wifi_assoc_event *ea = NULL;


	if (!!(ev->pending & WIFI_STA_EV_ASSOC)) {
		int reset_notify = 1;

		list_for_each_entry_reverse(ea, &ev->assoclist, list) {
			struct blob_buf bb = {0};

			if (ea->delivered)
				continue;

			if (ea->pending) {
				reset_notify = 0;
				continue;
			}

			ea->delivered = 1;
			blob_buf_init(&bb, 0);
			decollector_dump_assoc_event(ea, &bb);
			ubus_send_event(p->ctx, "wifi.dataelements.Associated", bb.head);
			blob_buf_free(&bb);

#ifdef DECOLLECTOR_BUILD_TR181_PLUGIN
			// Refresh References Data Base
			bbfdm_refresh_references(BBFDM_BOTH, "bbfdm."SERVICE_NAME);

			// Send Device.WiFi.DataElements.AssociationEvent.Associated! event
			bbfdm_send_WiFiDataElementsAssociationEvent_Associated_event(ea);
#endif
		}

		if (reset_notify)
			ev->pending &= ~ WIFI_STA_EV_ASSOC;
	}

	if (!!(ev->pending & WIFI_STA_EV_DISASSOC)) {
		int reset_notify = 1;

		list_for_each_entry_reverse(eb, &ev->disassoclist, list) {
			struct blob_buf bb = {0};

			if (eb->delivered)
				continue;

			if (eb->pending) {
				reset_notify = 0;
				continue;
			}

			eb->delivered = 1;
			blob_buf_init(&bb, 0);
			decollector_dump_disassoc_event(eb, &bb);
			ubus_send_event(p->ctx, "wifi.dataelements.Disassociated", bb.head);
			blob_buf_free(&bb);

#ifdef DECOLLECTOR_BUILD_TR181_PLUGIN
			// Refresh References Data Base
			bbfdm_refresh_references(BBFDM_BOTH, "bbfdm."SERVICE_NAME);

			// Send Device.WiFi.DataElements.DisassociationEvent.Disassociated! event
			bbfdm_send_WiFiDataElementsAssociationEvent_Disassociated_event(eb);
#endif
		}

		if (reset_notify)
			ev->pending &= ~ WIFI_STA_EV_DISASSOC;
	}

	/* Free up the oldest entries, when either max-count reached, or
	 * list has too old entries.
	 */
	//TODO

	return 0;
}

