#include <select.hfa>
#include <thread.hfa>
#include <future.hfa>
#include <fstream.hfa>
#include <stdio.h>
#include <time.hfa>
#include <string.h>

// #define ANDOR

size_t Processors = 2, Time = 10;

size_t globalTotal = 0;
future(size_t) A, B, C;
volatile bool done_loop = false;
volatile bool client_done = false;
volatile bool server_done = false;

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

static inline fulfill( size_t i ) {
    #ifdef OR
    if ( i % 3 == 0 ) {
        fulfil(A, i);
    } else if ( i % 3 == 1 ) {
        fulfil(B, i);
    } else {
        fulfil(C, i);
    }
    #endif
    #ifdef AND
    if ( i % 2 == 0 ) {
        fulfil(A, i);
        fulfil(B, i);
    } else {
        fulfil(B, i);
        fulfil(A, i);
    }
    #endif
    #ifdef AND3
    if ( i % 6 == 0 ) {
        fulfil(A, i);
        fulfil(B, i);
        fulfil(C, i);
    } else if ( i % 6 == 1 ) {
        fulfil(A, i);
        fulfil(C, i);
        fulfil(B, i);
    } else if ( i % 6 == 2 ) {
        fulfil(B, i);
        fulfil(A, i);
        fulfil(C, i);
    } else if ( i % 6 == 3 ) {
        fulfil(B, i);
        fulfil(C, i);
        fulfil(A, i);
    } else if ( i % 6 == 4 ) {
        fulfil(C, i);
        fulfil(A, i);
        fulfil(B, i);
    } else if ( i % 6 == 5 ) {
        fulfil(C, i);
        fulfil(B, i);
        fulfil(A, i);
    }
    #endif
    #ifdef ANDOR
    if ( i % 4 == 0 ) {
        fulfil(A, i);
        fulfil(B, i);
    } else if ( i % 4 == 1 ) {
        fulfil(A, i);
        fulfil(C, i);
    } else if ( i % 4 == 2 ) {
        fulfil(B, i);
        fulfil(C, i);
    } else {
        fulfil(C, i);
    }
    #endif
    #ifdef ORAND
    if ( i % 4 == 0 ) {
        fulfil(A, i);
        fulfil(C, i);
    } else if ( i % 4 == 1 ) {
        fulfil(C, i);
        fulfil(A, i);
    } else if ( i % 4 == 2 ) {
        fulfil(B, i);
        fulfil(C, i);
    } else {
        fulfil(C, i);
        fulfil(B, i);
    }
    #endif
    #ifdef BASIC
    fulfil(A, i);
    #endif
}

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

thread Server {};
void main( Server & this ) {
    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:
		sout | "Usage: " | argv[0]
             | "[ time (>= 0) | 'd' (default " | Time
			 | ") ]" ;
		exit( EXIT_FAILURE );
	} // switch
    processor p[Processors - 1];

    {
        Server s;
        {
            Client c;

            sleep(Time`s);

            client_done = true;
        }
        server_done = true;
        done_loop = true;
    }
    printf("%zu\n", globalTotal);
}