/*
 *
 *
 *    dec_file.cc
 *
 *    File-based decoder for NWS/EAS alert messages.
 *
 *    Copyright (C) 2018 by Matt Roberts.
 *    License: GNU GPL3 (www.gnu.org)
 *
 *
 */

#include "nlimits.h"
#include "dec_file.h"
#include "FirFilter.h"

#include <iostream>

//#define USE_PRESELECTOR
//#define CLIP_DETECTION

// the number of samples to read at once
#define BLOCK_SIZE (8 * 1024)

using namespace std;
using namespace KK5JY::DSP;

//
//  FileDecoder::ctor
//
FileDecoder::FileDecoder(const std::string &fn, const detector_t::Config &c)
	: demod(c),
	  decoder(c.fs / c.bps),
	  filen(fn) {
	// nop
}


//
//  FileDecoder::run()
//
int FileDecoder::run() {
	// the I/O buffer
	sample_t buffer[BLOCK_SIZE];

	// open the WAV file for reading
	SoundFile sf;
	try {
		sf.open(filen.c_str());
	} catch (std::exception &ex) {
		cerr << "Error: " << ex.what() << endl;
		return 2;
	}

#ifdef USE_PRESELECTOR
	// create a preselector
	fir_filter_t preselector(fir_filter_t::Types::BandPass, FILTER_LEN, 1400, 2200, MODEM_FS);
#endif

	if (sf.rate() == MODEM_FS) {
		uint32_t count = 0;
		uint32_t limit = sf.frames();
		while (count < limit) {
			// read data
			sf_count_t read = sf.read(buffer, BLOCK_SIZE);
			count += read;

			sample_t *in = buffer;
			for (uint32_t i = 0; i != read; ++i) {
				sample_t sample = *in++;
#ifdef CLIP_DETECTION
				if (abs(sample) >= norm_limits<sample_t>::maximum)
					cerr << "IN_CLIP" << endl;
#endif
#ifdef USE_PRESELECTOR
				// BPF the noisy audio
				sample = preselector.filter(sample);
#ifdef CLIP_DETECTION
				if (abs(sample) >= norm_limits<sample_t>::maximum)
					cerr << "PS_CLIP" << endl;
#endif
#endif
				// run the discriminator
				bool output = demod.run(sample);

				// and process it
				decoder.process_sample(output);
			}
		}
	} else if (sf.rate() > MODEM_FS) {
		// DEBUG:
		cerr << "DEBUG: decimating " << sf.rate() << " to " << MODEM_FS << endl;

		// create a decimation filter
		fir_filter_t diFilter(fir_filter_t::Types::LowPass, FILTER_LEN, FILTER_COF, sf.rate());

		const size_t factor = sf.rate() / MODEM_FS;
		size_t di_ctr = 0;

		uint32_t count = 0;
		uint32_t limit = sf.frames();
		while (count < limit) {
			// read data
			sf_count_t read = sf.read(buffer, BLOCK_SIZE);
			count += read;

			sample_t *in = buffer;
			for (uint32_t i = 0; i != read; ++i) {
				// read the FSK data
				sample_t sample = *in++;
#ifdef CLIP_DETECTION
				if (abs(sample) >= norm_limits<sample_t>::maximum)
					cerr << "IN_CLIP" << endl;
#endif
				// decimate
				sample = diFilter.filter(sample);
#ifdef CLIP_DETECTION
				if (abs(sample) >= norm_limits<sample_t>::maximum)
					cerr << "DEC_CLIP" << endl;
#endif
				if (++di_ctr != factor) {
					continue;
				}
				di_ctr = 0;

#ifdef USE_PRESELECTOR
				// BPF the noisy audio
				sample = preselector.filter(sample);
#ifdef CLIP_DETECTION
				if (abs(sample) >= norm_limits<sample_t>::maximum)
					cerr << "PS_CLIP" << endl;
#endif
#endif
				// run the discriminator
				bool output = demod.run(sample);

				// and process it
				decoder.process_sample(output);
			}
		}
	} else {
		// DEBUG:
		cerr << "DEBUG: interpolating " << sf.rate() << " to " << MODEM_FS << endl;

		// create a decimation filter
		fir_filter_t diFilter(fir_filter_t::Types::LowPass, FILTER_LEN, FILTER_COF, MODEM_FS);

		const size_t factor = MODEM_FS / sf.rate();

		uint32_t count = 0;
		uint32_t limit = sf.frames();
		while (count < limit) {
			// read data
			sf_count_t read = sf.read(buffer, BLOCK_SIZE);
			count += read;

			sample_t *in = buffer;
			for (uint32_t i = 0; i != read; ++i) {
				// read the FSK data
				sample_t sample = *in++;
#ifdef CLIP_DETECTION
				if (abs(sample) >= norm_limits<sample_t>::maximum)
					cerr << "IN_CLIP" << endl;
#endif
				// interpolate
				for (uint16_t j = 0; j != factor; ++j) {
					// upsampling filter
					sample = diFilter.filter(sample);
#ifdef CLIP_DETECTION
					if (abs(sample) >= norm_limits<sample_t>::maximum)
						cerr << "INT_CLIP" << endl;
#endif
#ifdef USE_PRESELECTOR
					// BPF the noisy audio
					sample = preselector.filter(sample);
#ifdef CLIP_DETECTION
					if (abs(sample) >= norm_limits<sample_t>::maximum)
						cerr << "PS_CLIP" << endl;
#endif
#endif
					// run the discriminator
					bool output = demod.run(sample);

					// and process it
					decoder.process_sample(output);
				}
			}
		}
	}

	return 0;
}
