#ifndef _USER_COMPUTE_H
#define _USER_COMPUTE_H

#include <memory>
#include <vector>
#include <string_view>
#define CL_TARGET_OPENCL_VERSION 120
#include <CL/cl.h>

namespace smo {
namespace compute {

// Helper function to validate OpenCL version
bool validateOpenClVersion(
	std::string_view versionStr, std::string_view versionType,
	int minMajor, int minMinor);

/**
 * @brief OpenCL compute device information
 *
 * Manages a single OpenCL device, creating and owning its context and command
 * queue.
 */
class ComputeDevice
{
public:
	/**
	 * @brief Construct a ComputeDevice from platform and device IDs
	 *
	 * Creates the OpenCL context and command queue for the device.
	 * Throws std::runtime_error if context or queue creation fails.
	 *
	 * @param platformId OpenCL platform ID
	 * @param deviceId OpenCL device ID
	 */
	ComputeDevice(cl_platform_id platformId, cl_device_id deviceId);

	~ComputeDevice()
	{
		if (commandQueue)
		{
			clReleaseCommandQueue(commandQueue);
			commandQueue = nullptr;
		}
		if (context)
		{
			clReleaseContext(context);
			context = nullptr;
		}
	}

	// Non-copyable
	ComputeDevice(const ComputeDevice&) = delete;
	ComputeDevice& operator=(const ComputeDevice&) = delete;

	cl_platform_id platform;
	cl_device_id device;
	cl_context context;
	cl_command_queue commandQueue;
};

/**
 * @brief Association between an OpenCL buffer and a compute device
 */
struct ClBufferDeviceAssociation
{
	ClBufferDeviceAssociation(
		cl_mem buf, const std::shared_ptr<ComputeDevice>& dev)
	: buffer(buf), device(dev)
	{}

	cl_mem buffer;
	std::shared_ptr<ComputeDevice> device;
};

/**
 * @brief OpenCL buffer created on all compute devices
 *
 * Manages a USE_HOST_PTR buffer created on all available compute devices.
 * The constructor creates buffers for all devices, and the destructor releases
 * them.
 */
class ClBuffer
{
public:
	/**
	 * @brief Construct a ClBuffer and create buffers on all devices
	 *
	 * Creates a USE_HOST_PTR buffer on each device's context.
	 * Throws std::runtime_error if buffer creation fails for any device.
	 *
	 * @param hostPtr Host pointer to use
	 * @param size Size of buffer in bytes
	 * @param flags Additional OpenCL memory flags
	 * @param devices Vector of compute devices to create buffers on
	 */
	ClBuffer(
		void* hostPtr, size_t size, cl_mem_flags flags,
		const std::vector<std::shared_ptr<ComputeDevice>>& devices);

	~ClBuffer()
	{
		for (auto& assoc : associations)
		{
			if (assoc.buffer)
			{
				clReleaseMemObject(assoc.buffer);
				assoc.buffer = nullptr;
			}
		}
	}

	// Non-copyable
	ClBuffer(const ClBuffer&) = delete;
	ClBuffer& operator=(const ClBuffer&) = delete;

	/**
	 * @brief Get the cl_mem handle for a specific compute device
	 * @param device The compute device to find the buffer for
	 * @return The cl_mem handle for the device, or nullptr if not found
	 */
	cl_mem getAssociatedBufferHandleForDevice(
		const std::shared_ptr<ComputeDevice>& device) const;

	void* hostPtr;
	size_t size;
	cl_mem_flags flags;
	std::vector<ClBufferDeviceAssociation> associations;
};

} // namespace compute
} // namespace smo

#endif // _USER_COMPUTE_H
