#ifndef _TRANSMITTER_H_
#define _TRANSMITTER_H_

#include <cstdint>
#include <radio/symbol.h>
#include <radio/interpolation.h>
#include <radio/Frame.hpp>

#include "JuceHeader.h"
#include "LogComponent.h"

#include <radio/Vector.hpp>
#include <radio/Nco.hpp>
#include <radio/Interpolation.hpp>

using namespace Radio;

#define TRANSMITTER_RCF_OVERSAMPLING		((uint32_t)32)
#define TRANSMITTER_RCF_ROLLOFF				((radio_float_t)0.35)

class Transmitter;
// ---------------------------------------------------
class TransmitterInterface
{
public:
	TransmitterInterface() {}
	virtual ~TransmitterInterface() {}

	virtual void reset() = 0;
	virtual void setBuffersize(uint32_t size) = 0;
	virtual void setCarrierFrequency(float frequency_hz) = 0;
	virtual void setSamplerate(float samplerate_hz) = 0;
	virtual void setSymbolRate(float symbolrate_bit_per_sec) = 0;
	virtual void setNumBitPerSymbol(uint32_t numBitPerSymbol) = 0;
	virtual void getData(float *pRF, uint32_t len) = 0;

};

class TxBuffer
{
public:	
	TxBuffer(uint32_t size, uint32_t numBuffers)
		: m_ppBuffer(0)
		, m_capacityPerBuffer(0)
		, m_numBuffers(0)
		, m_pT(0)
	{
		resize(size, numBuffers);
	}

	~TxBuffer()
	{
		resize(0, 0);
	}

	void write(RVec const &data, uint32_t len)
	{
		uint32_t i;

		m_pT = Thread::getCurrentThread();
		while (m_numfill.get() >= m_numBuffers)
		{
			m_pT->wait(100);
			if (m_pT->threadShouldExit())
				return;
		}
		for (i=0; i < len; i++)
		{
			m_ppBuffer[m_w][i] = data[i];
		}
		m_w++;
		if (m_w >= m_numBuffers)
			m_w = 0;

		m_numfill += 1;
	}

	uint32_t read(float *pData)
	{
		uint32_t i;

		if (m_numfill.get() == 0)
			return 0;

		for (i=0; i < m_capacityPerBuffer; i++)
		{
			pData[i] = m_ppBuffer[m_r][i];
		}
		m_r++;
		if (m_r >= m_numBuffers)
			m_r = 0;

		m_numfill -= 1;

		if (m_pT)
			m_pT->notify();

		return i;
	}

	void resize(uint32_t size, uint32_t numBuffers)
	{

		uint32_t i;
		if ((size != m_capacityPerBuffer) || (numBuffers != m_numBuffers))
		{
			if (m_ppBuffer)
			{
				for (i=0; i < m_numBuffers; i++)
				{
					delete m_ppBuffer[i];
				}
				delete m_ppBuffer;
				m_ppBuffer = nullptr;
			}
			m_r = 0;
			m_w = 0;
			m_numfill = 0;
		}

		m_numBuffers = numBuffers;
		m_capacityPerBuffer = size;

		if (!size || !numBuffers)
			return;

		m_ppBuffer = new float*[m_numBuffers];
		for (i=0; i < m_numBuffers; i++)
		{
			m_ppBuffer[i] = new float[m_capacityPerBuffer];
		}
	}

private:
	Atomic<uint32_t> m_numfill;
	float **m_ppBuffer;
	uint32_t m_capacityPerBuffer;
	uint32_t m_numBuffers;
	uint32_t m_r;
	uint32_t m_w;
	Thread *m_pT;
};

class Transmitter : public TransmitterInterface, public ISymbolizer, public juce::Thread
{
public:
	Transmitter(void);
	~Transmitter(void);

	void bufferResize(uint32_t size);

private:
    CriticalSection m_lock;
	Symbolizer m_symbolizer;
	Formatter m_formatter;
	sym_map_t m_symbolMapper;
	Interpolation::UpSampler m_firUp;
	uint32_t m_bufferSize;
	RVec m_bufferRF;
	CVec m_bufferSym;
	CVec m_bufferSymRcf;
	float m_samplerate;
	float m_ddcFrequency;
	float m_symbolrate;
	uint32_t m_numBitPerSymbol;
	radio_float_t *m_pCoeffRCF;
	radio_float_t m_kRcf;
	radio_float_t m_carrierFreq;
	Nco m_duc;
	
	TxBuffer m_txBuf;
	uint32_t m_symCount;
	uint32_t m_upsampleFactor;

	// Thread
	void run() override
	{
		uint32_t data;

		data = 0;
		while(!threadShouldExit())
		{
			m_formatter.process(&data, sizeof(data));
			data++;
		}
	}

	// ISymbolizer
	void onSymbol(symbol_t symbol) override
	{
		uint32_t symCountRcf;
		const ScopedLock sl (m_lock);

		m_bufferSym[m_symCount++] = toComplexScalar(SymMapMap(&m_symbolMapper, symbol));

		if (m_symCount >= m_bufferSize/m_upsampleFactor)
		{
			symCountRcf = m_firUp.process(m_bufferSymRcf, m_bufferSym, m_symCount);
			m_duc.mixComplexRealV(m_bufferRF, m_bufferSymRcf, ComplexScalar(m_kRcf/4, m_kRcf/4), symCountRcf);
			m_txBuf.write(m_bufferRF, symCountRcf);
			m_symCount = 0;
		}
	}

	void paramChanged();

	// TransmitterInterface
	void reset() override
	{
	}
	void setBuffersize(uint32_t size) override
	{
		bufferResize(size);
		paramChanged();
	}
	void setSamplerate(float samplerate_hz) override
	{
		m_samplerate = samplerate_hz;
		m_carrierFreq = samplerate_hz/4;
		m_symbolrate = samplerate_hz/4;
		paramChanged();
	}
	void setCarrierFrequency(float frequency_hz) override
	{
		const ScopedLock sl (m_lock);
		m_carrierFreq = frequency_hz;
		m_duc.init(m_carrierFreq/m_samplerate, 0);
	}
	void setSymbolRate(float symbolrate_bit_per_sec) override
	{
		m_symbolrate = symbolrate_bit_per_sec;
		paramChanged();
	}
	void setNumBitPerSymbol(uint32_t numBitPerSymbol) override
	{
		m_numBitPerSymbol = numBitPerSymbol;
		if (m_numBitPerSymbol > 16)
			m_numBitPerSymbol = 16;

		paramChanged();
	}
	void getData(float *pRF, uint32_t len) override
	{
		uint32_t res;

		if (len != m_bufferSize)
		{
			memset(pRF, 0, len*sizeof(float));
			return;
		}

		res = m_txBuf.read(pRF);

		if (!res)
			memset(pRF, 0, len*sizeof(float));

	}
};

#endif // _TRANSMITTER_H_
