#include <iostream>
#include <vector>
#include <cstring>

#include "dsd2pcm.hpp"
#include "noiseshape.hpp"

namespace {

const float my_ns_coeffs[] = {
//     b1           b2           a1           a2
  -1.62666423,  0.79410094,  0.61367127,  0.23311013,  // section 1
  -1.44870017,  0.54196219,  0.03373857,  0.70316556   // section 2
};

const int my_ns_soscount = sizeof(my_ns_coeffs)/(sizeof(my_ns_coeffs[0])*4);

inline long myround(float x)
{
	return static_cast<long>(x + (x>=0 ? 0.5f : -0.5f));
}

template<typename T>
struct id { typedef T type; };

template<typename T>
inline T clip(
	typename id<T>::type min,
	T v,
	typename id<T>::type max)
{
	if (v<min) return min;
	if (v>max) return max;
	return v;
}

inline void write_intel16(unsigned char * ptr, unsigned word)
{
	ptr[0] =  word       & 0xFF;
	ptr[1] = (word >> 8) & 0xFF;
}

inline void write_intel24(unsigned char * ptr, unsigned long word)
{
	ptr[0] =  word        & 0xFF;
	ptr[1] = (word >>  8) & 0xFF;
	ptr[2] = (word >> 16) & 0xFF;
}

} // anonymous namespace

using std::vector;
using std::cin;
using std::cout;
using std::cerr;

int main(int argc, char *argv[])
{
	const int block = 16384;
	int channels   = -1;
	int lsbitfirst = -1;
	int bits       = -1;
	if (argc==4) {
		if ('1'<=argv[1][0] && argv[1][0]<='9') channels = 1 + (argv[1][0]-'1');
		if (argv[2][0]=='m' || argv[2][0]=='M') lsbitfirst=0;
		if (argv[2][0]=='l' || argv[2][0]=='L') lsbitfirst=1;
		if (!strcmp(argv[3],"16")) bits = 16;
		if (!strcmp(argv[3],"24")) bits = 24;
	}
	if (channels<1 || lsbitfirst<0 || bits<0) {
		cerr << "\n"
			"DSD2PCM filter (raw DSD64 --> 352 kHz raw PCM)\n"
			"(c) 2009 Sebastian Gesemann\n\n"
			"(filter as in \"reads data from stdin and writes to stdout\")\n\n"
			"Syntax: dsd2pcm <channels> <bitorder> <bitdepth>\n"
			"channels = 1,2,3,...,9 (number of channels in DSD stream)\n"
			"bitorder = L (lsb first), M (msb first) (DSD stream option)\n"
			"bitdepth = 16 or 24 (intel byte order, output option)\n\n"
			"Note: At 16 bits/sample a noise shaper kicks in that can preserve\n"
			"a dynamic range of 135 dB below 30 kHz.\n\n";
		return 1;
	}
	int bytespersample = bits/8;
	vector<dxd> dxds (channels);
	vector<noise_shaper> ns;
	if (bits==16) {
		ns.resize(channels, noise_shaper(my_ns_soscount, my_ns_coeffs) );
	}
	vector<unsigned char> dsd_data (block * channels);
	vector<float> float_data (block);
	vector<unsigned char> pcm_data (block * channels * bytespersample);
	char * const dsd_in  = reinterpret_cast<char*>(&dsd_data[0]);
	char * const pcm_out = reinterpret_cast<char*>(&pcm_data[0]);
	while (cin.read(dsd_in,block * channels)) {
		for (int c=0; c<channels; ++c) {
			dxds[c].translate(block,&dsd_data[0]+c,channels,
				lsbitfirst,
				&float_data[0],1);
			unsigned char * out = &pcm_data[0] + c*bytespersample;
			if (bits==16) {
				for (int s=0; s<block; ++s) {
					float r = float_data[s]*32768 + ns[c].get();
					long smp = clip(-32768,myround(r),32767);
					ns[c].update( clip(-1,smp-r,1) );
					write_intel16(out,smp);
					out += channels*bytespersample;
				}
			} else {
				for (int s=0; s<block; ++s) {
					float r = float_data[s]*8388608;
					long smp = clip(-8388608,myround(r),8388607);
					write_intel24(out,smp);
					out += channels*bytespersample;
				}
			}
		}
		cout.write(pcm_out,block*channels*bytespersample);
	}
}

