#include <iostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <algorithm>
#include <computeManager/computeManager.h>
#include <user/compute.h>

namespace smo {
namespace compute {

void ComputeManager::initialize()
{
	if (initialized) { return; }

	cl_int err;

	// Get number of platforms
	cl_uint numPlatforms = 0;
	err = clGetPlatformIDs(0, nullptr, &numPlatforms);
	if (err != CL_SUCCESS)
	{
		throw std::runtime_error(
			std::string(__func__) + ": failed to get OpenCL platforms: " +
			std::to_string(err));
	}
	if (numPlatforms == 0)
	{
		throw std::runtime_error(
			std::string(__func__) + ": no OpenCL platforms found");
	}

	// Get all platforms
	std::vector<cl_platform_id> platforms(numPlatforms);
	err = clGetPlatformIDs(numPlatforms, platforms.data(), nullptr);
	if (err != CL_SUCCESS)
	{
		throw std::runtime_error(
			std::string(__func__) + ": failed to enumerate OpenCL platforms: " +
			std::to_string(err));
	}

	// Enumerate devices for each platform
	for (cl_uint p = 0; p < numPlatforms; ++p)
	{
		cl_platform_id platform = platforms[p];

		// Check platform version
		char platformVersion[128];
		err = clGetPlatformInfo(
			platform, CL_PLATFORM_VERSION,
			sizeof(platformVersion), platformVersion, nullptr);

		if (err == CL_SUCCESS)
		{
			if (!validateOpenClVersion(platformVersion, "platform", 1, 2))
			{
				std::cout << __func__ << ": skipping platform " << p
					<< " with incompatible OpenCL version "
					<< std::string(platformVersion) << std::endl;
				continue;
			}
		}

		// Get number of devices
		cl_uint numDevices = 0;
		err = clGetDeviceIDs(
			platform, CL_DEVICE_TYPE_ALL, 0, nullptr, &numDevices);

		if (err != CL_SUCCESS || numDevices == 0)
		{
			std::cout << __func__ << ": skipping platform " << p
				<< " with no devices" << std::endl;
			continue;
		}

		// Get all devices
		std::vector<cl_device_id> platformDevices(numDevices);
		err = clGetDeviceIDs(
			platform, CL_DEVICE_TYPE_ALL, numDevices,
			platformDevices.data(), nullptr);

		if (err != CL_SUCCESS)
		{
			throw std::runtime_error(
				std::string(__func__) + ": failed to enumerate devices for "
				"platform " + std::to_string(p) + ": " + std::to_string(err));
		}

		// Create ComputeDevice for each device
		for (cl_uint d = 0; d < numDevices; ++d)
		{
			cl_device_id device = platformDevices[d];

			// Check device version
			char deviceVersion[128];
			err = clGetDeviceInfo(
				device, CL_DEVICE_VERSION,
				sizeof(deviceVersion), deviceVersion, nullptr);

			if (err == CL_SUCCESS)
			{
				if (!validateOpenClVersion(deviceVersion, "device", 1, 2))
				{
					std::cout << __func__ << ": skipping device " << d
						<< " with incompatible OpenCL version "
						<< std::string(deviceVersion) << std::endl;
					continue;
				}
			}

			// Create ComputeDevice (constructor creates context and queue)
			try
			{
				auto deviceObj = std::make_shared<ComputeDevice>(
					platform, device);
				devices.push_back(deviceObj);
			}
			catch (const std::runtime_error& e)
			{
				// Re-throw with more context about which device/platform
				throw std::runtime_error(
					std::string(__func__) + ": failed to create ComputeDevice "
					"for device " + std::to_string(d) + " on platform " +
					std::to_string(p) + ": " + e.what());
			}
		}
	}

	if (devices.empty())
	{
		throw std::runtime_error(
			std::string(__func__) + ": no compatible OpenCL devices found");
	}

	initialized = true;
	std::cout << __func__ << ": Initialized with " << devices.size()
		<< " compute device(s)" << std::endl;
}

void ComputeManager::finalize()
{
	if (!initialized) { return; }

	// Release all devices (their shared_ptrs will clean up contexts/queues)
	devices.clear();
	initialized = false;
	std::cout << __func__ << ": Finalized" << std::endl;
}

std::shared_ptr<ClBuffer>
ComputeManager::createUseHostPtrBuffer(
	void* hostPtr, size_t size, cl_mem_flags flags)
{
	if (!initialized)
	{
		std::cerr << __func__ << ": ComputeManager not initialized"
			<< std::endl;
		throw std::runtime_error(
			std::string(__func__) + ": ComputeManager not initialized");
	}

	return std::make_shared<ClBuffer>(hostPtr, size, flags, devices);
}

void ComputeManager::releaseUseHostPtrBuffer(std::shared_ptr<ClBuffer> buffer)
{
	// No-op: ClBuffer's destructor handles cleanup automatically
	// This function exists for API compatibility
	(void)buffer;
}

std::shared_ptr<ComputeDevice> ComputeManager::getDevice()
{
	if (!initialized || devices.empty()) {
		return nullptr;
	}

	// Return first available device
	// In the future, this will filter based on ComputeDeviceConstraints
	return devices[0];
}

void ComputeManager::releaseDevice(std::shared_ptr<ComputeDevice> device)
{
	// Placeholder for future refcounting implementation
	// Devices are only removed in finalize()
	(void)device;
}

} // namespace compute
} // namespace smo
