/*
 * topology.h - builds topology of the easymesh devices.
 *
 * Copyright (C) 2020-2024 IOPSYS Software Solutions AB. All rights reserved.
 * Copyright (C) 2025 Genexis AB.
 *
 * See LICENSE file for source code license information.
 *
 */

#include "topology.h"
#include <1905_tlvs.h>
#include <libubox/list.h>
#include <easy/easy.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include "utils/debug.h"
#include "utils/utils.h"
#include "cntlr_ubus.h"
#include "cntlr_events.h"

static bool is_wifi(uint16_t media_type)
{
	return (media_type >> 8) == 1;
}

void init_topology(struct bh_topology_data *topology)
{
	INIT_LIST_HEAD(&topology->dev_list);
	topology->num_devs = 0;
	topology->valid = false;
}

void free_topology(struct bh_topology_data *topology)
{
	topology_remove_all_devices(topology);
}

bool is_topology_valid(struct bh_topology_data *topology)
{
	return topology->valid;
}

uint32_t topology_num_devs(struct bh_topology_data *topology)
{
	return topology->num_devs;
}

struct bh_topology_dev *topology_find_device(struct bh_topology_data *topology, const uint8_t *al_macaddr)
{
	struct bh_topology_dev *bh_topo_dev = NULL;

	list_for_each_entry(bh_topo_dev, &topology->dev_list, list) {
		if (hwaddr_equal(bh_topo_dev->al_macaddr, al_macaddr))
			return bh_topo_dev;
	}

	return NULL;
}

struct bh_topology_dev *topology_add_device(struct bh_topology_data *topology, const uint8_t *al_macaddr)
{
	struct bh_topology_dev *bh_topo_dev =
		calloc(1, sizeof(struct bh_topology_dev));

	if (!bh_topo_dev)
		return NULL;

	memcpy(bh_topo_dev->al_macaddr, al_macaddr, 6);
	bh_topo_dev->bh.depth = UNKNOWN_TREE_LEVEL;

	memset(bh_topo_dev->prev_parent_aladdr, 0, 6);
	memset(bh_topo_dev->prev_iface_mac, 0, 6);

	list_add(&bh_topo_dev->list, &topology->dev_list);

	topology->num_devs++;
	topology->valid = false;

	return bh_topo_dev;
}

void topology_remove_device(struct bh_topology_data *topology, const uint8_t *al_macaddr)
{
	struct bh_topology_dev *bh_topo_dev = NULL, *bh_topo_dev_tmp;

	list_for_each_entry_safe(bh_topo_dev, bh_topo_dev_tmp, &topology->dev_list, list) {
		if (hwaddr_equal(bh_topo_dev->al_macaddr, al_macaddr)) {
			list_del(&bh_topo_dev->list);
			free(bh_topo_dev);

			topology->num_devs--;
			topology->valid = false;
		}
	}
}

void topology_remove_all_devices(struct bh_topology_data *topology)
{
	/* cppcheck-suppress uninitvar */
	list_flush(&topology->dev_list, struct bh_topology_dev, list);

	topology->num_devs = 0;
	topology->valid = false;
}

bool has_interface_info_changed(const struct tlv_device_info *tlv_dev_info,
				const struct bh_topology_dev *bh_topo_dev)
{
	const uint8_t *tlv_iface_p = (uint8_t *)tlv_dev_info->interface;
	size_t tlv_iface_offset;
	int i;

	if (bh_topo_dev->number_of_interfaces != tlv_dev_info->num_interface)
		return true;

	tlv_iface_offset = 0;
	for (i = 0; i < bh_topo_dev->number_of_interfaces; ++i) {
		const struct local_interface *interface =
			(struct local_interface *)(tlv_iface_p + tlv_iface_offset);

		if (!hwaddr_equal(bh_topo_dev->ifaces[i].macaddr, interface->macaddr))
			return true;

		tlv_iface_offset += sizeof(struct local_interface) +
				    interface->sizeof_mediainfo;
	}

	return false;
}

void copy_interface_info_from_tlv(struct bh_topology_data *topology,
				  const struct tlv_device_info *tlv_dev_info,
				  struct bh_topology_dev *bh_topo_dev)
{
	const uint8_t *tlv_iface_p = (uint8_t *)tlv_dev_info->interface;
	size_t tlv_iface_offset;
	int i;

	topology->valid = false;

	/* Clear device old iface data, including neighbors info */
	memset(bh_topo_dev->ifaces, 0, sizeof(bh_topo_dev->ifaces));

	bh_topo_dev->num_of_ifaces_with_neighbors = 0;
	bh_topo_dev->number_of_interfaces = tlv_dev_info->num_interface;

	if (tlv_dev_info->num_interface > IFACE_MAX_NUM) {
		warn("%s: Currently max %d interfaces supported!\n", __func__, IFACE_MAX_NUM);
		bh_topo_dev->number_of_interfaces = IFACE_MAX_NUM;
	}

	tlv_iface_offset = 0;
	for (i = 0; i < bh_topo_dev->number_of_interfaces; ++i) {
		const struct local_interface *interface =
			(struct local_interface *)(tlv_iface_p + tlv_iface_offset);

		memcpy(bh_topo_dev->ifaces[i].macaddr, interface->macaddr, 6);
		bh_topo_dev->ifaces[i].media_type =
			BUF_GET_BE16(interface->mediatype);

		tlv_iface_offset += sizeof(struct local_interface) +
				    interface->sizeof_mediainfo;
	}
}

static bool are_neighbor_lists_the_same(const struct local_iface *iface,
					const struct tlv_1905neighbor *tlv_1905neighbor,
					uint16_t tlv_length)
{
	const uint8_t tlv_neighbors_num =
		(tlv_length - sizeof(tlv_1905neighbor->local_macaddr)) /
		sizeof(struct i1905_neighbor);
	uint8_t i, j;

	if (iface->number_of_neighbors != tlv_neighbors_num)
		return false;

	for (i = 0; i < tlv_neighbors_num; ++i) {
		bool found = false;

		for (j = 0; j < iface->number_of_neighbors; ++j) {
			if (hwaddr_equal(tlv_1905neighbor->nbr[i].aladdr,
					 &iface->neighbors_al_macs[j][0])) {
				found = true;
				break;
			}
		}

		if (!found)
			return false;
	}

	return true;
}

bool has_neighbor_info_changed(const struct tlv_1905neighbor **neighbor_tlvs,
			       const uint16_t *tlv_lengths,
			       uint8_t tlv_number,
			       const struct bh_topology_dev *bh_topo_dev)
{
	uint8_t i;

	/* Every TLV provides neighbors for single local interface. */
	if (bh_topo_dev->num_of_ifaces_with_neighbors != tlv_number)
		return true;

	for (i = 0; i < tlv_number; ++i) {
		const struct tlv_1905neighbor *neighbor_tlv = neighbor_tlvs[i];
		uint8_t j;
		bool found = false;

		for (j = 0; j < bh_topo_dev->number_of_interfaces; ++j) {
			bool same_mac = hwaddr_equal(bh_topo_dev->ifaces[j].macaddr,
					 neighbor_tlv->local_macaddr);

			if (same_mac && are_neighbor_lists_the_same(
						&bh_topo_dev->ifaces[j],
						neighbor_tlv, tlv_lengths[i])) {

				found = true;
				break;
			}
		}

		if (!found)
			return true;
	}

	return false;

}

void copy_neighbor_info_from_tlvs(struct bh_topology_data *topology,
				  const struct tlv_1905neighbor **neighbor_tlvs,
				  const uint16_t *tlv_lengths,
				  uint8_t tlv_number,
				  struct bh_topology_dev *bh_topo_dev)
{
	uint8_t i;

	topology->valid = false;
	bh_topo_dev->num_of_ifaces_with_neighbors = tlv_number;

	/* Clear device old/previous neighbors info */
	for (i = 0; i < bh_topo_dev->number_of_interfaces; ++i) {
		if (bh_topo_dev->ifaces[i].number_of_neighbors != 0) {
			bh_topo_dev->ifaces[i].number_of_neighbors = 0;
			memset(bh_topo_dev->ifaces[i].neighbors_al_macs, 0,
			       sizeof(bh_topo_dev->ifaces[i].neighbors_al_macs));
		}

	}

	for (i = 0; i < tlv_number; ++i) {
		const struct tlv_1905neighbor *neighbor_tlv = neighbor_tlvs[i];
		const uint8_t neighbors_num =
			(tlv_lengths[i] - sizeof(neighbor_tlv->local_macaddr)) /
			sizeof(struct i1905_neighbor);
		struct local_iface *local_iface = NULL;
		uint8_t j;

		for (j = 0; j < bh_topo_dev->number_of_interfaces; ++j) {
			/*
			 * Because different local physical interfaces can use the same MAC
			 * address, find a one with matching MAC and no neighbors added yet.
			 */
			if (hwaddr_equal(bh_topo_dev->ifaces[j].macaddr,
						neighbor_tlv->local_macaddr) &&
				bh_topo_dev->ifaces[j].number_of_neighbors == 0) {

				local_iface = &bh_topo_dev->ifaces[j];

				if (bh_topo_dev->ifaces[j].media_type != 0xFFFF)
					break;
			}
		}

		if (local_iface) {
			uint8_t k;

			dbg("%s: adding neighbors for local_iface[%u].macaddr: " MACFMT "\n",
				__func__, j, MAC2STR(local_iface->macaddr));

			local_iface->number_of_neighbors = neighbors_num;

			for (k = 0; k < neighbors_num && k < NEIGHBORS_MAX_NUM; ++k) {
				dbg("%s:   neighbor[%u].aladdr: " MACFMT "\n",
					__func__, k, MAC2STR(neighbor_tlv->nbr[k].aladdr));

				memcpy(&local_iface->neighbors_al_macs[k][0],
					neighbor_tlv->nbr[k].aladdr, 6);
			}

		} else {
			warn("%s: no iface found, adding neighbors failed for local_macaddr: " MACFMT "\n",
				__func__, MAC2STR(neighbor_tlv->local_macaddr));
		}
	}

}

void set_bh_toplogy_response_timestamp(struct bh_topology_dev *bh_topo_dev)
{
	timestamp_update(&bh_topo_dev->last_topo_response);
}

static void clear_backhaul_tree_relations(struct list_head *bh_topo_dev_list)
{
	struct bh_topology_dev *bh_topo_dev = NULL;

	list_for_each_entry(bh_topo_dev, bh_topo_dev_list, list) {
		bh_topo_dev->bh =
			(struct backhaul_info){ UNKNOWN_TREE_LEVEL, 0, NULL, NULL, NULL };
	}
}

static const struct local_iface *
find_neighbor_local_iface(const struct bh_topology_dev *next_level_neighbor,
			  const uint8_t *parent_al_macaddr)
{
	int i, j;

	for (i = 0; i < next_level_neighbor->number_of_interfaces; ++i) {
		const struct local_iface *iface = &next_level_neighbor->ifaces[i];

		for (j = 0; j < iface->number_of_neighbors; ++j) {
			if (hwaddr_equal(&iface->neighbors_al_macs[j][0], parent_al_macaddr))
				return iface;
		}
	}

	return NULL;
}

static bool set_device_child_neighbors(struct bh_topology_data *topology,
				       const struct bh_topology_dev *parent_device,
				       struct list_head *bh_topo_dev_list)
{
	int i, j;
	const int8_t child_level = parent_device->bh.depth + 1;
	bool has_child_neighbor = false;

	for (i = 0; i < parent_device->number_of_interfaces; ++i) {
		const struct local_iface *parent_iface = &parent_device->ifaces[i];

		for (j = 0; j < parent_iface->number_of_neighbors; ++j) {

			struct bh_topology_dev *child_neighbor_dev =
				topology_find_device(topology, &parent_iface->neighbors_al_macs[j][0]);

			if (child_neighbor_dev &&
			    child_neighbor_dev->bh.depth == UNKNOWN_TREE_LEVEL) {
				const struct local_iface *child_iface;

				child_neighbor_dev->bh.parent = parent_device;
				child_neighbor_dev->bh.parent_iface = parent_iface;
				child_neighbor_dev->bh.depth = child_level;
				child_neighbor_dev->bh.wifi_hops_from_root =
					parent_device->bh.wifi_hops_from_root;
				if (is_wifi(parent_iface->media_type))
					child_neighbor_dev->bh.wifi_hops_from_root++;

				child_iface = find_neighbor_local_iface(child_neighbor_dev,
									parent_device->al_macaddr);

				if (!child_iface) {
					dbg("BH:%s: child doens't know about parent neighbor.\n", __func__);
				}

				child_neighbor_dev->bh.own_iface = child_iface;

				if (child_iface && (is_wifi(child_iface->media_type) != is_wifi(parent_iface->media_type))) {
					dbg("BH:%s: child and parent iface media type differs, parent: %s, child: %s\n",
					    i1905_media_type_to_str(parent_iface->media_type),
					    i1905_media_type_to_str(child_iface->media_type),
					    __func__);
				}

				dbg("BH: %s New parent-child link set:\n", __func__);
				//dbg_dump_bh_topo_link(child_neighbor_dev, INDENT_LVL_0);

				has_child_neighbor = true;
			}
		}
	}

	return has_child_neighbor;
}

static bool set_next_level_neighbors(struct bh_topology_data *topology, int8_t current_level,
				     struct list_head *bh_topo_dev_list)
{
	struct bh_topology_dev *bh_topo_dev = NULL;
	bool new_level_discovered = false;

	list_for_each_entry(bh_topo_dev, bh_topo_dev_list, list) {
		if (bh_topo_dev->bh.depth == current_level) {
			bool has_next_level =
				set_device_child_neighbors(topology, bh_topo_dev, bh_topo_dev_list);

			new_level_discovered = new_level_discovered || has_next_level;
		}
	}

	dbg("BH: %s: current_level: %d, new_level_discovered: %d\n",
	    __func__, current_level, new_level_discovered);

	return new_level_discovered;
}

struct bh_topology_dev *build_local_controller_root(struct controller *c,
						    struct bh_topology_data *topology,
						    const uint8_t *ctrl_al_macaddr)
{
	struct local_iface ifaces[IFACE_MAX_NUM];
	struct bh_topology_dev *root;
	int num_ifaces;
	int i;

	num_ifaces = cntlr_get_local_ifaces(c, ifaces, IFACE_MAX_NUM);
	if (num_ifaces == 0) {
		warn("%s: no local interfaces from ieee1905 info for " MACFMT "\n",
		     __func__, MAC2STR(ctrl_al_macaddr));
		return NULL;
	}

	root = topology_find_device(topology, ctrl_al_macaddr);
	if (!root)
		root = topology_add_device(topology, ctrl_al_macaddr);

	if (!root) {
		err("%s: failed to add local controller root " MACFMT "\n",
		    __func__, MAC2STR(ctrl_al_macaddr));
		return NULL;
	}

	memset(root->ifaces, 0, sizeof(root->ifaces));
	root->number_of_interfaces = num_ifaces;
	root->num_of_ifaces_with_neighbors = 0;

	for (i = 0; i < num_ifaces; i++) {
		root->ifaces[i] = ifaces[i];
		if (ifaces[i].number_of_neighbors > 0)
			root->num_of_ifaces_with_neighbors++;
	}

	dbg("BH: %s: derived local controller root " MACFMT " with %d interface(s)\n",
	    __func__, MAC2STR(ctrl_al_macaddr), num_ifaces);

	return root;
}

void topology_build_tree(struct controller *c, struct bh_topology_data *topology, const uint8_t *ctrl_al_macaddr)
{
	trace("%s: --->\n", __func__);

	struct bh_topology_dev *ctrl_dev = NULL;
	struct bh_topology_dev *dev = NULL;
	bool new_level_discovered = false;
	int8_t current_level;

	clear_backhaul_tree_relations(&topology->dev_list);

	/* Mark controller as root node in BH tree (depth = 0) */
	ctrl_dev = topology_find_device(topology, ctrl_al_macaddr);
	if (!ctrl_dev) {
		err("%s: cannot find controller (root ndode) and build tree.\n",
			__func__);
		return;
	}
	ctrl_dev->bh.depth = 0;
	ctrl_dev->bh.wifi_hops_from_root = 0;

	current_level = ctrl_dev->bh.depth;

	do {
		new_level_discovered =
			set_next_level_neighbors(topology, current_level, &topology->dev_list);

		++current_level;
	} while (new_level_discovered);

	topology->valid = true;

	dbg("%s: Backhaul topology tree built. BH topo devs num: %d, cntlr: " MACFMT "\n",
		__func__,  topology->num_devs, MAC2STR(ctrl_al_macaddr));

	dbg_dump_bh_topo_devs(&topology->dev_list, INDENT_LVL_0);

	list_for_each_entry(dev, &topology->dev_list, list) {
		const uint8_t *downstream_mac = NULL;
		const uint8_t *upstream_mac = NULL;
		const uint8_t *upstream_al = NULL;
		bool topology_changed = false;
		bool first_event = false;
		static const uint8_t zero_mac[6] = {0};
		char ev_data[512] = {0};

		if (memcmp(dev->al_macaddr, ctrl_al_macaddr, 6) == 0)
			continue;

		downstream_mac = (dev->bh.own_iface) ? dev->bh.own_iface->macaddr : dev->al_macaddr;
		upstream_mac = (dev->bh.parent_iface) ? dev->bh.parent_iface->macaddr : ctrl_al_macaddr;
		upstream_al = (dev->bh.parent) ? dev->bh.parent->al_macaddr : ctrl_al_macaddr;

		if (!memcmp(dev->prev_parent_aladdr, zero_mac, 6) &&
				!memcmp(dev->prev_iface_mac, zero_mac, 6)) {
			first_event = true;
		}

		if (memcmp(dev->prev_parent_aladdr, upstream_al, 6) != 0 ||
				memcmp(dev->prev_iface_mac, downstream_mac, 6) != 0) {
			topology_changed = true;
		}

		if (!topology_changed)
			continue;

		memcpy(dev->prev_parent_aladdr, upstream_al, 6);
		memcpy(dev->prev_iface_mac, downstream_mac, 6);

		snprintf(ev_data, sizeof(ev_data), "{ \"agent\": \"" MACFMT "\", "
				"\"backhaulsta\":\"" MACFMT "\", "
				"\"backhaulmac\":\"" MACFMT "\", "
				"\"backhaulalid\":\"" MACFMT "\"}", MAC2STR(dev->al_macaddr),
				MAC2STR(downstream_mac), MAC2STR(upstream_mac), MAC2STR(upstream_al));
		if (first_event)
			cntlr_notify_event(c, CNTLR_EVENT_AGENT_ONBOARDED, ev_data);
		else
			cntlr_notify_event(c, CNTLR_EVENT_TOPOLOGY_CHANGE, ev_data);
	}
}

#define BH_TOPO_PREFIX "BH:"

static const char *get_prefix(enum dbg_bh_topo_indent_lvl indent_level)
{
	switch (indent_level) {
	case INDENT_LVL_0: return BH_TOPO_PREFIX;
	case INDENT_LVL_1: return BH_TOPO_PREFIX"  ";
	case INDENT_LVL_2: return BH_TOPO_PREFIX"    ";
	case INDENT_LVL_3: return BH_TOPO_PREFIX"      ";
	default:           return BH_TOPO_PREFIX"        ";
	}
}

const char *i1905_media_type_to_str(uint16_t media_type)
{
	switch (media_type) {
	case 0x0000:  return "Ethernet (802_3U_FAST_ETHERNET)";
	case 0x0001:  return "Ethernet (802_3AB_GIGABIT_ETHERNET)";
#ifdef IEEE1905_ETH_MEDIA_EXTENSION
	case 0x0002:  return "Ethernet (802_3BZ_2_5G_ETHERNET)";
	case 0x0003:  return "Ethernet (802_3BZ_5G_ETHERNET)";
	case 0x0004:  return "Ethernet (802_3AE_10G_ETHERNET)";
#endif
	case 0x0100:  return "Wi-Fi (802_11B_2_4_GHZ)";
	case 0x0101:  return "Wi-Fi (802_11G_2_4_GHZ)";
	case 0x0102:  return "Wi-Fi (802_11A_5_GHZ)";
	case 0x0103:  return "Wi-Fi (802_11N_2_4_GHZ )";
	case 0x0104:  return "Wi-Fi (802_11N_5_GHZ)";
	case 0x0105:  return "Wi-Fi (802_11AC_5_GHZ)";
	case 0x0106:  return "Wi-Fi (802_11AD_60_GHZ)";
	case 0x0107:  return "Wi-Fi (802_11AF_WHITESPACE)";
	case 0x0108:  return "Wi-Fi (802_11AX)";
	case 0x0109:  return "Wi-Fi (802_11BE)";

	default: return "Other";
	}
}

void dbg_dump_bh_topo_backhaul_info(const struct backhaul_info *bh,
				    enum dbg_bh_topo_indent_lvl indent_lvl)
{
	const char *pref = get_prefix(indent_lvl);

	dbg("%s backhaul_info:\n", pref);

	if (bh->depth == UNKNOWN_TREE_LEVEL) {
		dbg("%s .depth: %s\n", pref, "UNKNOWN_TREE_LEVEL");
	} else {
		dbg("%s .depth: %d\n", pref, bh->depth);
		dbg("%s .wifi_hops_from_root: %d\n", pref, bh->wifi_hops_from_root);
	}

	if (bh->parent) {
		dbg("%s .parent: " MACFMT "\n", pref,
		    MAC2STR(bh->parent->al_macaddr));
	}

	if (bh->parent_iface) {
		dbg("%s .parent_iface: " MACFMT "\n", pref,
		    MAC2STR(bh->parent_iface->macaddr));
	}

	if (bh->own_iface) {
		dbg("%s .own_iface: " MACFMT "\n", pref,
		    MAC2STR(bh->own_iface->macaddr));
	}
}

void dbg_dump_bh_topo_local_iface(const struct local_iface *local_iface,
				  enum dbg_bh_topo_indent_lvl indent_lvl)
{
	const char *pref = get_prefix(indent_lvl);
	int i;

	dbg("%s local_iface:\n", pref);
	dbg("%s .macaddr: " MACFMT "\n", pref, MAC2STR(local_iface->macaddr));
	dbg("%s .media_type: %s\n", pref,
	    i1905_media_type_to_str(local_iface->media_type));
	dbg("%s .number_of_neighbors: %u\n", pref, local_iface->number_of_neighbors);

	for (i = 0; i < local_iface->number_of_neighbors; ++i) {
		dbg("%s .neigh_al_mac[%d]: " MACFMT "\n",
			pref, i, MAC2STR(&local_iface->neighbors_al_macs[i][0]));
	}
}

void dbg_dump_bh_topo_dev(const struct bh_topology_dev *dev,
			  enum dbg_bh_topo_indent_lvl indent_lvl)
{
	const char *pref = get_prefix(indent_lvl);
	int i;

	dbg("%s bh_topology_dev:\n", pref);
	dbg("%s .last_topo_response: %u\n", pref, timestamp_elapsed_sec(&dev->last_topo_response));
	dbg("%s .al_macaddr: " MACFMT "\n", pref, MAC2STR(dev->al_macaddr));
	dbg("%s .number_of_interfaces: %d\n", pref, dev->number_of_interfaces);
	dbg("%s .num_of_ifaces_with_neighbors: %d\n", pref, dev->num_of_ifaces_with_neighbors);

	for (i = 0; i < dev->number_of_interfaces; ++i) {
		dbg("%s .iface[%d]:\n", pref, i);
		dbg_dump_bh_topo_local_iface(&dev->ifaces[i], indent_lvl + 1);
	}

	dbg("%s .bh:\n", pref);
	dbg_dump_bh_topo_backhaul_info(&dev->bh, indent_lvl + 1);

}

void dbg_dump_bh_topo_devs(const struct list_head *devlist,
			   enum dbg_bh_topo_indent_lvl indent_lvl)
{
	const char *pref = get_prefix(indent_lvl);
	const struct bh_topology_dev *dev = NULL;
	int i = 0;

	dbg("%s =============== %s ===============\n", pref, __func__);

	list_for_each_entry(dev, devlist, list) {
		dbg("%s bh_topo_dev[%d]:\n", pref, i++);
		dbg_dump_bh_topo_dev(dev, ++indent_lvl);

	}

	dbg("%s ============ END %s ==============\n", pref, __func__);
}

#if 0
void dbg_dump_bh_topo_link(const struct bh_topology_dev *dev,
			   enum dbg_bh_topo_indent_lvl indent_lvl)
{
	const char *pref = get_prefix(indent_lvl);
	const struct bh_topology_dev *parent_dev = dev->bh.parent;
	const struct local_iface *parent_iface = dev->bh.parent_iface;
	const struct local_iface *own_iface = dev->bh.own_iface;

	if (dev->bh.depth == UNKNOWN_TREE_LEVEL) {
		dbg("%s child level: %s\n", pref, "UNKNOWN_TREE_LEVEL");
	} else {
		dbg("%s child level: %d\n", pref, dev->bh.depth);
	}

	dbg("%s child wifi_hops_from_root: %d\n", pref, dev->bh.wifi_hops_from_root);
	dbg("%s child almacaddr: " MACFMT "\n", pref, MAC2STR(dev->al_macaddr));

	if (own_iface) {
		dbg("%s child iface: " MACFMT "\n", pref, MAC2STR(own_iface->macaddr));
	}

	if (parent_dev) {
		dbg("%s parent level: %d\n", pref, parent_dev->bh.depth);
		dbg("%s parent almacaddr: " MACFMT "\n", pref, MAC2STR(parent_dev->al_macaddr));
	}

	if (parent_iface) {
		dbg("%s parent iface: " MACFMT "\n", pref, MAC2STR(parent_iface->macaddr));
		dbg("%s media: %s\n", pref, i1905_media_type_to_str(parent_iface->media_type));
	}
}
#endif
