#ifndef _INTERPOLATION_DOWNSAMPLER_HPP_
#define _INTERPOLATION_DOWNSAMPLER_HPP_

#pragma once

#include <cpp/radio/Vector.hpp>
#include <cpp/radio/FirComplex.hpp>

namespace Radio
{
namespace Interpolation
{

// --------------------------------------------------------------
// Complex FIR-based DownSampler
// --------------------------------------------------------------

class DownSampler
{
public:
	DownSampler()
	: m_pFir(nullptr)
	, m_pCoeff(nullptr)
	, m_M(0)
	, m_currStage(0)
	{
		
	}
	~DownSampler()
	{
		if (m_pFir)
			delete [] m_pFir;
			
		if (m_pCoeff)
			delete [] m_pCoeff;
		
	}
	void init(uint32_t M, RealScalar const *coeff, uint32_t N)
	{
		uint32_t i, j;
		uint32_t stage_N;
		uint32_t stage;
		
		if (m_pFir)
			delete [] m_pFir;
			
		if (m_pCoeff)
			delete [] m_pCoeff;

		m_pFir = new FirComplex[M];
		m_pCoeff = new RVec[M];
		
		for (stage=0; stage < M; stage++)
		{
			stage_N = 0;
			for (i=stage; i < N; i += M)
			{
				stage_N++;
			}
			m_pFir[stage].setNumTaps(stage_N);
			m_pCoeff[stage].resize(stage_N);
			
			j=0;
			for (i=stage; i < N; i += M)
			{
				if (coeff)
				{
					m_pCoeff[stage][j++] = coeff[i];
				}
			}
		}
		m_M = M;
		m_currStage = m_M-1;

	}
	
	uint32_t process(CVec &dst, CVec const &src, uint32_t len)
	{
		uint32_t i;
		uint32_t j;
		
		j = 0;
		for (i=0; i < len; i++)
		{
			m_pFir[m_currStage].feed(src[i]);
			if (m_currStage == (m_M-1))
			{
				dst[j] = m_pFir[m_currStage].processReal(m_pCoeff[m_currStage]);
			}
			else
			{
				dst[j] += m_pFir[m_currStage].processReal(m_pCoeff[m_currStage]);
			}
			if (--m_currStage >= m_M)
			{
				m_currStage = m_M-1;
				j++;
			}
			
		}
		return j;
	}
	
private:
	FirComplex *m_pFir;
	RVec *m_pCoeff;
	uint32_t m_M;
	uint32_t m_currStage;
	
};

} // Radio::Interpolation
} // ::Radio

#endif // _INTERPOLATION_DOWNSAMPLER_HPP_