#ifndef _STIMULUS_PRODUCER_H
#define _STIMULUS_PRODUCER_H

#include <boostAsioLinkageFix.h>
#include <vector>
#include <memory>
#include <cstdint>
#include <atomic>
#include <mutex>
#include <functional>
#include <iostream>
#include <chrono>
#include <config.h>
#include <boost/asio/io_service.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <spinscale/spinLock.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_service& ioService_)
	: deviceAttachmentSpec(deviceAttachmentSpec),
	ioService(ioService_),
	shouldContinue(false), timer(ioService),
	nDeferrals(0)
	{}

	virtual ~StimulusProducer() = default;

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

	// Control methods
	virtual void start()
	{
		std::cout << __func__ << ": Starting stimulus producer for device "
			<< deviceAttachmentSpec->deviceSelector << std::endl;

		shouldContinue = true;
		nDeferrals = 0;
		scheduleNextTimeout();
	}

	virtual void stop();

	void allowNextStimulusFrame()
		{ frameAssemblyRateLimiter.release(); }

	virtual std::shared_ptr<StimulusBuffer> getAttachedStimulusBuffer(
		const std::shared_ptr<device::DeviceAttachmentSpec>& spec) 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;

protected:
	sscl::SpinLock frameAssemblyRateLimiter;

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

	virtual void stimFrameProductionTimesliceInd() = 0;

private:
	void onTimeout(const boost::system::error_code& error);

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

private:
	boost::asio::io_service& ioService;
protected:
	sscl::SpinLock shouldContinueLock;
	bool shouldContinue;
private:
	boost::asio::deadline_timer timer;
	size_t nDeferrals;
	std::chrono::high_resolution_clock::time_point deferralStartTime;

	void scheduleNextTimeout(int delayMs = CONFIG_STIMBUFF_FRAME_PERIOD_MS);
};

} // namespace stim_buff
} // namespace smo

#endif // _STIMULUS_PRODUCER_H
