/*
 * Copyright (c) 2009 Veselin Georgiev
 *
 * Cutting the bullshit:
 *  all sources here are free software; you can use them for everything you
 *  want. Just don't blame me if something goes wrong; if something happens
 *  (including, but not limited to:
 *     coredumps, formatted harddrives, ping timeouts, winamp sound skips,
 *     fried CPUs, sudden mother-in-law visits, fraglimits, viagra spam,
 *     barking dogs, earthquakes, frog rain, Windows Vista installations,
 *     anrieff playing the bass guitar, dangerous sporks, apocalypse,
 *     concrete donkey, or even Chuck Norris getting your butt kicked...
 *  ), then you know you have been warned. Four times.
 *
 * Btw, Chuck Norris now doesn't code neither Python, nor Ruby. He's hacking
 * with Makefiles.
 */

#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include "queryengine.h"
#include "queryserver.h"

QueryEngine q(System::DateTime::Now);
static int zero[MAXX * MAXY];

#ifdef DEBUG_JOURNAL
#warning Debug journal is enabled
FILE *jfile;
static void journal(const char* format, ...) __attribute__((format(printf, 1, 2)));
static void journal(const char* format, ...)
{
	if (!jfile) jfile = fopen("/home/vesko/recognition/jfile", "wt");
	va_list arg;
	char s[1024];
	
	va_start(arg, format);
	vsnprintf(s, 1024, format, arg);
	va_end(arg);
	fprintf(jfile, "%s", s);
}

#endif

QueryEngine::QueryEngine(System::DateTime m) : mark(m)
{
	w = h = 0;
	memset(data, 0xff, sizeof(data));
	zeroscore = -1;
	num_queries = q_type1 = q_type2 = q_misses = 0;
	memset(zero, 0, sizeof(zero));
	server = NULL;
	cc = 0;
}

QueryEngine::~QueryEngine()
{
#ifdef PRINT_DEBUGINFO
	printf("Queryengine stats:\n");
	printf("  #queries : %d\n", num_queries);
	printf("  type 1   : %d\n", q_type1 - q_misses);
	printf("  type 2   : %d\n", q_type2);
	printf("  misses   : %d\n", q_misses);
	printf("  submits  : %d\n", q_submits);
#endif
	if (server) {
		delete_query_server(server);
		server = NULL;
	}
#ifdef DEBUG_JOURNAL
	if (jfile) {
		fclose(jfile);
		jfile = NULL;
	}
#endif
}

void QueryEngine::init(int &width, int &height)
{
	server = new_query_server();
	if (server) {
		server->init(w, h);
		zeroscore = server->score(zero);
#ifdef PRINT_DEBUGINFO
		printf("Zero score: %d\n", zeroscore);
#endif
		width = w;
		height = h;
		// just output some full frames, in case something goes terribly wrong
		int failsafe_colors[2] = {127, 255};
		for (int k = 0; k < 2; k++) {
			for (int i = 0; i < w * h; i++)
				zero[i] = failsafe_colors[k];
			server->score(zero);
		}
		// return to zeros :)
		memset(zero, 0, sizeof(zero));
	} else {
		width = height = 0;
	}
}

void QueryEngine::query(int x1, int y1, int x2, int y2)
{
#ifdef DEBUG_JOURNAL
	journal("FASTQUERY %d %d %d %d\n", x1, y1, x2, y2);
#endif
	if (data[y1 * w + x1] != -1) {
		query(x2, y2);
		return;
	}
	if (data[y2 * w + x2] != -1) {
		query(x1, y1);
		return;
	}
	if (x1 == x2 && y1 == y2) {
		query(x1, y1);
		return;
	}
	num_queries++;
	q_type2++;
	zero[y1 * w + x1] = 1;
	zero[y2 * w + x2] = 255;
	int ds = server->score(zero) - zeroscore - 255*255 - 1*1;
	zero[y1 * w + x1] = 0;
	zero[y2 * w + x2] = 0;
	assert(ds <= 0);
	ds = -ds;
	if (ds == 0) {
		// both zeros:
		data[y1 * w + x1] = 0;
		data[y2 * w + x2] = 0;
#ifdef DEBUG_JOURNAL
		journal("QRES %d %d 0\nQRES %d %d 0\n", x1, y1, x2, y2);
#endif
		return;
	}
	if (ds == 510 * 255 + 2 * 255) {
		// both 255's:
		data[y1 * w + x1] = 255;
		data[y2 * w + x2] = 255;
#ifdef DEBUG_JOURNAL
		journal("QRES %d %d 255\nQRES %d %d 255\n", x1, y1, x2, y2);
#endif
		return;
	}
	if (ds % 510 == 0) {
		// bad case, cannot distinguish Q1 = 255, Q2 = 0 from Q1 = 0, Q2 = 1...
#ifdef DEBUG_JOURNAL
		journal("BADCASE %d %d %d %d\n", x1, y1, x2, y2);
#endif
		q_misses++;
		query(x1, y1);
		int x = data[y1 * w + x1];
		assert((ds - 2 * x) % 510 == 0);
		int y = (ds - 2 * x) / 510;
		assert(y >= 0 && y <= 255);
		data[y2 * w + x2] = y;
#ifdef DEBUG_JOURNAL
		journal("QRES %d %d %d\n", x2, y2, y);
#endif
	} else {
		// alle gut!
		assert(ds % 2 == 0);
		int x = (ds % 510) / 2;
		int y = (ds - 2 * x) / 510;
		assert(x >= 0 && x <= 255);
		assert(y >= 0 && y <= 255);
		data[y1 * w + x1] = x;
		data[y2 * w + x2] = y;
#ifdef DEBUG_JOURNAL
		journal("QRES %d %d %d\nQRES %d %d %d\n", x1, y1, x, x2, y2, y);
#endif
	}
}

void QueryEngine::query(int x, int y)
{
#ifdef DEBUG_JOURNAL
	journal("QUERY %d %d\n", x, y);
#endif
	num_queries++;
	q_type1++;
	if (data[y * w + x] != -1) return;
	zero[y * w + x] = 1;
	int ds = server->score(zero) - zeroscore - 1*1;
	zero[y * w + x] = 0;
	assert(ds <= 0);
	ds = -ds;
	assert(ds % 2 == 0);
	data[y * w + x] = ds / 2;
#ifdef DEBUG_JOURNAL
	journal("QRES %d %d %d\n", x, y, ds / 2);
#endif
}

void QueryEngine::cached_query(int x, int y)
{
	if (data[y * w + x] != -1) return;
	cache[cc][0] = x;
	cache[cc][1] = y;
	cc++;
	if (cc == 2) {
		cc = 0;
		query(cache[0][0], cache[0][1], cache[1][0], cache[1][1]);
	}
}

void QueryEngine::flush_cache(void)
{
	if (cc) {
		cc = 0;
		query(cache[0][0], cache[0][1]);
	}
}

void QueryEngine::submit(int *xdata)
{
	num_queries++;
	q_submits++;
#ifdef DEBUG_JOURNAL
	journal("SUBMIT");
	for (int i = 0; i < w*h; i++) journal(" %d", xdata[i]);
	journal("\n");
#endif
	server->score(xdata);
}

double QueryEngine::time(void)
{
	System::TimeSpan span = System::DateTime::Now - mark;
	double ret = span.TotalMilliseconds / 1000.0;
	//printf("%.2lf\n", ret);
	return ret;
}

void QueryEngine::mark_selected(int x, int y)
{
#ifdef DEBUG_JOURNAL
	journal("SELECTED %d %d\n", x, y);
#endif
}

bool QueryEngine::have_all() const
{
	int sz = w * h;
	for (int i = 0; i < sz; i++) if (data[i] == -1) return false;
	return true;
}
