/**
 * @file appnote_mempool.c
 * @brief Demonstrates how to use brp_mempool for memory management in the
 *        BALTECH SDK.
 *
 * Many wrapper functions return variable-length data (arrays, strings).
 * The brp_mempool mechanism handles memory allocation for this data.
 *
 * This app note shows two usage patterns:
 *
 * 1. SIMPLE USAGE: Pass NULL as the mempool parameter.
 *    - Uses the protocol's internal mempool
 *    - Data is automatically freed on the next API call
 *    - Best for: single calls where you use the result immediately
 *
 * 2. ADVANCED USAGE: Create your own mempool.
 *    - You control when memory is freed
 *    - Data from multiple calls can coexist
 *    - Best for: collecting data from multiple API calls
 *
 * For more details, see:
 * https://docs.baltech.de/developers/use-brp-mempool.html
 */

#include <stdio.h>
#include <baltech_sdk.h>


/**
 * Check if an error occurred and exit immediately with an error message.
 *
 * @param errcode      The error code returned by a BRP function
 * @param errormessage Description of the operation that failed
 * @param dev          Protocol handle (will be destroyed on error)
 */
static void exit_on_error(brp_errcode errcode, char *errormessage,
                          brp_protocol dev)
{
	if (errcode != BRP_OK)
	{
		char *errname = brp_map_errcode(errcode);
		if (errname == NULL)
			fprintf(stderr, "%s [brp_errcode: 0x%08X]\n", errormessage, errcode);
		else
			fprintf(stderr, "%s [%s]\n", errormessage, errname);
		brp_destroy(dev);
		exit(EXIT_FAILURE);
	}
}


/**
 * Demonstrates simple mempool usage (internal mempool).
 *
 * When you pass NULL as the mempool parameter, the API uses the protocol's
 * internal mempool. This is convenient because you don't need to manage
 * memory yourself.
 *
 * IMPORTANT: The internal mempool is freed when you call the next API
 * function on the same protocol. This means:
 * - You must use the returned data BEFORE calling another API function
 * - Do NOT store pointers to the returned data for later use
 *
 * In this example, we print "features" immediately after retrieving them.
 * After calling brp_Sys_GetInfo(), the "features" pointer becomes invalid.
 *
 * @param dev protocol handle
 */
void simple_mempool_usage(brp_protocol dev)
{
	brp_errcode errcode;
	char *fws;
	brp_FeatureID *features, max_featureid;
	size_t features_len, cnt;

	printf("\nSIMPLE MEMPOOL USAGE (pass NULL):\n");

	// Get features - data is stored in protocol's internal mempool
	errcode = brp_Sys_GetFeatures((brp_Sys_GetFeatures_t){
		dev, .FeatureList=&features, .FeatureList_len=&features_len,
		.MaxFeatureID=&max_featureid});
	exit_on_error(errcode, "Failed to retrieve feature list", dev);

	// Use "features" immediately, before any other API call
	printf("  Supported features: ");
	for (cnt = 0; cnt < features_len; cnt++)
		printf("0x%04X ", features[cnt]);
	printf("\n");

	// This call frees the internal mempool, invalidating "features"
	errcode = brp_Sys_GetInfo((brp_Sys_GetInfo_t){dev, .Info=&fws});
	exit_on_error(errcode, "Failed to retrieve firmware string", dev);
	printf("  Firmware: %s\n", fws);

	// WARNING: "features" is now invalid and must not be used!
	// See advanced_mempool_usage() for how to keep data from multiple calls.
}


/**
 * Demonstrates advanced mempool usage (own mempool).
 *
 * When you need data from multiple API calls to remain valid simultaneously,
 * create your own mempool. Initialize it to NULL - the API will allocate it
 * on first use.
 *
 * With your own mempool:
 * - Data remains valid until you call brp_mempool_free()
 * - Multiple API calls can store data in the same pool
 * - You must remember to free the pool when done
 *
 * In this example, both "features" and "fws" remain valid until we
 * explicitly free the mempool at the end.
 *
 * @param dev protocol handle
 */
void advanced_mempool_usage(brp_protocol dev)
{
	brp_errcode errcode;
	char *fws;
	brp_FeatureID *features, max_featureid;
	size_t features_len, cnt;

	printf("\nADVANCED MEMPOOL USAGE (own mempool):\n");

	// Create our own mempool (initialize to NULL)
	brp_mempool my_mempool = NULL;

	// Both API calls store their results in our mempool
	errcode = brp_Sys_GetFeatures((brp_Sys_GetFeatures_t){
		dev, .FeatureList=&features, .FeatureList_len=&features_len,
		.MaxFeatureID=&max_featureid, .mempool=&my_mempool});
	exit_on_error(errcode, "Failed to retrieve feature list", dev);

	errcode = brp_Sys_GetInfo((brp_Sys_GetInfo_t){
		dev, .Info=&fws, .mempool=&my_mempool});
	exit_on_error(errcode, "Failed to retrieve firmware string", dev);

	// Both "features" and "fws" are still valid here
	printf("  Supported features: ");
	for (cnt = 0; cnt < features_len; cnt++)
		printf("0x%04X ", features[cnt]);
	printf("\n");
	printf("  Firmware: %s\n", fws);

	// Free all memory allocated in our mempool
	// After this call, both "features" and "fws" become invalid
	brp_mempool_free(&my_mempool);
}


int main(int argc, char *argv[])
{
	printf("BALTECH SDK mempool demonstration\n");
	printf("==================================\n");

	// Create and open a connection to a USB HID reader
	brp_protocol dev = brp_create();
	brp_set_io(dev, brp_create_usb_hid(0));
	exit_on_error(brp_open(dev), "Failed to connect to reader", dev);

	// Demonstrate both usage patterns
	simple_mempool_usage(dev);
	advanced_mempool_usage(dev);

	brp_destroy(dev);
	printf("\nDone.\n");
	return EXIT_SUCCESS;
}
