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

// #define ANDOR

size_t Processors = 2, Time = 10;
size_t globalTotal = 0;
volatile bool done_loop = false;
volatile bool client_done = false;
volatile bool server_done = false;

Future_ISM<size_t> A, B, C;

static inline void wait() {
    #ifdef OR
    _Select( A ) { A(); }
    or _Select( B ) { B(); }
    or _Select( C ) { C(); }
    #endif
    #ifdef AND
    _Select( A ) { A(); }
    and _Select( B ) { B(); }
    #endif
    #ifdef AND3
    _Select( A ) { A(); }
    and _Select( B ) { B(); }
    and _Select( C ) { C(); }
    #endif
    #ifdef ANDOR
    _Select( A ) { A(); }
    and _Select( B ) { B(); }
    or _Select( C ) { C(); }
    #endif
    #ifdef ORAND
    (_Select( A ) { A(); }
    or _Select( B ) { B(); })
    and _Select( C ) { C(); }
    #endif
    #ifdef BASIC
    A();
    #endif
}

static inline void fulfill( size_t i ) {
    #ifdef OR
    if ( i % 3 == 0 ) {
        A.delivery(i);
    } else if ( i % 3 == 1 ) {
        B.delivery(i);
    } else {
        C.delivery(i);
    }
    #endif
    #ifdef AND
    if ( i % 2 == 0 ) {
        A.delivery(i);
        B.delivery(i);
    } else {
        B.delivery(i);
        A.delivery(i);
    }
    #endif
    #ifdef AND3
    if ( i % 6 == 0 ) {
        A.delivery(i);
        B.delivery(i);
        C.delivery(i);
    } else if ( i % 6 == 1 ) {
        A.delivery(i);
        C.delivery(i);
        B.delivery(i);
    } else if ( i % 6 == 2 ) {
        B.delivery(i);
        A.delivery(i);
        C.delivery(i);
    } else if ( i % 6 == 3 ) {
        B.delivery(i);
        C.delivery(i);
        A.delivery(i);
    } else if ( i % 6 == 4 ) {
        C.delivery(i);
        A.delivery(i);
        B.delivery(i);
    } else if ( i % 6 == 5 ) {
        C.delivery(i);
        B.delivery(i);
        A.delivery(i);
    }
    #endif
    #ifdef ANDOR
    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);
    }
    #endif
    #ifdef ORAND
    if ( i % 4 == 0 ) {
        A.delivery(i);
        C.delivery(i);
    } else if ( i % 4 == 1 ) {
        C.delivery(i);
        A.delivery(i);
    } else if ( i % 4 == 2 ) {
        B.delivery(i);
        C.delivery(i);
    } else {
        C.delivery(i);
        B.delivery(i);
    }
    #endif
    #ifdef BASIC
    A.delivery(i);
    #endif
}

_Task Client {
	void main() {
		size_t i = 0;
        for(; !client_done; i++ ) {
            wait();
            A.reset();
            B.reset();
            C.reset();
            done_loop = true;
        }
        __atomic_fetch_add( &globalTotal, i, __ATOMIC_SEQ_CST );
	} 
};

_Task Server {
	void main() {
		for( size_t i = 0; !server_done; i++ ) {
            fulfill( i );
            while( !done_loop ) {}
            done_loop = false;
        }
	}
};

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

    {
        Server s;
        {
            Client c;

            uBaseTask::sleep( uDuration( Time ) );

            client_done = true;
        }
        server_done = true;
        done_loop = true;
    }
    cout << globalTotal << endl;
} // main
