/* libjodycode version checks
 *
 * Code to embed the libjodycode version info and check against the currently
 * linked libjodycode to check for and report incompatibilities
 *
 * Copyright (C) 2023 by Jody Bruchon <jody@jodybruchon.com>
 * Licensed under The MIT License */

#include <stdio.h>
#include <stdlib.h>
#include <libjodycode.h>
#include "libjodycode_check.h"
#include "libjodycode_check_defs.h"

#ifdef JC_TEST
 #define JC_TEST_ONLY(a) a
#else
 #define JC_TEST_ONLY(a)
#endif

const char *jc_build_version = LIBJODYCODE_VER;
const int jc_build_api_version = LIBJODYCODE_API_VERSION;
const int jc_build_api_featurelevel = LIBJODYCODE_API_FEATURE_LEVEL;

const int jc_build_min_featurelevel = MY_FEATURELEVEL_REQ;

/* API sub-version info array, terminated with 255
 * The user-defined part has moved to libjodycode_check_defs.h
 * Do not edit this file, edit that one instead! */

/* Build the array data using user definitions */
#if MY_CACHEINFO_REQ == 255
 #undef MY_CACHEINFO_REQ
 #define MY_CACHEINFO_REQ LIBJODYCODE_CACHEINFO_VER
#endif
#if MY_JODY_HASH_REQ == 255
 #undef MY_JODY_HASH_REQ
 #define MY_JODY_HASH_REQ LIBJODYCODE_JODY_HASH_VER
#endif
#if MY_OOM_REQ == 255
 #undef MY_OOM_REQ
 #define MY_OOM_REQ LIBJODYCODE_OOM_VER
#endif
#if MY_PATHS_REQ == 255
 #undef MY_PATHS_REQ
 #define MY_PATHS_REQ LIBJODYCODE_PATHS_VER
#endif
#if MY_SIZE_SUFFIX_REQ == 255
 #undef MY_SIZE_SUFFIX_REQ
 #define MY_SIZE_SUFFIX_REQ LIBJODYCODE_SIZE_SUFFIX_VER
#endif
#if MY_SORT_REQ == 255
 #undef MY_SORT_REQ
 #define MY_SORT_REQ LIBJODYCODE_SORT_VER
#endif
#if MY_STRING_REQ == 255
 #undef MY_STRING_REQ
 #define MY_STRING_REQ LIBJODYCODE_STRING_VER
#endif
#if MY_STRTOEPOCH_REQ == 255
 #undef MY_STRTOEPOCH_REQ
 #define MY_STRTOEPOCH_REQ LIBJODYCODE_STRTOEPOCH_VER
#endif
#if MY_WIN_STAT_REQ == 255
 #undef MY_WIN_STAT_REQ
 #define MY_WIN_STAT_REQ LIBJODYCODE_WIN_STAT_VER
#endif
#if MY_WIN_UNICODE_REQ == 255
 #undef MY_WIN_UNICODE_REQ
 #define MY_WIN_UNICODE_REQ LIBJODYCODE_WIN_UNICODE_VER
#endif
#if MY_ERROR_REQ == 255
 #undef MY_ERROR_REQ
 #define MY_ERROR_REQ LIBJODYCODE_ERROR_VER
#endif
#if MY_ALARM_REQ == 255
 #undef MY_ALARM_REQ
 #define MY_ALARM_REQ LIBJODYCODE_ALARM_VER
#endif


const unsigned char jc_build_api_versiontable[] = {
	MY_CACHEINFO_REQ,
	MY_JODY_HASH_REQ,
	MY_OOM_REQ,
	MY_PATHS_REQ,
	MY_SIZE_SUFFIX_REQ,
	MY_SORT_REQ,
	MY_STRING_REQ,
	MY_STRTOEPOCH_REQ,
	MY_WIN_STAT_REQ,
	MY_WIN_UNICODE_REQ,
	MY_ERROR_REQ,
	MY_ALARM_REQ,
	255
};

const char *jc_versiontable_section[] = {
	"cacheinfo",
	"jody_hash",
	"oom",
	"paths",
	"size_suffix",
	"sort",
	"string",
	"strtoepoch",
	"win_stat",
	"win_unicode",
	"error",
	"alarm",
	NULL
};


int libjodycode_version_check(int verbose, int bail)
{
	const unsigned char * const restrict build = jc_build_api_versiontable;
	const unsigned char * const restrict lib = jc_api_versiontable;
	int i = 0;

	JC_TEST_ONLY(if (verbose > 1) fprintf(stderr, "libjodycode version check test code\n\n");)
	/* Force a version dump if requested */
	while (build[i] != 255) {
		JC_TEST_ONLY(if (verbose > 1) fprintf(stderr, "API %d: %s: builtin ver %u, lib ver %u\n", i, jc_versiontable_section[i], build[i], lib[i]);)
		if (build[i] != 0 && (lib[i] == 0 || build[i] != lib[i])) goto incompatible_versiontable;
		i++;
	}
	JC_TEST_ONLY(if (verbose > 1) goto incompatible_versiontable;)
	return 0;

incompatible_versiontable:
	if (verbose) {
		fprintf(stderr, "\n==============================================================================\n");
		fprintf(stderr,   "internal error: libjodycode on this system is an incompatible version\n\n");
		fprintf(stderr, "Currently using libjodycode v%s, API %d, feature level %d\n",
				jc_version, jc_api_version, jc_api_featurelevel);
		fprintf(stderr, "  Built against libjodycode v%s, API %d, feature level %d\n\n",
				jc_build_version, jc_build_api_version, jc_build_api_featurelevel);
		if (jc_build_min_featurelevel > jc_build_api_featurelevel)
			fprintf(stderr, "libjodycode feature level %d is required but linked library is level %d\n",
				jc_build_min_featurelevel, jc_build_api_featurelevel);
		if (lib[i] == 0) fprintf(stderr, "API sections are missing in libjodycode; it's probably too old.\n");
		else fprintf(stderr, "The first incompatible API section found is '%s' (want v%d, got v%d).\n",
				jc_versiontable_section[i], build[i], lib[i]);
		fprintf(stderr, "==============================================================================\n\n");
		fprintf(stderr, "\nUpdate libjodycode on your system and try again. If you continue to get this\n");
		fprintf(stderr, "error, contact the package or distribution maintainer. If all else fails, send\n");
		fprintf(stderr, "an email to jody@jodybruchon.com for help (but only as a last resort, please.)\n\n");
	}
	if (bail) exit(EXIT_FAILURE);
	return 1;
}

#ifdef JC_TEST
int main(void)
{
	libjodycode_version_check(2, 0);
	return 0;
}
#endif
