#include <fstream>
#include <kernel>
#include <monitor>
#include <thread>
#include <time>

#ifdef LONG_TEST
static const unsigned long N = 300_000ul;
#else
static const unsigned long N = 10_000ul;
#endif

#ifndef PREEMPTION_RATE
#define PREEMPTION_RATE 10`ms
#endif

Duration default_preemption() {
	return PREEMPTION_RATE;
}

enum state_t { WAIT, SIGNAL, BARGE };

monitor global_t {};
global_t mut;

monitor global_data_t;
void ?{}( global_data_t & this );
void ^?{} ( global_data_t & this );

monitor global_data_t {
	int counter;
	state_t state;
} data;

condition cond;

volatile bool all_done;

void ?{}( global_data_t & this ) {
	this.counter == 0;
	this.state = BARGE;
}

void ^?{} ( global_data_t & this ) {}

//------------------------------------------------------------------------------
// Barging logic
void barge( global_data_t & mutex d ) {
	d.state = BARGE;
}

thread Barger {};

void main( Barger & this ) {
	while( !all_done ) {
		barge( data );
		yield();
	}
}

//------------------------------------------------------------------------------
// Waiting logic
bool wait( global_t & mutex m, global_data_t & mutex d ) {
	wait( cond );
	if( d.state != SIGNAL ) {
		sout | "ERROR barging!" | endl;
	}

	d.counter++;

	if( (d.counter % 1000) == 0 ) sout | d.counter | endl;

	return d.counter < N;
}

thread Waiter {};

void main( Waiter & this ) {
	while( wait( mut, data ) ) { yield(); }
}


//------------------------------------------------------------------------------
// Signalling logic
void signal( condition & cond, global_t & mutex a, global_data_t & mutex b ) {
	b.state = SIGNAL;
	signal( cond );
}

void logic( global_t & mutex a ) {
	signal( cond, a, data );

	yield( random( 10 ) );

	//This is technically a mutual exclusion violation but the mutex monitor protects us
	bool running = data.counter < N && data.counter > 0;
	if( data.state != SIGNAL && running ) {
		sout | "ERROR Eager signal" | data.state | endl;
	}
}

thread Signaller {};

void main( Signaller & this ) {
	while( !all_done ) {
		logic( mut );
		yield();
	}
}

//------------------------------------------------------------------------------
// Main loop
int main(int argc, char* argv[]) {
	srandom( time( NULL ) );
	all_done = false;
	processor p;
	{
		Signaller s;
		Barger b[17];
		{
			Waiter w[4];
		}
		sout | "All waiter done" | endl;
		all_done = true;
	}
}
