import util;
import game;
import driver;
import core.stdc.signal;

class NilOutput : GameObserver
{
	this(in Game g) {}
	override void on_game_changed(char c, in Game g, bool finished) {}
}

class StdOutput : GameObserver
{
	this(in Game g) {}
	override void on_game_changed(char c, in Game g, bool finished)
	{
		stdout.write(c);
		stdout.flush();
	}
}

class GuardedOutput : GameObserver
{
	this(in Game g)
	{
		setup_sigint_handling();
		score_log ~= g.score;
		flushed = false;
	}

	override void on_game_changed(char c, in Game g, bool finished)
	{
		if(flushed)
			return;

		log ~= c;
		score_log ~= g.score;
		if(finished || log.length+1==g.map.W*g.map.H)
			flush();
		if(log.length+1==g.map.W*g.map.H)
			application_exit();
	}

	void safety_data(string s_log, long[] s_score_log) {
		this.s_log = s_log;
		this.s_score_log = s_score_log;
	}

private:
	string log;
	long[] score_log;
	bool   flushed;

	string s_log;
	long[] s_score_log;

	void flush() nothrow
	{
		if(flushed)
			return;

		Tuple!(long, int, immutable(char)*) cand;
		cand[0] = long.min;

		for(int i=0; i<score_log.length; ++i)
			if(cand[0] < score_log[i])	
				cand = tuple(score_log[i],i,log.ptr);
		for(int i=0; i<s_score_log.length; ++i)
			if(cand[0] < s_score_log[i])	
				cand = tuple(s_score_log[i],i,s_log.ptr);

		std.c.stdio.printf("%.*sA\n", cand[1], cand[2]);
		std.c.stdio.fflush(std.c.stdio.stdout);
		flushed = true;
	}

private:
	static __gshared GuardedOutput g_output;

	void setup_sigint_handling()
	{
		assert(g_output is null);
		g_output = this;
		extern(C) nothrow @system static void catch_sigint(int) { g_output.flush(); application_exit(); }
		core.stdc.signal.signal(SIGINT, &catch_sigint);
	}
}
