/*
 * topology.h - topology of 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.
 *
 */

#ifndef BACKHAUL_TOPOLOGY_H
#define BACKHAUL_TOPOLOGY_H

#include <libubox/list.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>

struct tlv_1905neighbor;
struct tlv_device_info;
struct controller;

#define IFACE_MAX_NUM 16
#define NEIGHBORS_MAX_NUM 16

struct local_iface {
	uint8_t macaddr[6];
	uint16_t media_type;
	uint8_t number_of_neighbors;
	uint8_t neighbors_al_macs[NEIGHBORS_MAX_NUM][6];
};

 /* When populated BH link defined as in TR-181:
  * BHDevALMac/BHLocalMac() <-LinkType -< LocalMac
  * can be read. Above corresponds to:
  * parent/parent_iface <--own_iface->media_type-< own_iface
  */
#define UNKNOWN_TREE_LEVEL -1
struct backhaul_info {
	int8_t depth;			/** total number of BH links from root node */
	uint8_t wifi_hops_from_root;	/** number of Wi-Fi BH links from root node */
	const struct bh_topology_dev *parent;
	const struct local_iface *parent_iface;
	const struct local_iface *own_iface;
};

/** Represents single device and its relation to neighbors in BH tree. */
struct bh_topology_dev {
	struct list_head list;
	struct timespec last_topo_response;

	uint8_t al_macaddr[6];
	uint8_t number_of_interfaces;
	uint8_t num_of_ifaces_with_neighbors;
	struct local_iface ifaces[IFACE_MAX_NUM];
	struct backhaul_info bh;
	uint8_t prev_parent_aladdr[6];
	uint8_t prev_iface_mac[6];
};

/** Used to store all discovered EasyMesh devices and model tree topology. */
struct bh_topology_data {
	struct list_head dev_list; /* list of struct bh_topology_dev */
	uint32_t num_devs;
	bool valid;
};

/** Initializes internal structures.
 *  Has to be called once before any other functions are called.
 */
void init_topology(struct bh_topology_data *topology);

/** Releases all data and internal structures. */
void free_topology(struct bh_topology_data *topology);

/** Returns tree if backhaul topology tree was already build and is still valid.
 *
 *  It's undefined behaviour to read interface, neighbor and backhaul info
 *  of any bh_topology_dev when this function returns false.
 */
bool is_topology_valid(struct bh_topology_data *topology);

/** Number of devices in backhaul topology tree. */
uint32_t topology_num_devs(struct bh_topology_data *topology);

/** Returns backhaul topology device if exists in the model, NULL otherwise. */
struct bh_topology_dev *topology_find_device(struct bh_topology_data *topology, const uint8_t *al_macaddr);

/** Adds backhaul topology device to the model, invalidates backhaul tree. */
struct bh_topology_dev *topology_add_device(struct bh_topology_data *topology, const uint8_t *al_macaddr);

/** Removes backhaul topology device from the model, invalidates backhaul tree. */
void topology_remove_device(struct bh_topology_data *topology, const uint8_t *al_macaddr);

/** Removes all backhaul topology devices from the model, invalidates backhaul tree. */
void topology_remove_all_devices(struct bh_topology_data *topology);

/**
 * Returns true if interface information provided with tlv_device_info differs
 * from data already stored in bh_topology_dev (model).
 * To reduce time complexity, it's assumed interfaces received in TLV are always ordered
 * the same way. Also only number of interfaces and their MAC addresses are compared.
 */
bool has_interface_info_changed(const struct tlv_device_info *tlv_dev_info,
				const struct bh_topology_dev *bh_topo_dev);
/**
 * Copies interface details from tlv_device_info into bh_topology_dev.
 * Invalidates backhaul tree.
 */
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);

/**
 * Returns true if neighbor information provided with 1905 neighbor TLVs differs
 * from data already stored in bh_topology_dev (model).
 */
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);

/**
 * Copies neighbors details from 1905 neighbor TLVs into bh_topology_dev.
 * Invalidates backhaul tree.
 */
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);

/**
 * Shall be called each time device provides topology response.
 */
void set_bh_toplogy_response_timestamp(struct bh_topology_dev *bh_topo_dev);

/**
 * Builds backhaul topology tree from unordered list of bh_topology_dev structs
 * by setting backhaul_info fields.
 *
 * Controller is always root node in tree. Edges between tree nodes represent
 * BH links, parent nodes provides BH links to child node - each child node
 * can have only one parent (be connected over one BH link).
 *
 * After calling it, is_bh_topology_valid() will return true, until next
 * input data change (device add/remove, interface or neighbor info change.)
 */
void topology_build_tree(struct controller *c, struct bh_topology_data *topology, const uint8_t *ctrl_al_macaddr);

enum dbg_bh_topo_indent_lvl {
	INDENT_LVL_0 = 0,
	INDENT_LVL_1 = 1,
	INDENT_LVL_2 = 2,
	INDENT_LVL_3 = 3,
};

const char *i1905_media_type_to_str(uint16_t media_type);

void dbg_dump_bh_topo_backhaul_info(const struct backhaul_info *bh,
				    enum dbg_bh_topo_indent_lvl indent_lvl);

void dbg_dump_bh_topo_local_iface(const struct local_iface *local_iface,
				  enum dbg_bh_topo_indent_lvl indent_lvl);

void dbg_dump_bh_topo_dev(const struct bh_topology_dev *bh_topo_dev,
			  enum dbg_bh_topo_indent_lvl indent_lvl);

void dbg_dump_bh_topo_devs(const struct list_head *bh_topo_dev_list,
			   enum dbg_bh_topo_indent_lvl indent_lvl);

void dbg_dump_bh_topo_link(const struct bh_topology_dev *bh_topo_dev,
			   enum dbg_bh_topo_indent_lvl indent_lvl);


#endif /* BACKHAUL_TOPOLOGY_H */
