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

#define N 100_000

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( (unsigned)rand48() % 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[]) {
	all_done = false;
	processor p;
	{
		Signaller s;
		Barger b[17];
		{
			Waiter w[4];
		}
		sout | "All waiter done" | endl;
		all_done = true;
	}	
}