#pragma once

#include "QGPlugin.h"

#include <atomic>
#include <complex>
#include <memory>
#include <mutex>
#include <vector>

#include <yaml-cpp/yaml.h>

class QGInputDevice: public QGPlugin {
public:
	static std::vector<std::pair<std::string, std::string>> listModules();
	static std::vector<std::pair<std::string, std::vector<std::string>>> listDevices();

	static std::unique_ptr<QGInputDevice> CreateInputDevice(
		int index,
		const YAML::Node &config
	);

	QGInputDevice(std::string const& pluginId, int index, YAML::Node const &config);
	~QGInputDevice() override = default;

    QGInputDevice (const QGInputDevice &) = delete;
    QGInputDevice & operator = (const QGInputDevice &) = delete;

	void run();
	void stop();

	const std::string& type() const { return _type; };
	unsigned int sampleRate() const { return _sampleRate; };

	// Choosen target frequency, used by default in image header and filenames
	// Always the one specified in the config file's input section
	double dispFreq() const { return _dispFreq; };
	// Tuning frequency. Adjusted to compensate ppm error. Used to set up tuner
	// Can also be adjusted with an offset for transverter operation
	double tunerFreq() const { return _tunerFreq; };
	// Center frequency of the FFT. Usually the same as _dispFreq, unless no or
	// only a partial ppm correction could be performed, in latter case it is
	// adjusted by the ppm error (ie for alsa input with ppm specified)
	double centerFreq() const { return _centerFreq; };
	// PPM error for correction in software (adjusts the tuner frequency).
	// If not possible or only partially it will adjust the output image
	// In case the driver supports setting a ppm, it will be corrected
	// to contain only the residual part, if any (rtlsdr only supports int)
	double ppm() const { return _ppm; };

protected:
	void _checkSourceOk(std::shared_ptr<QGPlugin> const & source) override;
	void _checkSinkOk(std::shared_ptr<QGPlugin> const & sink) override;

	// A device can update the ppm error, like when the driver supports ppm
	// correction. It can be updated to 0 or another value if the correction is
	// only partial, like only an integer correction is supported like for rtlsdr
	void _updatePPM(double ppm);

	// A device can update the tuner frequency, if for example only specific
	// frequencies are supported
	// If _updatePPM() is needed as well, _updatePPM() must be called
	// before _updateTunerFreq() as _updatePPM() will recalculate the theoretical
	// tuner frequency
	void _updateTunerFreq(double freq);

	// A device can have no tuner involved, like for alsa input.
	// In this case the whole ppm correction is done in the output image
	void _noTuner();

	// TODO: use QGPlugin::name() and remove _type
	std::string _type;
	unsigned int _sampleRate;

	bool _noDevice;

	int _bufferlength;
	unsigned int _bufferCapacity;
	std::atomic<unsigned int> _bufferSize;
	unsigned int _bufferHead;
	unsigned int _bufferTail;
	std::vector<std::complex<float>> _buffer;

	std::atomic<bool> _running;

private:
	void _calcTunerFreq();
	void _calcCenterFreq();
	void _bufferMonitor();

	virtual void _startDevice() {};
	virtual void _stopDevice() {};

	double _dispFreq;
	double _tunerFreq;
	double _centerFreq;

	double _ppm;

	double _offset;
	double _offsetPpm;

	unsigned int _chunkSize;

	bool _debugBufferMonitor;
};
