#include <iostream>
using namespace std;
#include <uFuture.h>

size_t Clients = 2, Time = 10;

size_t globalTotal = 0;
Future_ISM<size_t> A, B, C;
volatile bool server_done_loop = false;
volatile bool client_done_loop = false;
volatile bool client_done = false;
volatile bool server_done = false;

volatile size_t client_count = 0;
volatile size_t client_loop_count = 0;
_Task Client {
	void main() {
		size_t i = 0;
        for(;; i++ ) {
            _Select( A ) { A(); }
            and _Select( B ) { B(); }
            or _Select( C ) { C(); }

            // needs to check after waituntil for termination synchronization
            if ( client_done ) break;
            
            // Barrier-like synch needed to reset futures safely
            if ( __atomic_add_fetch(&client_count, 1, __ATOMIC_SEQ_CST) == Clients ) {
                client_count = 0;
                A.reset();
                B.reset();
                C.reset();
                client_done_loop = true;
            }
            while( !client_done_loop ) {} // client barrier
            if ( __atomic_add_fetch( &client_loop_count, 1, __ATOMIC_SEQ_CST ) == Clients ) { 
                client_done_loop = false; // reset barrier before clients can proceed past waituntil
                server_done_loop = true; // unblock server to restart iteration
                client_loop_count = 0;
            }
        }
        __atomic_fetch_add( &globalTotal, i, __ATOMIC_SEQ_CST );
	} 
};

_Task Server {
	void main() {
		for( size_t i = 0; !server_done; i++ ) {
            if ( i % 4 == 0 ) {
                A.delivery(i);
                B.delivery(i);
            } else if ( i % 4 == 1 ) {
                A.delivery(i);
                C.delivery(i);
            } else if ( i % 4 == 2 ) {
                B.delivery(i);
                C.delivery(i);
            } else {
                C.delivery(i);
            }
            while( !server_done_loop && !server_done ) {} // server barrier
            server_done_loop = false; // reset server barrier
        }
	}
};

int main( int argc, char * argv[] ) {
	switch ( argc ) {
	  case 3:
		if ( strcmp( argv[2], "d" ) != 0 ) {			// default ?
			Time = atoi( argv[2] );
		} // if
	  case 2:
		if ( strcmp( argv[1], "d" ) != 0 ) {			// default ?
			Clients = atoi( argv[1] );
			if ( Clients < 1 ) goto Usage;
		} // if
	  case 1:											// use defaults
		break;
	  default:
	  Usage:
		cerr << "Usage: " << argv[0]
             << " [ clients (> 0) | 'd' (default " << Clients
			 << ") ] [ time (>= 0) | 'd' (default " << Time
			 << ") ]" ;
		exit( EXIT_FAILURE );
	} // switch
    uProcessor p[Clients];

    {
        Client c[Clients];
        {
            Server s;

            uBaseTask::sleep( uDuration( Time ) );

            server_done = true;
        }
        while( A.available() || B.available() || C.available() ) {}
        client_done = true;
        C.delivery(1); // can't deliver 0 since it causes ambiguity
    }
    cout << globalTotal << endl;
} // main
