/*
 * (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 * All Rights Reserved.
 *
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 *
 * SPDX-License-Identifier: GPL-2.0+
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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, see <http://www.gnu.org/licenses/>.
 *
 */

#include <string.h>

#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include <osmocom/vty/vty.h>

#include <osmocom/upf/netinst.h>

/* Add a new netinst entry to the given list.
 * \param ctx  talloc allocate new entry from ctx.
 * \param list  append to this list.
 * \param name  The Network Instance name as given in PFCP Network Instance IEs.
 * \param addr  IP address string of local interface to associate with the Network Instance.
 * \param errmsg  On error, an error description is returned in this out-argument.
 * \return new network_instance entry, or NULL on error.
 */
const struct network_instance *netinst_add(void *ctx, struct llist_head *list, const char *name, const char *addr,
					   const char **errmsg)
{
	struct network_instance *netinst;
	if (errmsg)
		*errmsg = NULL;

	if (!name || !*name) {
		if (errmsg)
			*errmsg = "Network Instance name must not be empty";
		return NULL;
	}

	if (netinst_find(list, name)) {
		if (errmsg)
			*errmsg = "Network Instance entry with this name already exists";
		return NULL;
	}

	netinst = talloc(ctx, struct network_instance);
	*netinst = (struct network_instance){
		.name = talloc_strdup(netinst, name),
	};
	if (osmo_sockaddr_str_from_str(&netinst->addr, addr, 0)) {
		if (errmsg)
			*errmsg = "Network Instance address is not a valid IP address string";
		talloc_free(netinst);
		return NULL;
	}

	llist_add_tail(&netinst->entry, list);

	return netinst;
}

const struct network_instance *netinst_find(struct llist_head *list, const char *name)
{
	const struct network_instance *netinst;

	if (!name)
		return NULL;

	llist_for_each_entry(netinst, list, entry)
		if (!strcmp(netinst->name, name))
			return netinst;

	return NULL;
}

const struct network_instance *netinst_first(struct llist_head *list)
{
	return llist_first_entry_or_null(list, struct network_instance, entry);
}

/* Clear the list of Network Instance entries, return the nr of entries that were removed. */
int netinst_clear(struct llist_head *list)
{
	int count = 0;
	while (1) {
		struct network_instance *netinst = llist_first_entry_or_null(list, struct network_instance, entry);
		if (!netinst)
			break;
		llist_del(&netinst->entry);
		talloc_free(netinst);
		count++;
	}
	return count;
}

/* Write one or all netinst entries to the VTY output.
 * If name_or_null is NULL, print all entries. Else, print only the entry matching that name.
 * Return number of printed entries. */
int netinst_vty_write(struct vty *vty, struct llist_head *list, const char *indent, const char *name_or_null)
{
	const struct network_instance *netinst;
	int count = 0;

	llist_for_each_entry(netinst, list, entry) {
		if (name_or_null && strcmp(netinst->name, name_or_null))
			continue;
		vty_out(vty, "%sadd %s %s%s", indent, netinst->name, netinst->addr.ip, VTY_NEWLINE);
		count++;
	}
	return count;
}
