/*
 * sai-virt - v-conf.c
 *
 * Copyright (C) 2019 - 2026 Andy Green <andy@warmcat.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation:
 *  version 2.1 of the License.
 */

#include <libwebsockets.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#include "v-private.h"

static const char * const paths_plat[] = {
	"name",
	"platform",
	"base_image",
	"overlay_size",
};

enum {
	VJ_NAME,
	VJ_PLATFORM,
	VJ_BASE_IMAGE,
	VJ_OVERLAY_SIZE,
};

struct v_conf_ctx {
	saiv_plat_t *plat;
};

static signed char
saiv_conf_plat_cb(struct lejp_ctx *ctx, char reason)
{
	struct v_conf_ctx *v = (struct v_conf_ctx *)ctx->user;

	if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0) {
		v->plat = malloc(sizeof(*v->plat));
		if (!v->plat)
			return -1;
		memset(v->plat, 0, sizeof(*v->plat));
		return 0;
	}

	if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0) {
		if (v->plat && v->plat->name[0] && v->plat->base_image[0]) {
			lws_dll2_add_tail(&v->plat->list, &virt.plat_owner);
			lwsl_notice("Added platform %s (base %s)\n", v->plat->name, v->plat->base_image);
			v->plat = NULL;
		} else if (v->plat) {
			lwsl_err("Platform definition missing name or base_image\n");
			free(v->plat);
			v->plat = NULL;
		}
		return 0;
	}

	if (reason == LEJPCB_VAL_STR_END) {
		switch (ctx->path_match - 1) {
		case VJ_NAME:
			lws_strncpy(v->plat->name, ctx->buf, sizeof(v->plat->name));
			break;
		case VJ_PLATFORM:
			lws_strncpy(v->plat->platform, ctx->buf, sizeof(v->plat->platform));
			break;
		case VJ_BASE_IMAGE:
			lws_strncpy(v->plat->base_image, ctx->buf, sizeof(v->plat->base_image));
			break;
		case VJ_OVERLAY_SIZE:
			lws_strncpy(v->plat->overlay_size, ctx->buf, sizeof(v->plat->overlay_size));
			break;
		}
	}

	return 0;
}

static int
saiv_conf_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
{
	char filepath[256];
	struct lejp_ctx ctx;
	struct v_conf_ctx vctx;
	int fd, m = 0;
	ssize_t n;
	uint8_t buf[1024];

	if (lde->name[0] == '.')
		return 0;

	lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, lde->name);

	fd = open(filepath, O_RDONLY);
	if (fd < 0) {
		lwsl_err("Cannot open %s\n", filepath);
		return 0;
	}

	memset(&vctx, 0, sizeof(vctx));
	lejp_construct(&ctx, saiv_conf_plat_cb, &vctx, paths_plat, LWS_ARRAY_SIZE(paths_plat));

	while ((n = read(fd, buf, sizeof(buf))) > 0) {
		m = lejp_parse(&ctx, buf, (int)n);
		if (m < 0 && m != LEJP_CONTINUE) {
			lwsl_err("%s: JSON decode error %d\n", filepath, m);
			break;
		}
	}
	close(fd);
	lejp_destruct(&ctx);

	if (vctx.plat)
		free(vctx.plat);

	return 0;
}

int
saiv_config(struct sai_virt *virt, const char *d)
{
	lwsl_notice("Parsing configuration directory %s\n", d);
	return lws_dir(d, virt, saiv_conf_dir_cb);
}

static const char * const paths_global[] = {
	"servers[].url",
	"max_vms",
};

enum {
	VJG_SERVER_URL,
	VJG_MAX_VMS,
};

static signed char
saiv_conf_global_cb(struct lejp_ctx *ctx, char reason)
{
	struct sai_virt *v = (struct sai_virt *)ctx->user;

	if (reason == LEJPCB_VAL_STR_END) {
		switch (ctx->path_match - 1) {
		case VJG_SERVER_URL:
		{
			saiv_server_t *srv = malloc(sizeof(*srv));
			if (srv) {
				memset(srv, 0, sizeof(*srv));
				srv->url = strdup(ctx->buf);
				if (lws_ss_create(v->context, 0, &ssi_saiv_server_link_t,
						  srv, &srv->ss, NULL, NULL)) {
					lwsl_err("%s: failed to create ss\n", __func__);
					free((void *)srv->url);
					free(srv);
				} else {
					lws_dll2_add_tail(&srv->list, &v->sai_server_owner);
					lwsl_notice("Added server %s\n", srv->url);
				}
			}
			break;
		}
		case VJG_MAX_VMS:
			v->max_vms = atoi(ctx->buf);
			lwsl_notice("Set max_vms to %d\n", v->max_vms);
			break;
		}
	}
	return 0;
}

int
saiv_config_global(struct sai_virt *virt, const char *filepath)
{
	struct lejp_ctx ctx;
	int fd, m = 0;
	ssize_t n;
	uint8_t buf[1024];

	lwsl_notice("Parsing global configuration %s\n", filepath);

	fd = open(filepath, O_RDONLY);
	if (fd < 0) {
		lwsl_err("Cannot open %s\n", filepath);
		return 1;
	}

	lejp_construct(&ctx, saiv_conf_global_cb, virt, paths_global, LWS_ARRAY_SIZE(paths_global));

	while ((n = read(fd, buf, sizeof(buf))) > 0) {
		m = lejp_parse(&ctx, buf, (int)n);
		if (m < 0 && m != LEJP_CONTINUE) {
			lwsl_err("%s: JSON decode error %d\n", filepath, m);
			break;
		}
	}
	close(fd);
	lejp_destruct(&ctx);

	return 0;
}