/*
 * cntlr_qos.c - QoS-related controller functions
 *
 * Copyright (C) 2023 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: maxim.menshikov@iopsys.eu
 *
 */


#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <libubox/list.h>
#include <stdbool.h>
#include <cmdu.h>
#include <easymesh.h>
#include <wifidefs.h>

#include "utils/debug.h"
#include "config.h"
#include "cntlr.h"
#include "cntlr_tlv.h"
#include "cntlr_map.h"

#if (EASYMESH_VERSION > 2)

const char *cntlr_qos_type2str(enum qos_rule_type type)
{
	if (type == QOS_RULE_TYPE_DSCP_PCP)
		return "dscp_pcp";
	else if (type == QOS_RULE_TYPE_MSCS)
		return "mscs";
	else if (type == QOS_RULE_TYPE_SCS)
		return "scs";
	else if (type == QOS_RULE_TYPE_MGMT)
		return "mgmt";

	return "Unknown";
}

struct qos_rule *cntlr_qos_get_rule(struct controller_config *cc, uint32_t id)
{
	struct qos_rule *r = NULL;

	list_for_each_entry(r, &cc->qos.rule, list) {
		if (r->id == id)
			return r;
	}

	return NULL;
}

/* Send Service Prioritization Request to specific hardware address */
static int cntlr_qos_send_sp_req(struct controller *c,
                                 bool add,
                                 uint8_t *origin,
                                 struct qos_rule *rule)
{
	struct cmdu_buff *buf;
	uint16_t mid = 0;
	int ret;

	/*
	 * FIXME:
	 * the Multi-AP Controller should ensure that the Service Prioritization
	 * Rule TLV is compatible with the Multi-AP Agent’s capabilities as
	 * identified in the most recent AP Capability Report message received from
	 * that Multi-AP Agent.
	 */

	buf = cmdu_alloc_simple(CMDU_SERVICE_PRIORITIZATION_REQUEST,
	                        &mid);
	if (!buf) {
		err("%s: -ENOMEM\n", __func__);
		return -1;
	}

	ret = cntlr_gen_spr_tlv(c,
	                        buf,
	                        rule->id,
	                        add,
	                        rule->precedence,
	                        rule->output,
	                        rule->always_match);
	if (ret != 0) {
		cmdu_free(buf);
		dbg("Failed to build SPR TLV\n");
		return -1;
	}

	if (add) {
		switch (rule->type)
		{
			case QOS_RULE_TYPE_DSCP_PCP:
			{
				/*
				 * If triggered, a Multi-AP Controller shall send a
				 * Service Prioritization Request message with a DSCP Mapping
				 * Table TLV (see section 17.2.71).
				 */
				ret = cntlr_gen_dscp_mapping_table_tlv(c, buf,
					rule->dscp_pcp.dscp_pcp);

				break;
			}
#if (EASYMESH_VERSION >= 4)
			/*
			 * If triggered, a Multi-AP Controller shall send a Service
			 * Prioritization Request message with a QoS Management Descriptor
			 * TLV (see section 17.2.93).
			 */
			/*
			 * If the Descriptor Element field within a QoS Management
			 * Descriptor TLV is an MSCS or SCS Descriptor element, the validity
			 * lifetime of the QMID is either until the Controller receives a
			 * Profile-2 Error Code TLV indicating the Multi-AP Agent is unable
			 * to configure the Descriptor or (if the Descriptor is accepted)
			 * until the Multi-AP Controller sends a QoS Management Descriptor
			 * TLV removing the Descriptor.
			 */
			case QOS_RULE_TYPE_MSCS:
			case QOS_RULE_TYPE_SCS:
			case QOS_RULE_TYPE_MGMT:
			{
				uint8_t *desc = NULL;
				uint32_t desc_len = 0;

				switch (rule->type)
				{
					case QOS_RULE_TYPE_SCS:
					{
						desc = cntlr_gen_qos_scs_desc(c,
							SCS_DESC_REQ_TYPE_ADD,
							&rule->mgmt.scs,
							&desc_len);
						break;
					}
					case QOS_RULE_TYPE_MSCS:
					{
						desc = cntlr_gen_qos_mscs_desc(c,
							SCS_DESC_REQ_TYPE_ADD,
							&rule->mgmt.mscs,
							&desc_len);
						break;
					}
					case QOS_RULE_TYPE_MGMT:
					{
						desc = cntlr_gen_qos_mgmt_elem_desc(c,
							0 /* not defined yet */,
							&rule->mgmt.qm,
							&desc_len);
						break;
					}
					default:
					{
						break;
					}
				}

				if (desc == NULL) {
					ret = -1;
					break;
				}

				/*
				 * If the Descriptor Element field within a QoS Management
				 * Descriptor TLV is a QoS Management element containing a DSCP
				 * Policy attibute, the QMID is used in a Profile-2 Error Code
				 * TLV to indicate the Multi-AP Agent is unable to configure the
				 * rule.
				 *
				 * If the Descriptor Element field within a QoS Management
				 * Descriptor TLV is a QoS Management element containing a DSCP
				 * Policy attribute, the Policy ID in the Descriptor Element
				 * field is chosen by the Multi-AP Controller, and (per [25])
				 * has scope across the ESS (i.e., policy survives a roam after,
				 * after STA roams to another BSSID).
				 *
				 * If triggered, a Multi-AP Controller may modify or remove a
				 * DSCP Policy by sending an updated QoS Mangement element
				 * containing a DSCP Policy attribute with the same Policy ID
				 * within a QoS Management Descriptor TLV in a Service
				 * Prioritization Request message to a Multi-AP Agent.
				 */
				ret = cntlr_gen_qos_management_desc_tlv(c, buf,
					rule->mgmt.qmid, rule->mgmt.bssid, rule->mgmt.sta,
					desc, desc_len);
				free(desc);
				break;
			}
#endif
			default:
			{
				ret = -1;
				break;
			}
		}
	}

	memcpy(buf->origin, origin, 6);
	cmdu_put_eom(buf);
	if (ret == 0) {
		send_cmdu(c, buf);
	}
	cmdu_free(buf);

	return ret;
}

void cntlr_qos_sync_node(struct controller *c,
                         struct qos_control_config *qos_config,
                         uint8_t *origin,
                         bool add)
{
	struct qos_rule *rule = NULL;

	dbg("%s: syncing QoS in nodes\n", __func__);

	/*
	 * If triggered, a Multi-AP Controller shall send a Service Prioritization
	 * Request message with a Service Prioritization Rule TLV
	 * (see section 17.1.47).
	 */
	list_for_each_entry(rule, &qos_config->rule, list) {
		dbg("%s: syncing QoS in node, sending SPR\n", __func__);
		cntlr_qos_send_sp_req(c, add, origin, rule);
	}
}

#endif
