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

static const int N = 10_000;

monitor global_t {};

global_t globalA;
global_t globalB;
global_t globalC;

condition condAB, condAC, condBC, condABC;

thread Signaler {};
thread WaiterAB {};
thread WaiterAC {};
thread WaiterBC {};
thread WaiterABC{};

volatile int waiter_left;

//----------------------------------------------------------------------------------------------------
// Tools
void signal( condition * cond, global_t * mutex a, global_t * mutex b ) {
	signal( cond );
}

void signal( condition * cond, global_t * mutex a, global_t * mutex b, global_t * mutex c ) {
	signal( cond );
}

void wait( condition * cond, global_t * mutex a, global_t * mutex b ) {
	wait( cond );
}

void wait( condition * cond, global_t * mutex a, global_t * mutex b, global_t * mutex c ) {
	wait( cond );
}

//----------------------------------------------------------------------------------------------------
// Signaler
void main( Signaler* this ) {

	while( waiter_left != 0 ) {
		unsigned action = (unsigned)rand48() % 4;
		switch( action ) {
			case 0: 
				signal( &condABC, &globalA, &globalB, &globalC );
				break;
			case 1: 
				signal( &condAB , &globalA, &globalB );
				break;
			case 2: 
				signal( &condBC , &globalB, &globalC );
				break;
			case 3: 
				signal( &condAC , &globalA, &globalC );
				break;
			default:
				sout | "Something went wrong" | endl;
				abort();
		}
		yield();
	}	
}

//----------------------------------------------------------------------------------------------------
// Waiter ABC
void main( WaiterABC* this ) {
	for( int i = 0; i < N; i++ ) {
		wait( &condABC, &globalA, &globalB, &globalC );
	}

	__sync_fetch_and_sub_4( &waiter_left, 1);
}

//----------------------------------------------------------------------------------------------------
// Waiter AB
void main( WaiterAB* this ) {
	for( int i = 0; i < N; i++ ) {
		wait( &condAB , &globalA, &globalB );
	}

	__sync_fetch_and_sub_4( &waiter_left, 1);
}

//----------------------------------------------------------------------------------------------------
// Waiter AC
void main( WaiterAC* this ) {
	for( int i = 0; i < N; i++ ) {
		wait( &condAC , &globalA, &globalC );
	}

	__sync_fetch_and_sub_4( &waiter_left, 1);
}

//----------------------------------------------------------------------------------------------------
// Waiter BC
void main( WaiterBC* this ) {
	for( int i = 0; i < N; i++ ) {
		wait( &condBC , &globalB, &globalC );
	}

	__sync_fetch_and_sub_4( &waiter_left, 1);
}

//----------------------------------------------------------------------------------------------------
// Main
int main(int argc, char* argv[]) {
	waiter_left = 4;
	processor p;
	sout | "Starting" | endl;
	{
		Signaler  e;
		{
			WaiterABC a;
			WaiterAB  b;
			WaiterBC  c;
			WaiterAC  d;
		}
	}
	sout | "Done" | endl;
}