/*
 *
 *
 *    nwsrx.cc
 *
 *    Experimental NOAA NWS / EAS data decoder.
 *
 *    Copyright (C) 2018 by Matt Roberts.
 *    License: GNU GPL3 (www.gnu.org)
 *
 *
 */

#include <unistd.h>
#include <stdint.h>
#include <iostream>
#include <iomanip>
#include <deque>
#include <cstring>
#include <bitset>
#include <climits>
#include <algorithm>

#include "bitclock.h"
#include "sf.h"
#include "sc.h"

#include "decoder.h"
#include "dec_file.h"
#include "dec_card.h"
#include "localtypes.h"

using namespace std;

// mid-way between the two tones
const double F0 = 1822.9;

// the signaling rate (bps)
const size_t bitRate = 521;

// the normalized filter 'Q'
const double R = 0.9675; //0.985;

// total shift (Hz)
const double shift = 520.8;

// integrator length
const size_t intlen = 3;

// trigger level
const size_t trigger = 0.15;

// SFC divisor (selective fading compensation)
const size_t sfc_divisor = 13;


//
//
//   main(...)
//
//
int main (int argc, char ** argv) {
	if (argc < 2 || argc > 3) {
		cerr << endl;
		cerr << "Usage: nwsrx <input_audio_file>" << endl;
		cerr << "       nwsrx -c <sound_device_number>" << endl;
		cerr << "       nwsrx -l" << endl;
		cerr << endl;
		return 1;
	}

	// list devices (-l option)
	if (argc == 2 && strcmp(argv[1], "-l") == 0) {
	    RtAudio adc(RtAudio::LINUX_ALSA);

		// Determine the number of devices available
		unsigned int devices = adc.getDeviceCount();
		if (devices == 0) {
			std::cout << "No audio devices found." << std::endl;
			return 0;
		}

		// Scan through devices for various capabilities
		RtAudio::DeviceInfo info;
		cout << "Valid devices:" << endl;
		for (unsigned int i = 0; i < devices; i++) {
			info = adc.getDeviceInfo(i);
			if (info.probed == true) {
				// Print, for example, the maximum number of output channels for each device
				cout << " + Device ID = " << i;
				cout << ": \"" << info.name << "\"";
				/*
				cout << ", inputs = " << channelsToString(info.inputChannels);
				cout << ", outputs = " << channelsToString(info.outputChannels);
				cout << ", rates = " << ratesToString(info.sampleRates);
				*/
				cout << endl;
			}
		}

		// done
		return 0;
	}

	bool realtime = false;
	int card = 0;
	if (argc == 3 && strcmp(argv[1], "-c") == 0) {
		card = atoi(argv[2]);
		realtime = true;
	}

	// build the discriminator
	detector_t::Config c;
	c.f0 = F0;
	c.fs = MODEM_FS;
	c.shift = shift;
	c.R = R;
	c.int_len = intlen;
	c.trigger = trigger;
	c.bps = bitRate;
	c.sfc_divisor = sfc_divisor;

	#ifdef DEMOD_DEBUG
	// column width for output
	const size_t COLUMN_WIDTH = 12;

	cerr << "Using Fs = " << MODEM_FS << "; f0 = " << F0 << endl;
	cerr << "Using bps = " << c.bps << endl;
	cerr
		<< setw(6) << left << "i"
		<< setw(6) << "ms"
		<< setw(COLUMN_WIDTH) << "x(t)"
		<< setw(COLUMN_WIDTH) << "tpf(i)"
		<< setw(COLUMN_WIDTH) << "high"
		<< setw(COLUMN_WIDTH) << "low"
		<< setw(COLUMN_WIDTH) << "|high|"
		<< setw(COLUMN_WIDTH) << "|low|"
		<< setw(COLUMN_WIDTH) << "dev"
		<< setw(COLUMN_WIDTH) << "(%)"
		<< setw(COLUMN_WIDTH) << "SF"
		<< setw(COLUMN_WIDTH) << "in"
		<< setw(COLUMN_WIDTH) << "out"
		<< endl;
	int16_t freq = 0;
	#endif

	if (realtime) {
		//
		//  real-time branch: read data from sound device and process indefinitely
		//

		// start the card
		cerr << "DEBUG: Using card #" << card << endl;
		SoundCardWX *adc = 0;
		try {
			adc = new SoundCardWX(card, c);
			adc->start();
		} catch ( RtAudioError& e ) {
			if (adc)
				try { adc->stop(); } catch (const std::exception &e) { /* nop */ }
			e.printMessage();
			return 1;
		}

		// wait forever
		while (true) {
			//
			//  TODO: keep and watch an exit flag, set by signals
			//
			::sleep(1);
			if (adc->isClipping()) {
				cerr << "# CLIP" << endl;
			}
		}

		// stop the card
		try {
			adc->stop();
		} catch ( RtAudioError& e ) {
			e.printMessage();
			return 1;
		}
	} else {
		//
		//  file-based branch: read data from a WAV file
		//

		// open the file
		SoundFile sf;
		try {
			sf.open(argv[1]);
		} catch (std::exception &ex) {
			cerr << "Error: " << ex.what() << endl;
			return 2;
		}

		// create a file-based decoder
		FileDecoder decoder(argv[1], c);

		// run it
		return decoder.run();
	}
}
