#ifndef _STIMULUS_PRODUCER_H
#define _STIMULUS_PRODUCER_H

#include <vector>
#include <memory>
#include <cstdint>
#include <functional>
#include <iostream>
#include <chrono>
#include <config.h>
#include <boost/asio/io_context.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <spinscale/co/invokers.h>
#include <spinscale/co/nonViralTaskNursery.h>
#include <spinscale/syncCancelerForAsyncWork.h>
#include "deviceAttachmentSpec.h"

namespace smo {
namespace stim_buff {

// Forward declaration
class StimulusBuffer;

/**
 * StimulusProducer manages a collection of stimulus frames with simultaneity stamps.
 *
 * This producer is designed to hold stimulus frames that have been assembled
 * from raw sensor data (e.g., Livox Avia point cloud data) and are ready
 * for processing by the mind layer.
 *
 * The producer provides thread-safe operations for adding frames, retrieving
 * frames, and managing the producer state.
 */
class StimulusProducer
{
public:
	explicit StimulusProducer(
		const std::shared_ptr<device::DeviceAttachmentSpec>
			&deviceAttachmentSpec,
		boost::asio::io_context& ioContext_)
	: daemonTimer(ioContext_),
	deviceAttachmentSpec(deviceAttachmentSpec), ioContext(ioContext_)
	{}

	virtual ~StimulusProducer() = default;

	// Non-copyable, movable
	StimulusProducer(const StimulusProducer&) = delete;
	StimulusProducer& operator=(const StimulusProducer&) = delete;
	StimulusProducer(StimulusProducer&&) = default;
	StimulusProducer& operator=(StimulusProducer&&) = default;

	virtual void start();
	virtual void stop();

	virtual std::shared_ptr<StimulusBuffer> getAttachedStimulusBuffer(
		const std::shared_ptr<device::DeviceAttachmentSpec>& spec) const;

	std::shared_ptr<StimulusBuffer> getAttachedStimulusBufferByAttachIdentity(
		const std::string& deviceIdentifier,
		const std::string& qualeIfaceApi) const;

	virtual std::shared_ptr<StimulusBuffer> getOrCreateAttachedStimulusBuffer(
		const std::shared_ptr<device::DeviceAttachmentSpec>
			&deviceAttachmentSpec) = 0;

	virtual bool exportsQualeIfaceApi(
		const std::string& qualeIfaceApi) const = 0;

	bool addAttachedStimulusBufferIfNotExists(
		const std::shared_ptr<StimulusBuffer>& buffer);

	virtual void destroyAttachedStimulusBuffer(
		const std::shared_ptr<StimulusBuffer>& buffer);

	// Check if any attached buffer has the specified qualeIfaceApi
	bool hasBufferWithQualeIfaceApi(const std::string& qualeIfaceApi) const;

	/** Reject a second buffer for the same qualeIface on this producer session. */
	void ensureNoDuplicateQualeIface(
		const std::string& qualeIfaceApi) const;

protected:
	// Virtual functions for derived classes to override
	virtual int getStopDelayMs() const
	{
		return CONFIG_STIMBUFF_FRAME_PERIOD_MS;
	}

	virtual sscl::co::ViralNonPostingInvoker<void>
		stimFrameProductionTimesliceCInd(
			sscl::SyncCancelerForAsyncWork &canceler) = 0;

	sscl::co::NonViralNonPostingInvoker productionCDaemon(
		std::exception_ptr &exceptionPtr,
		std::function<void()> callback,
		sscl::SyncCancelerForAsyncWork &canceler);

	sscl::co::NonViralTaskNursery taskNursery;
	boost::asio::deadline_timer daemonTimer;
	size_t nTimesliceOverruns = 0;

public:
	std::shared_ptr<device::DeviceAttachmentSpec> deviceAttachmentSpec;
	std::vector<std::shared_ptr<StimulusBuffer>> attachedStimulusBuffers;

private:
	boost::asio::io_context& ioContext;
};

} // namespace stim_buff
} // namespace smo

#endif // _STIMULUS_PRODUCER_H
