/* SPDX-License-Identifier: GPL-2.0 */
/*
 * main.c - qosmngr's boilerplate and entry point
 *
 * Copyright (C) 2020-2025 iopsys Software Solutions AB. All rights reserved.
 *
 * Author: Oskar Viljasaar <oskar.viljasaar@iopsys.eu>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

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

#include <libubus.h>
#include <libbbfdm-ubus/bbfdm-ubus.h>

#include "qosmngr.h"
#include "qos_bbf.h"

#define PROTOCOLS_FILE "/etc/protocols"

protocol_list_t g_protocol_maps;

extern DM_MAP_OBJ tDynamicObj[];

#ifdef UBUS_SUPPORT
const struct blobmsg_policy get_status_policy[NUM_QOS_POLICY] = {
      [QOS_POLICY_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
      [QOS_POLICY_QID] = { .name = "qid", .type = BLOBMSG_TYPE_INT32},
};

static const struct ubus_method qos_methods[] = {
        UBUS_METHOD("queue_stats", qosmngr_get_stats, get_status_policy),
};

static struct ubus_object_type qos_object_type =
        UBUS_OBJECT_TYPE("qos", qos_methods);

static struct ubus_object test_object = {
        .name = "qos",
        .type = &qos_object_type,
        .methods = qos_methods,
        .n_methods = ARRAY_SIZE(qos_methods),
};

/**
 *  To expose the qosmngr object on ubus i.e., qos with method queue_stats
 *  @param context input parameter pointer to ubus context
 *  retrun integer value 0 on success and -1 on failure
 */
static int qosmngr_publish_object(struct ubus_context *context)
{
	int ret;
	ret = ubus_add_object(context, &test_object);
	if (ret) {
		BBF_ERR("Failed to add 'qos' ubus object: %s\n",
			ubus_strerror(ret));
	}

	return ret;
}
#endif

static int init_protocol_numbers()
{
	char line[256] = {0};
	protocol_list_t *pentry;

	INIT_LIST_HEAD(&g_protocol_maps.list);

	// cppcheck-suppress cert-MSC24-C
	FILE *file = fopen(PROTOCOLS_FILE, "r");
	if (!file)
		return -1;

	while (fgets(line, sizeof(line), file)) {
		if (line[0] == '#' || line[0] == '\n')
			continue;

		char name[32] = {0};
		int number = 0;
		if (sscanf(line, "%31s %d", name, &number) == 2) {
			pentry = (protocol_list_t *) calloc(1, sizeof(protocol_list_t));
			if (pentry) {
				pentry->proto_num = number;
				snprintf(pentry->proto_name, sizeof(name), "%s", name);
				list_add_tail(&pentry->list, &g_protocol_maps.list);
			}
		}
	}

	fclose(file);
	return 0;
}

int cleanup()
{
	free_qstat();

	protocol_list_t *entry = NULL, *tmp = NULL;

	list_for_each_entry_safe(entry, tmp, &g_protocol_maps.list, list) {
		list_del(&entry->list);
		free(entry);
	}
	return 0;
}

int main(int argc, char **argv)
{
	int ch, dm_type = 0;
	int log_level = 7;
	struct bbfdm_context bbfdm_ctx = {0};

	while ((ch = getopt(argc, argv, "dl:q:")) != -1) {
		switch (ch) {
		case 'l':
			if (optarg) {
				log_level = (int)strtoul(optarg, NULL, 10);
			}

			if (log_level < 0 || log_level > 7)
				log_level = 7;
			break;
		case 'd':
			dm_type++;
			break;
		case 'q':
			int num_of_q = get_no_of_q_per_port(argv[argc-1]);
			printf("%d", num_of_q);
			exit(0);
		default:
			break;
		}
	}

	openlog(argv[0], LOG_PID|LOG_CONS|LOG_NDELAY, LOG_LOCAL1);

	memset(&bbfdm_ctx, 0, sizeof(struct bbfdm_context));
	bbfdm_ubus_set_service_name(&bbfdm_ctx, "qosmngr");
	bbfdm_ubus_set_log_level(log_level);
	bbfdm_ubus_load_data_model(tDynamicObj);

	if (dm_type > 0) {
		int res = bbfdm_print_data_model_schema(&bbfdm_ctx, dm_type);
		exit(res);
	}

	if (init_protocol_numbers() != 0) {
		BBF_ERR("Failed to initialize protocol numbers");
		goto out;
	}

	if (init_qstat() != 0) {
		BBF_ERR("Failed to initialize global qstat");
		goto out;
	}

	if (bbfdm_ubus_register_init(&bbfdm_ctx)) {
		BBF_ERR("Failed to register bbfdm.qosmngr ubus");
		goto out;
	}

#ifdef UBUS_SUPPORT
	if (qosmngr_publish_object(bbfdm_ctx.ubus_ctx)) {
		BBF_ERR("Failed to register qos object");
		goto out;
	}
#endif

	uloop_run();
 out:
	bbfdm_ubus_register_free(&bbfdm_ctx);
	cleanup();
	return 0;
}
