#ifndef ADAPTERS_OPENCL_CL_KERNEL_COMPLETION_AREQ_H
#define ADAPTERS_OPENCL_CL_KERNEL_COMPLETION_AREQ_H

#include <atomic>
#include <coroutine>
#include <functional>
#include <memory>

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

#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>

namespace smo {
namespace openclBoundary {

/** Eager-start std::function callback -> coroutine adapter for OpenCL kernel
 *  completion (cl_int event_command_exec_status posted to io_context).
 */
template <typename StartFn>
class ClKernelCompletionAReq
{
public:
	struct AsyncState
	{
		std::atomic<bool> settled{false};
		cl_int result{};
		std::coroutine_handle<> callerSchedHandle;
	};

	explicit ClKernelCompletionAReq(
		boost::asio::io_context &resumeIoContext,
		StartFn startFn)
	: asyncState(std::make_shared<AsyncState>()),
	resumeIoContext(resumeIoContext)
	{
		startFn([this](cl_int eventCommandExecStatus)
		{
			asyncState->result = eventCommandExecStatus;
			signalSettledAndResumeCaller();
		});
	}

	bool await_ready() const noexcept
	{
		return asyncState->settled.load(std::memory_order_acquire);
	}

	bool await_suspend(std::coroutine_handle<> callerSchedHandle) noexcept
	{
		if (asyncState->settled.load(std::memory_order_acquire)) {
			return false;
		}

		asyncState->callerSchedHandle = callerSchedHandle;
		return true;
	}

	cl_int await_resume() noexcept
	{
		return asyncState->result;
	}

private:
	void signalSettledAndResumeCaller() noexcept
	{
		asyncState->settled.store(true, std::memory_order_release);

		std::coroutine_handle<> handle = asyncState->callerSchedHandle;
		if (!handle) {
			return;
		}

		boost::asio::post(resumeIoContext, handle);
	}

	std::shared_ptr<AsyncState> asyncState;
	boost::asio::io_context &resumeIoContext;
};

template <typename StartFn>
ClKernelCompletionAReq<StartFn> getClKernelCompletionAReqAwaiter(
	boost::asio::io_context &resumeIoContext,
	StartFn startFn)
{
	return ClKernelCompletionAReq<StartFn>(resumeIoContext, std::move(startFn));
}

} // namespace openclBoundary
} // namespace smo

#endif // ADAPTERS_OPENCL_CL_KERNEL_COMPLETION_AREQ_H
