#ifndef _RECEIVER_H_
#define _RECEIVER_H_

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

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

#include <radio/FirComplex.hpp>
#include <radio/Equalizer.hpp>
#include <radio/Interpolation.hpp>
#include <radio/Nco.hpp>

using namespace Radio;

// ---------------------------------------------------
#define NUM_PASSBAND_SAMPLES_PER_SYM	4
#define NUM_BASEBAND_SAMPLES_PER_SYM	2
#define	MODE_PROCESS_PASSBAND			0
#define	MODE_PROCESS_BASEBAND			1
#define	RCF_TYPE_POLYPHASE_DISCRETE		0
#define	RCF_TYPE_POLYPHASE_FARROW		1

#define EQ_ENABLE						1
#define MODULATION_TYPE					MAP_MODE_QAM
#define RCF_TYPE						RCF_TYPE_POLYPHASE_FARROW
#define MAX_NUMBITS_PERSYM				16
// ---------------------------------------------------
enum eq_mode
{
	eq_mode_disabled,
	eq_mode_cma,
	eq_mode_dfe
};

enum cma_type
{
	cma_type_cma = 0,
	cma_type_mma,
	cma_type_smma
};

enum cma_mode
{
	cma_mode_training_disabled = 0,
	cma_mode_training_enabled
};

enum dfe_mode
{
	dfe_mode_training_disabled = 0,
	dfe_mode_training_enabled
};

enum agc_mode
{
	agc_mode_disabled = 0,
	agc_mode_enabled
};

enum agc_state
{
	agc_state_acquisition = 0,
	agc_state_track
};

enum cpr_mode
{
	cpr_mode_disabled = 0,
	cpr_mode_enabled
};

enum cpr_state
{
	cpr_state_acquisition = 0,
	cpr_state_track
};

enum str_mode
{
	str_mode_disabled = 0,
	str_mode_enabled
};

enum str_state
{
	str_state_acquisition = 0,
	str_state_track
};

typedef struct _minmax_t
{
	float min, max;

} minmax_t;

typedef struct _cpx_minmax_t
{
	cpx_t min, max;
	float magMin, magMax;
	float phiAtMinI;
	float phiAtMaxI;
	float phiAtMinQ;
	float phiAtMaxQ;

} cpx_minmax_t;

typedef struct _statistics_t
{
	sl_var_t RF;
	sl_var_t I_DDC;
	sl_var_t Q_DDC;
	sl_var_t I_MF;
	sl_var_t Q_MF;
	sl_var_t I_CPR;
	sl_var_t Q_CPR;
	sl_var_t I_AGC;
	sl_var_t Q_AGC;
	sl_var_t I_EQ;
	sl_var_t Q_EQ;
	sl_var_t I_decision;
	sl_var_t Q_decision;
	sl_var_t MagDecision;
	sl_var_t PhiDecision;
	sl_var_t sym_err_mag;
	sl_var_t sym_err_phi;
	sl_var_t noise;
	sl_var_t noise_cma;
	sl_var_t noise_str;
	sl_var_t noise_cpr;
	sl_var_t noise_dfe_on;
	sl_var_t noise_dfe_off;
	sl_minmax_t sl_min_I;
	sl_minmax_t sl_max_I;
	sl_minmax_t sl_min_Q;
	sl_minmax_t sl_max_Q;
	sl_minmax_t sl_min_mag;
	sl_minmax_t sl_max_mag;

	cpx_minmax_t ddcMinMax;

} statistics_t;

typedef struct _params_t
{
	uint32_t numBitsPerSymbol;
	float symbolrate;
	float samplerate;
	float ddc_freq;
	float CPR_phase;
	bool dfeAutoUpdateEnable;
	bool strAutoControlEnable;
	bool cprAutoControlEnable;
	bool agcAutoControlEnable;
	enum str_mode str_mode;
	enum cpr_mode cpr_mode;
	enum agc_mode agc_mode;
	enum eq_mode eq_mode;
	enum cma_type cmaType;
	enum cma_mode cmaMode;
	enum dfe_mode dfeMode;
	lead_lag_coeff_t str_loopfilter_coeff[2];
	uint32_t str_loopfilter_coeff_index;
	lead_lag_coeff_t cpr_loopfilter_coeff[2];
	uint32_t cpr_loopfilter_coeff_index;

	float eqMuCma;
	float eqMuDfe;
	float agcMu[2];
	uint32_t agcMu_index;

	enum str_state strState;
	enum cpr_state cprState;
	enum agc_state agc_state;

} params_t;

typedef struct _status_t
{
	uint32 numSymbolsReceived;
	frame_statistics_t frameStatRx;

	float noiseStr;
	float noiseCpr;

	float EB_N0;
	float powerRF_dB;
	float powerDDC_dB;
	float powerMF_dB;
	float powerCPR_dB;
	float powerAGC_dB;
	float powerEQ_dB;
	float powerDecison_dB;
	float powerMagDecision_dB;
	float powerPhiDecision_dB;

	float snrCurrent_dB;
	float snrCma_dB;
	float snrDfeOn_dB;
	float snrDfeOff_dB;
	float snrSymbolMagnitude_dB;
	float snrSymbolPhase_dB;

	float deltaFrequencyCPR;
	float deltaFrequencySTR;

	float agcGain[2];

	minmax_t rfMinMax;
	cpx_minmax_t ddcMinMax;

} status_t;

class Receiver;
// ---------------------------------------------------
class ReceiverInterface
{
public:
	ReceiverInterface() {}
	virtual ~ReceiverInterface() {}

	virtual void setSamplerate(float samplerate_hz) = 0;

	virtual void setParams(const params_t &params) = 0;
	virtual const params_t& getParams() = 0;
	virtual const status_t& getStatus() = 0;
	virtual const cpx_t& getTracker(uint32 index) = 0;
	virtual void reset() = 0;
	virtual void statisticsReset() = 0;
	virtual void strReset() = 0;
	virtual void cprReset() = 0;
	virtual void cmaReset() = 0;
	virtual void dfeReset() = 0;
	virtual void dfeOffUpdateFromCma() = 0;
	virtual void dfeOnUpdateFromDfeOff() = 0;
	virtual const CVec& getWeightsCMA() = 0;
	virtual const CVec& getWeightsDFE() = 0;

};

class ReceiverStatusListener
{
public:
	ReceiverStatusListener() {}
	virtual ~ReceiverStatusListener() {}

	virtual void receiverStatusChanged(ReceiverInterface *pReceiver) = 0;
};

class ReceiverDataListener
{
public:
	ReceiverDataListener() {}
	virtual ~ReceiverDataListener() {}
	virtual void receiverDataChanged(Receiver *pObj) = 0;
};

class Receiver : public ReceiverInterface, public IDeFormatter, public Timer
{
public:
	Receiver(LogHandler *pLogHandler);
	~Receiver(void);
	void init();
	void free();
	void processBaseband(float *pI, float *pQ, uint32_t len);
	void processPassband(RVec const &rf, uint32_t len);
	void setBufSize(uint32 size);
	uint32 getNumSoftSym();
	sym_err_t getSoftSym(uint32 index);
	sym_err_t* getSoftSyms();
	void initDefaultParams();
	void addStatusListener(ReceiverStatusListener *pListener);
	void addDataListener(ReceiverDataListener *pListener);
private:
	ListenerList<ReceiverStatusListener> m_statusListeners;
	LogComponent m_log;
	ReceiverDataListener *m_pDataListener;
    CriticalSection m_lock;
	cpx_t *m_pPassbandBuffer;
	cpx_t *m_pBasebandBuffer;
	CVec m_passbandBuffer;
	CVec m_basebandBuffer;
	
	sym_err_t *m_pSymbolBuffer;
	uint32_t m_numSymsInBuffer;
	uint32_t m_bufsize;
	bool m_ReceiverEnable;
	sample_clock_t m_symClock;
	pfd_t m_pfdCpr;
	cpx_t m_trackers_[4];
	cpx_t m_trackers[4];
	
	// Filter 
	Interpolation::DownSampler m_firArmDown;

	// Interpolators
	Interpolation::PolyPhase m_polyPhase;
	Interpolation::Farrow m_farrow;

	// NCOs
	Nco m_nco_ddc, m_nco_cpr;
	radio_float_t m_dOmega_vco;
	
	// Loop-Filter
	lead_lag_filter_t m_loop_filter_str;
	lead_lag_filter_t m_loop_filter_cpr;

	// Gardner Symbol Timing Recovery
	str_t m_SymbolTimingRevovery;
	timing_corrector_t m_timing_corrector;
	radio_float_t m_Vc;

	// AGC
	// Blind
	agc_t agcBlind;

	// Equalizer
	// Decision directed EQ
	Equalizer::Dfe m_dfe_on2;
	Equalizer::Dfe m_dfe_off2;

	cpx_t m_dfe_e_cpx;

	// Blind EQ
	Equalizer::Cma m_cma2;
	
	// Symbol demapper
	sym_map_t *m_pSymMapper;

	// Staticstics
	sym_stat_t m_sym_stat;
	statistics_t m_statistics;

	FrameReceiver m_frameReceiver;
	params_t m_params;

	void timerCallback();
	void processBaseband(CVec const &iq, uint32_t len);
	void minMaxInit(minmax_t *pObj, radio_float_t min_initial, radio_float_t max_initial)
	{
		pObj->min = min_initial;
		pObj->max = max_initial;
	}
	void minMaxInit(cpx_minmax_t *pObj, cpx_t min_initial, cpx_t max_initial)
	{
		pObj->min = min_initial;
		pObj->max = max_initial;
		pObj->magMin =  1000;
		pObj->magMax = -1000;
	}
	void minMaxProcess(minmax_t *pObj, radio_float_t x)
	{
		pObj->min = dmin(pObj->min, x);
		pObj->max = dmax(pObj->max, x);
	}
	void minMaxProcess(cpx_minmax_t *pObj, cpx_t x)
	{
		if (CpxMagS(x) >= pObj->magMax)
		{
			if (x.real > 0 && x.imag > 0)
				pObj->phiAtMaxI = CpxPhiS(x);
			if (x.real > 0 && x.imag < 0)
				pObj->phiAtMaxQ = CpxPhiS(x);
			if (x.real < 0 && x.imag < 0)
				pObj->phiAtMinQ = CpxPhiS(x);
			if (x.real < 0 && x.imag > 0)
				pObj->phiAtMinI = CpxPhiS(x);
		}
		pObj->min.real = SlidingMinMaxProcess(&m_statistics.sl_min_I, x.real);
		pObj->max.real = SlidingMinMaxProcess(&m_statistics.sl_max_I, x.real);
		pObj->min.imag = SlidingMinMaxProcess(&m_statistics.sl_min_Q, x.imag);
		pObj->max.imag = SlidingMinMaxProcess(&m_statistics.sl_max_Q, x.imag);
		pObj->magMin = SlidingMinMaxProcess(&m_statistics.sl_min_mag, CpxMagS(x));
		pObj->magMax = SlidingMinMaxProcess(&m_statistics.sl_max_mag, CpxMagS(x));

	}

	void initFilterArm();
	void initFilterRcf();
	void initSymbolMapper();
	void initSTR();
	void initCPR();
	void initDDC();

	// Interface
	void setSamplerate(float samplerate_hz) override;
	void setParams(const params_t &params) override;
	params_t& getParams() override;
	status_t& getStatus() override;
	void reset() override;
	void strReset() override;
	void cprReset() override;
	void dfeReset() override;
	void cmaReset() override;
	void statisticsReset() override;
	void dfeOffUpdateFromCma() override;
	void dfeOnUpdateFromDfeOff() override;
	cpx_t& getTracker(uint32 index) override;

	void onData(void *pData, uint32_t size) override
	{
		// Received raw data		
		(void)pData;
		(void)size;
	}

	const CVec& getWeightsCMA() override
	{
		return m_cma2.getWeights();
	}

	const CVec& getWeightsDFE() override
	{
		return m_dfe_off2.getWeights();
	}

	trace_t m_trace;
};

#endif // _RECEIVER_H_
