#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#ifdef WIN32
#include <windows.h>
#include <process.h>
#else
#include <sys/types.h>
#include <unistd.h>
#endif

#include "lib.h"
#include "thread.h"
#include "debug.h"
#include "lprintf.h"
#include "pool.h"

typedef   int (*Threadfn)(void *x);
typedef struct PLIST {Thread *th; Threadfn fn; void *p1; int busy; struct PLIST  *next; char *thing;} Plist; 
static Plist *plist;
static int isexit;
static Plist *pbusy;
static Mutex *m;

Plist *pool_find_idle(void);
Plist *pool_add(void);
int pool_thread(void *x);

int pool_init(void)
{
	m = th_mutex_new("pool");
	return TRUE;
}
int pool_mutex_get(void)
{
	if (!th_mutex_get(m)) { //,MUTEX_TIME)) { /* 10 minutes */
		lib_abort();
	}
	return TRUE;
}
int pool_mutex_release(void)
{
	th_mutex_release(m);
	return TRUE;
}


Thread *pool_start(int  fn(void *x),void *arg, int stack, char *source, int line)
{
	Plist *p;
	p = pool_find_idle();
	if (stack>1024*552) lib_abort();
	if (p==NULL) p = pool_add(); // create a new thread we can use...  could add code to handle 'bursts' here...
	p->thing = source;
	p->p1 = arg;
	p->fn = fn;
	return p->th;
}
int pool_set_exit(void)
{
	isexit = TRUE;
	return TRUE;
}
Plist *pool_add(void)
{
	Plist *p = calloc(1,sizeof(Plist)) ;
	
	p->busy = TRUE;
	p->th = th_new_stack_real(pool_thread,p,1024*552,"pool",1);
	if (p->th==NULL) lib_abort();
	pool_mutex_get();
	p->next = plist;
	plist = p;
	pool_mutex_release();
	return p;
}
Plist *pool_find_idle(void)
{
	Plist *p;
	pool_mutex_get();
	for (p=plist;p!=NULL;p=p->next) {
		if (!p->busy) {
			p->busy = TRUE;
			goto done;
		}
	}
done:
	pool_mutex_release();
	return p;
}
int pool_thread(void *x)
{
	Plist *p = (Plist *) x;
	for (;!isexit;) {
		lib_msleep(100);
		if (p->busy) if (p->fn!=NULL) {
			p->fn(p->p1);
			p->fn = NULL;
			p->p1 = NULL;
			p->busy = FALSE; // must be last as not mutex locked..
		}
	}
	th_exit(0);  /* Close the thread cleanly */
	return TRUE;
}
char *pool_status(char *msg)
{
	Plist *p;
	int nbusy=0;
	int nidle=0;
	pool_mutex_get();
	for (p=plist;p!=NULL;p=p->next) {
		if (p->busy) nbusy++; else nidle++;
	}
	pool_mutex_release();
	lprintf(msg,BFSZ-1,"Pool: Busy %d Idle %d",nbusy,nidle);
	return msg;
}
