// Copyright 2026 Daniel McGuire
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// PHASOR RUNTIME FFI LIBRARY ABI V2
// README
//
// See below:

#ifndef PHASOR_FFI_HPP
#define PHASOR_FFI_HPP

#ifdef __cplusplus
#include <cstdint>
#include <cstddef>
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#endif

/*
 * Phasor Foreign Function Interface
 *
 * This header provides the necessary definitions for creating third-party
 * plugins for the Phasor scripting engine. It is designed to be a stable,
 * C-compatible interface.
 *
 * USAGE:
 * 1. Define PHASOR_FFI_BUILD_DLL.
 * 2. Include this header in your C or C++ source file.
 * 3. Implement the entry point function:
 * void phasor_plugin_entry(const PhasorAPI* api, PhasorVM* vm);
 * 4. Inside this function, use the provided `api` object to register
 * your own native functions.
 * 5. Compile your code as a shared library (.dll, .so, .dylib).
 */

#if defined(_WIN32) && defined(PHASOR_FFI_BUILD_DLL)
#define PHASOR_FFI_EXPORT __declspec(dllexport)
#elif defined(__GNUC__) || defined(__clang__)
#define PHASOR_FFI_EXPORT __attribute__((visibility("default")))
#else
#define PHASOR_FFI_EXPORT
#endif

#ifdef __cplusplus
extern "C"
{
#endif

	/// @brief Phasor Virtual Machine pointer.
	typedef struct PhasorVM PhasorVM;

	/// @brief PhasorValue types.
	typedef enum
	{
		PHASOR_TYPE_NULL,
		PHASOR_TYPE_BOOL,
		PHASOR_TYPE_INT,
		PHASOR_TYPE_FLOAT,
		PHASOR_TYPE_STRING,
		PHASOR_TYPE_ARRAY,
		PHASOR_TYPE_STRUCT,
	} PhasorValueType;

	/// @brief Forward declare for self-reference in the union
	typedef struct PhasorValue PhasorValue;

	/// @brief Represents a value in the Phasor VM.
	struct PhasorValue
	{
		PhasorValueType type;
		union {
			bool        b;
			int64_t     i;
			double      f;
			const char *s; // Note: For strings returned from the VM, this is valid.
			// For strings passed to the VM, the VM makes a copy.
			struct{ const PhasorValue *elements; size_t count; } a;
			struct {
				const char *name;
				const char **keys;
				const PhasorValue *values;
				size_t count;
			} st;
		} as;
	};

	// -----------------------------------------------------------------------------
	// Value Manipulation Functions (static inline for self-containment)
	// -----------------------------------------------------------------------------

	// Helper functions to create PhasorValue instances.
	static inline PhasorValue phasor_make_null()
	{
		PhasorValue val;
		val.type = PHASOR_TYPE_NULL;
		return val;
	}

	static inline PhasorValue phasor_make_bool(bool b)
	{
		PhasorValue val;
		val.type = PHASOR_TYPE_BOOL;
		val.as.b = b;
		return val;
	}

	static inline PhasorValue phasor_make_int(int64_t i)
	{
		PhasorValue val;
		val.type = PHASOR_TYPE_INT;
		val.as.i = i;
		return val;
	}

	static inline PhasorValue phasor_make_float(double f)
	{
		PhasorValue val;
		val.type = PHASOR_TYPE_FLOAT;
		val.as.f = f;
		return val;
	}

	static inline PhasorValue phasor_make_string(const char *s)
	{
		PhasorValue val;
		val.type = PHASOR_TYPE_STRING;
		val.as.s = s;
		return val;
	}

	static inline PhasorValue phasor_make_array(const PhasorValue *elements, size_t count)
	{
		PhasorValue val;
		val.type = PHASOR_TYPE_ARRAY;
		val.as.a.elements = elements;
		val.as.a.count = count;
		return val;
	}

	static inline PhasorValue phasor_make_struct(const char *name, const char **keys,
	const PhasorValue *values, size_t count) 
	{
		PhasorValue val;
		val.type          = PHASOR_TYPE_STRUCT;
		val.as.st.name    = name;
		val.as.st.keys    = keys;
		val.as.st.values  = values;
		val.as.st.count   = count;
		return val;
	}


	// Helper functions to check the type of a PhasorValue.
	static inline bool phasor_is_null(PhasorValue val)
	{
		return val.type == PHASOR_TYPE_NULL;
	}
	static inline bool phasor_is_bool(PhasorValue val)
	{
		return val.type == PHASOR_TYPE_BOOL;
	}
	static inline bool phasor_is_int(PhasorValue val)
	{
		return val.type == PHASOR_TYPE_INT;
	}
	static inline bool phasor_is_float(PhasorValue val)
	{
		return val.type == PHASOR_TYPE_FLOAT;
	}
	static inline bool phasor_is_string(PhasorValue val)
	{
		return val.type == PHASOR_TYPE_STRING;
	}
	static inline bool phasor_is_array(PhasorValue val)
	{
		return val.type == PHASOR_TYPE_ARRAY;
	}
	static inline bool phasor_is_number(PhasorValue val)
	{
		return phasor_is_int(val) || phasor_is_float(val);
	}
	static inline bool phasor_is_struct(PhasorValue val)
	{
		return val.type == PHASOR_TYPE_STRUCT;
	}
	

	// Helper functions to get the underlying value from a PhasorValue.
	// Note: These do not perform type checking. Use the `phasor_is_*` functions first.
	static inline bool phasor_to_bool(PhasorValue val)
	{
		return val.as.b;
	}
	static inline int64_t phasor_to_int(PhasorValue val)
	{
		return val.as.i;
	}
	static inline double phasor_to_float(PhasorValue val)
	{
		if (phasor_is_int(val))
			return (double)val.as.i;
		return val.as.f;
	}
	static inline const char *phasor_to_string(PhasorValue val)
	{
		return val.as.s;
	}

	// -----------------------------------------------------------------------------
	// FFI API Definitions
	// -----------------------------------------------------------------------------

	/// @brief Signature for a native C function that can be registered with the Phasor VM.
	typedef PhasorValue (*PhasorNativeFunction)(PhasorVM *vm, int argc, const PhasorValue *argv);

	/// @brief Function pointer type for the function that registers a native function with the VM.
	typedef void (*PhasorRegisterFunction)(PhasorVM *vm, const char *name, PhasorNativeFunction func);

	/// @brief The collection of API functions that the Phasor host provides to the plugin.
	typedef struct
	{
		/// @brief Registers a native C function with the given name.
		PhasorRegisterFunction register_function;

		// VM Bindings
		void (*log)(PhasorVM *vm, PhasorValue msg);
		void (*logerr)(PhasorVM *vm, PhasorValue msg);
		void (*flush)(PhasorVM *vm);
		void (*flusherr)(PhasorVM *vm);
		const char *(*getVersion)(PhasorVM *vm);

		// FFI Management
		bool (*loadPlugin)(PhasorVM *vm, const char *libPath);
		void (*onExitCall)(PhasorVM *vm, void (*func)(void));
		void (*onExitFree)(PhasorVM *vm, void *ptr);

		// CRT Memory Management
		void *(*malloc)(size_t size);
		void *(*calloc)(size_t num, size_t size);
		void *(*realloc)(void *ptr, size_t size);
		void (*free)(void *ptr);
	} PhasorAPI;

	// -----------------------------------------------------------------------------
	// Plugin Entry Point
	// -----------------------------------------------------------------------------

	/**
	 * @brief The one and only entry point for a Phasor plugin.
	 *
	 * A Phasor-compatible shared library MUST export a C function with this exact
	 * signature. The Phasor host will call this function when the plugin is loaded,
	 * providing the plugin with a pointer to the host's API functions and the
	 * current VM instance.
	 *
	 * @param api A pointer to a struct containing function pointers that the plugin
	 * can use to interact with the host (e.g., register functions).
	 * @param vm  An opaque pointer to the VM instance. This should be passed back
	 * to any API functions that require it.
	 */
	PHASOR_FFI_EXPORT void phasor_plugin_entry(const PhasorAPI *api, PhasorVM *vm);

#ifdef __cplusplus
} // extern "C"
#endif

#endif // PHASOR_FFI_HPP