#ifndef _plan9_
#include <signal.h>
#include <stdio.h>
#endif
#include <xthread.h>
#ifndef __BEOS__
struct taskinfo definfo = {
    0,
};

#ifndef nthreads

#define getrange1(range,ncpu) ((range)*(ncpu))/nthreads
#define getrange2(range,ncpu) getrange1(range,(ncpu)+1)

int ethreads = 0;
int nthreads = 1;

#ifdef USE_PTHREAD

/* Well conde follows is probably very ugly, since this is
 * my absolutely first application for threads. Please let
 * me know how to improvie it
 */

static pthread_cond_t synccond, startcond;
static struct taskinfo infos[MAXTHREADS];
static pthread_mutex_t synccondmutex, startcondmutex;
pthread_mutex_t semaphors[MAXSEMAPHORS];
pthread_cond_t conds[MAXCONDS];

/*This loop is executed whole time in slave threads.
   Its function is following:
   1) wait for message
   2) call function from message
   3) syncronize
   4) again

   To invoke this mechanizm main thread(#1) should call
   xth_function
   xth_synchronize forces forces thread to wait for others
 */

static int nfinished;
static int npending;
static int counter;
static int bcounter;
static int bcounter1;
static int range;
static void *data;
static xfunction function;
static void *control_routine(void *i)
{
    struct taskinfo *info = i;
    int mycounter = 0;
    int r;
    void *d;
    xfunction f;
    while (1) {
	/* quite a lot pthread calls. Please if you are
	 * reading this code and crying "OH NO!" so ugly
	 * handling! Why so much calls? Stop crying, I am
	 * newbie in threads. Please rewrite my code and
	 * send me better and faster version. 
	 * 
	 * This function comunicates with pth_function from main loop
	 * as follows: it uses startcond to wait for order start function!
	 * Counter is used to ensure that main function did not give
	 * order whie control_routine was busy
	 *
	 * after order is received, function adress is readed from global
	 * variables and started. Pth_function then executes its
	 * own part of calculation. After that it waits counter
	 * nfinished to reach number of thasks-1. This is done
	 * using cond synccond and synccondmutex. Quite complex
	 * but it seems to work. Looking forward for someone, who
	 * should reduce number of _lock/_unlock
	 */
	pthread_mutex_lock(&synccondmutex);
	nfinished++;
	pthread_cond_signal(&synccond);
	pthread_mutex_unlock(&synccondmutex);

	pthread_mutex_lock(&startcondmutex);
	while (mycounter >= counter) {
	    if (bcounter > bcounter1) {
		/*Well we are already locked using start lock..should be OK */
		mycounter--;
		bcounter1++;
		break;
	    }
	    pthread_cond_wait(&startcond, &startcondmutex);
	}
	r = range;
	d = data;
	f = function;
	npending--;
	pthread_mutex_unlock(&startcondmutex);
	mycounter++;
	f(d, info, getrange1(r, info->n), getrange2(r, info->n));
    }
    return NULL;
}

void pth_init(int nthreads1)
{
    int i;
    pthread_attr_t attr;
    if (ethreads)
	return;

    if (nthreads1 == 1 || nthreads1 == 0)
	return;			/*use nothreads_* calls */
    if (nthreads1 > MAXTHREADS)
	nthreads1 = MAXTHREADS;
    nthreads = nthreads1;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    if (pthread_cond_init(&synccond, NULL))
	exit(1);
    if (pthread_cond_init(&startcond, NULL))
	exit(1);
    if (pthread_mutex_init(&synccondmutex, NULL))
	exit(1);
    if (pthread_mutex_init(&startcondmutex, NULL))
	exit(1);

    infos[0].n = 0;
    /*infos[0].id = pthread_self(); */

    for (i = 0; i < MAXSEMAPHORS; i++) {
	pthread_mutex_init(semaphors + i, NULL);
    }
    for (i = 0; i < MAXCONDS; i++) {
	pthread_cond_init(conds + i, NULL);
    }

    nfinished = 0;
    for (i = 0; i < nthreads - 1; i++) {
	if (pthread_create
	    (&infos[i + 1].id, &attr, control_routine, infos + i + 1)) {
	    nthreads = i + 1;
	    break;
	}
	infos[i + 1].n = i + 1;
    }
    if (nthreads != 1)
	ethreads = 1;
}

void pth_synchronize()
{
    /*Our job is done, synchronize now */
    if (nfinished < nthreads - 1) {
	pthread_mutex_lock(&synccondmutex);
	while (nfinished < nthreads - 1) {
	    pthread_cond_wait(&synccond, &synccondmutex);
	}
	pthread_mutex_unlock(&synccondmutex);
    }
    /*Ok job is done, lets continue :) */
}

void pth_bgjob(xfunction f, void *d)
{
    pthread_mutex_lock(&startcondmutex);
    if (npending) {
	printf("Collision!\n");	/*FIXME:remove this..I just want to know how often this happends */
	pthread_mutex_unlock(&startcondmutex);
	f(d, infos, 0, 0);
    }
    if (bcounter < bcounter1) {
	printf("Internal error\a\n");
    }
    if (!nfinished) {
	pthread_mutex_unlock(&startcondmutex);
	/*no more CPU available :( */
	f(d, infos, 0, 0);
    }
    data = d;
    range = 0;
    function = f;
    bcounter++;
    nfinished--;
    npending++;
    pthread_cond_signal(&startcond);
    pthread_mutex_unlock(&startcondmutex);
}

void pth_function(xfunction f, void *d, int r)
{
    pth_synchronize();
    pthread_mutex_lock(&startcondmutex);
    data = d;
    range = r;
    function = f;
    /*And lets start it:) */
    nfinished = 0;
    npending = nthreads - 1;
    counter++;
    if (nthreads == 2)
	pthread_cond_signal(&startcond);
    else
	pthread_cond_broadcast(&startcond);
    pthread_mutex_unlock(&startcondmutex);

    function(data, infos, getrange1(range, 0), getrange2(range, 0));
}

void pth_uninit()
{
    /*Should be empty for now since all threads will be killed after exit call */
    /*FIXME should be added something if necessary :) */
    nthreads = 1;
    ethreads = 0;
}
#endif				/*POSIX threads */
#endif				/*nthreads */
#endif /*__BEOS__*/
