/*
 * cntlr.h - MAP controller header file
 *
 * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#ifndef CNTLR_H
#define CNTLR_H

#include <libubox/list.h>
#include <libubus.h>
#include <map_module.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "timer_impl.h"
#include <cmdu_ackq.h>
#include <wifidefs.h>

#include "son.h"
#include "topology.h"
#include "config.h"
#include "easy/hlist.h"
#include "mactable.h"
#include "utils/debug.h"
#include "wifi_dataelements.h"
#include "cntlr_events.h"
#include "1905_tlvs.h"

struct blob_attr;
struct cmdu_buff;
struct sta;
struct tlv_rx_linkmetric;
struct tlv_tx_linkmetric;


#ifndef LIMIT_STA_COUNT
#define LIMIT_STA_COUNT (100)
#endif

#define NODE_EXPIRE_TIME 65
#define METRIC_REP_INTERVAL 10

extern const char *ubus_socket;

typedef uint32_t object_t;
#define OBJECT_INVALID	((uint32_t)-1)

enum device_type {
	NON_IEEE1905,
	IEEE1905
};

struct cac_data {
	uint8_t radio[6];
	uint8_t opclass;
	uint8_t channel;
	uint8_t cac_method;
	uint8_t cac_action;
};

#define SCAN_REQ_MAX_NUM_RADIO 4
#define SCAN_REQ_MAX_NUM_OPCLASS 8
#define SCAN_REQ_MAX_NUM_CHAN 16
struct scan_req_data {
	bool is_fresh_scan;
	uint8_t num_radio;
	struct scan_req_radio {
		uint8_t radio_mac[6];
		uint8_t num_opclass;
		struct scan_req_opclass {
			uint8_t classid;
			uint8_t num_channel;
			uint8_t channels[SCAN_REQ_MAX_NUM_CHAN];
		} opclasses[SCAN_REQ_MAX_NUM_OPCLASS];
	} radios[SCAN_REQ_MAX_NUM_RADIO];
};

struct bcn_meas_element {
	uint8_t tag_number;          /* 0x27 = Measurement Report element */
	uint8_t tag_length;
	uint8_t meas_token;
	uint8_t meas_report_mode;
	uint8_t meas_report_type;    /* 0x05 = Beacon Report */
	uint8_t op_class;
	uint8_t channel;
	uint8_t start_time[8];       /* measuring STA's TSF timer */
	uint8_t duration[2];
	uint8_t frame_info;
	uint8_t rcpi;
	uint8_t rsni;
	uint8_t bssid[6];
	uint8_t antena_id;
	uint8_t tsf[4];
	/* optional subelements */
	uint8_t frame[];
};

/* represents an outstanding Beacon metrics request for STA to an Agent */
struct bcnreq {
	uint8_t sta_mac[6];
	uint8_t agent_mac[6];
	time_t req_time;	/* time when the request is send */
	int num_channel;	/* num channel-report requested */
	struct list_head list;
};

/* represents fBSS or bBSS interface */
struct netif_iface {
	struct node *agent;
	struct netif_radio *radio;
	struct wifi_bss_element *bss;
	enum wifi_band band;
	struct list_head list;
};

#define MAX_NUM_RADIO	8	/* maximum number of radios per node */
#define MAX_NUM_BSSID	16	/* maximum number of BSSs per radio */

struct netif_radio {
	struct node *agent;
	struct wifi_radio_element *radio_el;
	struct list_head iflist;	/** list of struct netif_iface */
	struct list_head list;
};

enum nodetype {
	NODE_WIFI_EXTENDER,
	NODE_WIFI_REPEATER,
	NODE_ETH_EXTENDER,
};


enum uplink_type {
	UL_UNSPEC,
	UL_ETH,
	UL_WIFI,
	UL_DSL,
};

struct uobj_struct {
	object_t id;
	int (*req)(int argc, char **argv);
	void (*resp)(struct ubus_request *r, int t, struct blob_attr *m);
};

/* struct node - maps to a 1905 device */
struct node {
	uint8_t almacaddr[6];
	enum nodetype type;
	int depth;                         /** >= 0 or -1 for unknown */
	int ul_type;                       /** uplink type */
	uint32_t est_thput_ul;		   /* estimated l3 throughput achievable in uplink dir */
	uint32_t est_thput_dl;		   /* estimated l3 throughput achievable in downlink dir */
	bool scan_supported;               /** whether scanning supported */
	struct controller *cntlr;

	/* Link metrics from this node's perspective (as downstream bSTA) */
	uint8_t upstream_bssid[6];         /** MAC of upstream bBSS this node connects through */
	struct tx_link_info tx_metrics;    /** For bSTA: transmit to upstream bBSS */
	struct rx_link_info rx_metrics;    /** For bSTA: receive from upstream bBSS */
#define MAX_UOBJECTS	8
	atimer_t refresh_timer;
	struct ubus_event_handler evh;
	//struct agent_policy *ap;
	struct node_policy *np;
	uint8_t ap_cap;
	bool is_agent;
	uint8_t map_profile;		   /** profile info of agent node */
	uint32_t sta_count;

#if (EASYMESH_VERSION >= 6)
	int max_mld_num;
	int max_aplink_num;
	int max_bstalink_num;
	int tid_link_caps;

	uint16_t apmldconf_mid;
	struct timespec	last_apmld_ack;	  /** timestamp of most-recent AP MLD Config request ACK received by the agent */

	uint16_t bstamldconf_mid;
	struct timespec	last_bstamld_ack; /** timestamp of most-recent BSTA MLD Config request ACK received by the agent */
#endif

	struct timespec last_tsp_seen;     /** last seen tsp */
	struct timespec	last_config;       /** timestamp of most-recent config sent to agent */
	struct timespec last_cmdu;         /** timestamp of last CMDU received from agent */

	struct list_head radiolist;        /** list of netif_radio */
	struct list_head stalist;          /** list of sta */
	struct list_head list;
};

static inline bool node_is_agent(const struct node *n)
{
	return n->is_agent;
}

#define node_for_each_bss(p, r, n)				\
		list_for_each_entry(r, &n->radiolist, list)	\
			list_for_each_entry(p, &r->iflist, list)


#define NODE_STATS_INTERVAL		30000       /** 30 secs */

enum cntlr_state {
	CNTLR_INIT, /* while initializing, only look for ACS answer */
	CNTLR_IDLE, /* TODO: while idle, do nothing in a loop till awakened */
	CNTLR_START /* normal */
};

#ifndef STA_HASHTABLE_SIZE	// TODO: remove
#define STA_HASHTABLE_SIZE	256
#endif

struct controller {
	struct log_options log;
	uint64_t uptime; /* uptime in seconds */
	enum cntlr_state state;
	uint8_t almacaddr[6];
	void *comm;
	struct ubus_object obj;
	struct ubus_object obj_dbg; /* ubus debug object */
	struct ubus_context *ubus_ctx;
	struct ubus_event_handler evh;
	atimer_t heartbeat;
	atimer_t acs;
	int num_tx_links;
	int num_rx_links;
	int num_agents;
	struct list_head agentlist;     /* EasyMesh agents */
	int num_devs;
	struct list_head devlist;     /* devices not identified as agents */
	struct list_head bcnreqlist;    /* struct bcnreq list */
	atimer_t radar_timer;
	atimer_t discovery_timer;
	atimer_t start_timer;
	atimer_t signal_handler;
	atimer_t query_nodes;
	atimer_t son_timer;

	uint32_t num_sta;
	struct hlist_head sta_table[STA_HASHTABLE_SIZE];

	struct hlist_head mac_table[MAC_HASHTABLE_SIZE];

	struct controller_config cfg;
	struct cmdu_ackq cmdu_ack_q;

	/* Autoconfig */
	uint16_t mid_5g;
	uint16_t mid_2g;

	/* i1905.map registration */
	uint32_t map_oid;
	mapmodule_cmdu_mask_t cmdu_mask;
	void *subscriber;
	bool subscribed;

#define INFORM_STA_MAXNUM	64
	atimer_t steer_sched_timer;
	uint16_t inform_cmdu_type;
	uint32_t inform_sta_num;
	uint8_t inform_stalist[INFORM_STA_MAXNUM * 6];

	struct list_head sclist;	/* struct steer_control list */

	struct list_head plugins;	/* struct cntlr_plugin list */

#if (EASYMESH_VERSION > 2)
	void *dpp;
#endif

	struct bh_topology_data topology;

	void *son;
	son_func_t son_doit;

	struct wifi_data_element dlem; /* wifi data elements */
};

#define STA_CHANNEL_REPORT_MAX_NUM 32
struct sta_channel_report {
	uint8_t opclass;
#define STA_CHANNEL_REPORT_MAX_CHAN 64
	uint8_t num_channel;
	uint8_t channel[STA_CHANNEL_REPORT_MAX_CHAN];
};

#define MAX_UNASSOC_STAMACS 10
struct unassoc_sta_metric {
	uint8_t channel;
	uint8_t num_sta;
	struct {
		uint8_t macaddr[6];
	} sta[MAX_UNASSOC_STAMACS];
};

struct sta_error_response {
	uint8_t sta_mac[6];
	uint8_t response;
};

#define COMM_HANDLE(c)	(((struct controller *)(c))->ubus_ctx)

struct node *cntlr_add_node(struct controller *c, uint8_t *almacaddr);
struct node *cntlr_alloc_node(struct controller *c, uint8_t *almacaddr);
void cntlr_mark_agent(struct controller *c, struct node *n);
struct node *cntlr_find_node(struct controller *c, uint8_t *almacaddr);
struct node *cntlr_find_node_with_bssid(struct controller *c, uint8_t *bssid);
struct node *cntlr_find_node_with_sta(struct controller *c, uint8_t *stamacaddr);

struct netif_radio *cntlr_node_add_radio(struct controller *c, struct node *n,
		uint8_t *macaddr);
struct netif_radio *cntlr_find_radio(struct controller *c, uint8_t *macaddr);
struct netif_radio *cntlr_find_radio_with_bssid(struct controller *c, uint8_t *bssid);
struct netif_radio *cntlr_find_radio_in_node_by_band(struct controller *c, struct node *n,
				       enum wifi_band band);

struct netif_iface *cntlr_radio_add_iface(struct controller *c,
		struct netif_radio *r, uint8_t *hwaddr);
struct netif_iface *cntlr_find_iface(struct controller *c, uint8_t *macaddr);
struct netif_iface *cntlr_find_iface_type(struct controller *c, uint8_t *macaddr, uint32_t type);
struct netif_iface *cntlr_find_fbss(struct controller *c, uint8_t *macaddr);
struct netif_iface *cntlr_find_bbss(struct controller *c, uint8_t *macaddr);
struct netif_iface *cntlr_find_bss(struct controller *c, uint8_t *macaddr);

int cntlr_radio_add_bsta(struct controller *c, struct netif_radio *r, uint8_t *macaddr);

int cntlr_set_iface_type(struct controller *c, uint8_t *macaddr, uint32_t type);
int cntlr_get_iface_type(struct controller *c, uint8_t *macaddr, uint32_t *type);


#if (EASYMESH_VERSION >= 6)
bool cntlr_radio_support_wifi7(struct wifi_wifi7_capabilities *caps);
bool cntlr_node_support_ap_wifi7(struct node *n);
#endif

struct unassoc_sta_metrics *cntlr_find_usta_metric(struct controller *c,
                                                   struct sta *s,
                                                   uint8_t *agent_macaddr);

void cntlr_del_bcnreq(struct controller *c, uint8_t *sta_mac, uint8_t *agent_mac);
struct bcnreq *cntlr_find_bcnreq(struct controller *c, uint8_t *sta_mac, uint8_t *agent_mac);
struct bcnreq *cntlr_add_bcnreq(struct controller *c, uint8_t *sta_mac, uint8_t *agent_mac, int num_channel);

bool cntlr_resync_config(struct controller *c, bool reload);

int cntlr_radio_clean_scanlist_el(struct wifi_scanres_element *el);
void cntlr_clean_bcnreqlist_sta(struct controller *c, struct sta *s);

int cntlr_radio_get_beacon_channel(struct wifi_radio_element *radio,
				   uint8_t *opclass, uint8_t *channel);

int cntlr_sync_dyn_controller_config(struct controller *c, uint8_t *agent);

void cntrl_send_max_wifi_bh_hops_policy(struct controller *c);


int node_add_sta(struct node *n, struct sta *s);
int node_del_sta(struct node *n, struct sta *s);
struct sta *node_find_sta(struct node *n, uint8_t *sta_macaddr);
void node_update_stalist(struct node *n, uint8_t *stalist, int num);

int cntlr_register_steer_module(struct controller *c, const char *name);
int cntlr_unregister_steer_module(struct controller *c, char *name);

int cntlr_update_txlink_metric_data(struct controller *c, struct tlv_tx_linkmetric *txl, int len);
int cntlr_update_rxlink_metric_data(struct controller *c, struct tlv_rx_linkmetric *rxl, int len);

void cntlr_send_max_wifi_bh_hops_policy(struct controller *c);

void cntlr_load_steer_modules(struct controller *c);
void cntlr_unload_steer_modules(struct controller *c);
void cntlr_reorder_steer_modules(struct controller *c);
int cntlr_notify_plugins(struct controller *c, struct cmdu_buff *cmdu);

#if (EASYMESH_VERSION > 2)
/**
 * Return QoS rule type as string.
 *
 * @param      type    The QoS rule type
 */
const char *cntlr_qos_type2str(enum qos_rule_type type);

/**
 * Return a pointer to QoS rule with the given ID.
 *
 * @param      cc      Controller configuration
 * @param      id      QoS rule ID to search for
 *
 * @return     Pointer to the matching qos_rule, or NULL if not found
 */
struct qos_rule *cntlr_qos_get_rule(struct controller_config *cc, uint32_t id);

/**
 * Synchronize QoS-related options to a node with given @p origin.
 *
 * @param      c           Controller data
 * @param      qos_config  QoS configuration containing the rules to sync
 * @param      origin      The node hardware address
 * @param      add         true to apply rules, false to remove them
 */
void cntlr_qos_sync_node(struct controller *c,
                         struct qos_control_config *qos_config,
                         uint8_t *origin,
                         bool add);
#endif /* EASYMESH_VERSION > 2 */

int cntlr_estimate_max_throughput_for_node(struct controller *c, uint8_t *node_almacaddr);

char *cntlr_get_topology(struct controller *c);

#endif /* CNTLR_H */
