#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

double frac(double x)
{
	return x - (int)x;
}

class FlankDetector
{
private:
	static const unsigned NUM_SAMPLES = 10;
	static const int MIN_LEVEL = 8192;

	bool status, flag;  // high/low
	unsigned counter;

public:
	FlankDetector() : status(false), flag(false), counter(0) {}
	void update(short x)
	{
		if (abs(x) < MIN_LEVEL) {
			counter = 0;
			return;
		}

		if (!status) {
			// low
			if (x >= MIN_LEVEL) {
				++counter;
			} else {
				counter = 0;
			}
		} else {
			// high
			if (x <= -MIN_LEVEL) {
				++counter;
			} else {
				counter = 0;
			}
		}

		if (counter >= NUM_SAMPLES) {
			status = !status;
			counter = 0;
			flag = true;
		}
	}

	bool check_flag()
	{
		bool ret = flag;
		flag = false;
		return ret;
	}

	bool get_status() const
	{
		return status;
	}
};

class InterpolatingReader
{
private:
	FILE *f;
	double p;
	int in_pos;
	short prev_sample, sample;

public:
	InterpolatingReader(FILE *f) : f(f), p(0.0), in_pos(-1) {}

	double read_sample(double speed)
	{
		p += speed;
		while ((int)(ceil(p)) > in_pos) {
			prev_sample = sample;
			if (fread(&sample, sizeof(short), 1, f) != 1) {
				exit(0);
			}
			++in_pos;
		}

		// linear interpolation (works OK since delta_p varies so slowly)
		double t = frac(p);
		return prev_sample * (1.0 - t) + sample * t;
	}
};

double filter(double x)
{
	static double l;
	static bool init = false;

	if (init) {
		l = 0.05 * x + 0.95 * l;
	} else {
		init = true;
		l = x;
	}
	return l;
}

int main(int argc, char **argv)
{
	FILE *in1, *in2, *skew;

	// open input1 (reference)
	if (strcmp(argv[1], "-") == 0) {
		in1 = stdin;
	} else {
		in1 = fopen(argv[1], "rb");
		if (in1 == NULL) {
			perror(argv[1]);
			exit(1);
		}
	}

	// open input2
	if (strcmp(argv[2], "-") == 0) {
		in2 = stdin;
	} else {
		in2 = fopen(argv[2], "rb");
		if (in2 == NULL) {
			perror(argv[2]);
			exit(1);
		}
	}

	// open (estimated) skew
	if (strcmp(argv[3], "-") == 0) {
		skew = stdout;
	} else {
		skew = fopen(argv[3], "wb");
		if (skew == NULL) {
			perror(argv[3]);
			exit(1);
		}
	}

	FlankDetector f1, f2;
	InterpolatingReader intp(in2);
	double speed = 1.000;

	// initial sync, reference
	do {
		short s;
		if (fread(&s, sizeof(short), 1, in1) != 1) {
			exit(0);
		}

		f1.update(s);
	} while (!f1.check_flag() || !f1.get_status());
	
	// initial sync, input
	do {
		short s = (short)intp.read_sample(speed);
		f2.update(s);

		fwrite(&speed, sizeof(double), 1, skew);
	} while (!f2.check_flag() || !f2.get_status());

	while (!feof(in1) && !feof(in2)) {
		short refs;

		// read reference until the next triggering
		unsigned num_ref = 0;
		while (!feof(in1) && !f1.check_flag()) {
			if (fread(&refs, sizeof(short), 1, in1) != 1) {
				exit(0);
			}
			f1.update(refs);
			++num_ref;
		}

		// same here
		unsigned num_inp = 0;
		while (!feof(in2) && !f2.check_flag()) {
			double s = intp.read_sample(speed);
			f2.update((short)s);
			++num_inp;
		
			fwrite(&speed, sizeof(double), 1, skew);
		}

		double ns = filter(speed * double(num_inp) / double(num_ref));
		fprintf(stderr, "%u vs. %u -- ratio %.3f, speed %.3f, filtered speed %.3f\n", num_inp, num_ref,
			double(num_inp) / double(num_ref), speed * double(num_inp) / double(num_ref), ns);
		speed = ns;
	}
}
