#include <yuvStimProducer.h>
#include <yuvChannelStimulusBuffer.h>
#include <lcameraBuffInternal.h>
#include <config.h>
#include <stdexcept>
#define CL_TARGET_OPENCL_VERSION 120
#include <CL/cl.h>
#include <spinscale/syncCancelerForAsyncWork.h>

namespace smo {
namespace stim_buff {
namespace lcamera_buff {

YuvStimProducer::YuvStimProducer(
	const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec,
	boost::asio::io_context& ioContext,
	const std::shared_ptr<lcamera_dev::CameraSession>& _deviceSession,
	const lcamera_dev::CameraIdentityRecord& resolvedIdentity,
	const LcameraBuffParsedParams& _parsedParams,
	const lcamera_dev::LcameraDevConfiguredCameraMode& _configuredMode)
:	StimulusProducer(deviceAttachmentSpec, ioContext),
	deviceSession(_deviceSession),
	resolvedCameraId(resolvedIdentity.id),
	parsedParams(_parsedParams),
	configuredMode(_configuredMode),
	layoutPath(classifyYuvCaptureLayoutPath(_configuredMode)),
	chromaSubsampling(classifyYuvChromaSubsampling(_configuredMode)),
	channelBackingPlan(getChannelBackingPlanForLayoutPath(layoutPath))
{}

bool YuvStimProducer::supportsQualeIfaceApi(const std::string& qualeIfaceApi)
{
	return qualeIfaceApi == "colour-yuv-y"
		|| qualeIfaceApi == "colour-yuv-u"
		|| qualeIfaceApi == "colour-yuv-v";
}

bool YuvStimProducer::exportsQualeIfaceApi(
	const std::string& qualeIfaceApi) const
{
	return supportsQualeIfaceApi(qualeIfaceApi);
}

std::shared_ptr<StimulusBuffer> YuvStimProducer::getOrCreateAttachedStimulusBuffer(
	const std::shared_ptr<device::DeviceAttachmentSpec>& attachSpec)
{
	if (!lcameraBuffSmoHooksPtr)
	{
		throw std::runtime_error(
			"lcameraBuff: SMO hooks not initialized");
	}

	if (!supportsQualeIfaceApi(attachSpec->qualeIfaceApi))
	{
		throw std::runtime_error(
			"lcameraBuff: unsupported qualeIfaceApi '"
			+ attachSpec->qualeIfaceApi + "'");
	}

	const LcameraBuffParsedParams attachParsedParams =
		parseLcameraBuffParams(*attachSpec);

	if (!lcameraBuffParamsEqual(attachParsedParams, parsedParams))
	{
		throw std::runtime_error(
			"lcameraBuff: conflicting stimbuff-api params across Y/U/V "
			"attachments for camera '" + resolvedCameraId + "'");
	}

	auto existingBuffer = getAttachedStimulusBuffer(attachSpec);
	if (existingBuffer) {
		return existingBuffer;
	}

	/* One YuvStimProducer == one libcamera session. A second DAP line may use
	 * a different deviceSelector yet resolve to this same session; it must not
	 * attach the same qualeIface API twice.
	 */
	if (hasBufferWithQualeIfaceApi(attachSpec->qualeIfaceApi))
	{
		throw std::runtime_error(
			"lcameraBuff: qualeIface '" + attachSpec->qualeIfaceApi
			+ "' is already attached for camera session '"
			+ resolvedCameraId + "'; each producer allows one buffer per "
			"qualeIface API");
	}

	const YuvChannelKind channelKind =
		YuvChannelStimulusBuffer::yuvChannelKindFromQualeIfaceApi(
			attachSpec->qualeIfaceApi);

	const size_t channelByteSize = computeDeinterleavedChannelByteSize(
		channelKind,
		configuredMode.width,
		configuredMode.height,
		chromaSubsampling);

	auto channelBuffer = std::make_shared<YuvChannelStimulusBuffer>(
		*this,
		attachSpec,
		CONFIG_STIMBUFF_FRAME_PERIOD_MS,
		channelKind,
		channelByteSize,
		*lcameraBuffSmoHooksPtr,
		CL_MEM_READ_WRITE);

	if (!addAttachedStimulusBufferIfNotExists(channelBuffer))
	{
		if (hasBufferWithQualeIfaceApi(attachSpec->qualeIfaceApi))
		{
			throw std::runtime_error(
				"lcameraBuff: qualeIface '"
				+ attachSpec->qualeIfaceApi
				+ "' is already attached for camera session '"
				+ resolvedCameraId + "'");
		}

		throw std::runtime_error(
			"lcameraBuff: duplicate stimbuff attachment for "
			+ attachSpec->stringify());
	}

	return channelBuffer;
}

void YuvStimProducer::destroyAttachedStimulusBuffer(
	const std::shared_ptr<StimulusBuffer>& buffer)
{
	StimulusProducer::destroyAttachedStimulusBuffer(buffer);
}

void YuvStimProducer::start()
{
	// Stage 2: setup-only attach; capture daemon deferred to Stage 8.
}

void YuvStimProducer::stop()
{
	// Stage 2: setup-only attach; capture daemon deferred to Stage 8.
}

sscl::co::ViralNonPostingInvoker<void>
YuvStimProducer::stimFrameProductionTimesliceCInd(
	sscl::SyncCancelerForAsyncWork& canceler)
{
	(void)canceler;
	throw std::logic_error(
		"lcameraBuff: capture daemon not implemented in Stage 2");
	co_return;
}

} // namespace lcamera_buff
} // namespace stim_buff
} // namespace smo
