#include <9pm/windows.h>
#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/fcall.h>
#include "dat.h"
#include "fns.h"
#include "syscall.h"

enum
{
	MaxHandle = MAXIMUM_WAIT_OBJECTS,
};

typedef struct Hproc Hproc;
struct Hproc
{
	Lock lk;
	HANDLE h[MaxHandle];
	Hwait *w[MaxHandle];
	int nhandle, nahandle;

	Hwait *add;
	Hwait *remove;
};

Lock hproclk;
Lock halloclk;
Hproc *hproc[64];

static void
removew(Hproc *hp, Hwait *w)
{
	int i, ne;
	Hwait *ww;

	ne = w->ne;
	hp->nhandle -= ne;
	hp->nahandle -= ne;
	for(i=w->first; i<hp->nhandle; i++){
		hp->h[i] = hp->h[i+ne];
		hp->w[i] = ww = hp->w[i+ne];
		if(i+ne == ww->first)
			ww->first = i;
	}
}

static void
addw(Hproc *hp, Hwait *w)
{
	int i;

	if(hp->nhandle+w->ne > MaxHandle)
		panic("addw");
	w->first = hp->nhandle;
	for(i=0; i<w->ne; i++, hp->nhandle++){
		hp->h[hp->nhandle] = w->e[i].h;
		hp->w[hp->nhandle] = w;
	}
}

DWORD WINAPI
hwaitthread(void *a)
{
	int s, ret;
	Hproc *hp;
	Hwait *w, *wnext;

	setmach(&nomach);
	hp = a;
	for(;;){
		ret = WaitForMultipleObjects(hp->nhandle, hp->h, 0, INFINITE);
		if(ret < WAIT_OBJECT_0 || ret >= WAIT_OBJECT_0+hp->nhandle)
			panic("hwaitthread");
	//	print("WaitForMultipleObjects woke up\n");
		ret -= WAIT_OBJECT_0;
		s = splhi();
		lock(&hp->lk);
		splx(s);
		if(ret == 0){
			for(w=hp->remove; w; w=wnext){
				wnext = w->nextremove;
				removew(hp, w);
				w->removed = 1;
			}
			hp->remove = nil;
			for(w=hp->add; w; w=wnext){
				wnext = w->nextadd;
				addw(hp, w);
			}
			hp->add = nil;
			if(hp->nahandle != hp->nhandle)
				panic("hwaitproc nahandle");
		}else{
			w = hp->w[ret];
			w->alt = ret - w->first;
			removew(hp, w);
			rendwakeup(&w->r);
		}
		unlock(&hp->lk);
	}
	return 0;	/* not reached */
}

Hproc*
hwaitproc(Hwait *w)
{
	int i, s;
	Hproc *hp;

	if(w->ne > MaxHandle-1)
		panic("hwaitproc");

	s = splhi();
Again:
	lock(&hproclk);
	for(i=0; i<nelem(hproc); i++){
		hp = hproc[i];
		if(hp == nil)
			break;
		lock(&hp->lk);
		if(hp->nahandle+w->ne < MaxHandle)
			break;
		unlock(&hp->lk);
	}
	splx(s);
	if(i == nelem(hproc))
		panic("hwaitproc");
	if(hp == nil){
		lock(&halloclk);
		unlock(&hproclk);
		if(hproc[i] != nil){
			unlock(&halloclk);
			goto Again;
		}
		hp = smalloc(sizeof(Hproc));
		hp->nhandle = 1;
		hp->nahandle = 1;
		hp->h[0] = CreateSemaphore(nil, 0, 1, nil);
		if(hp->h[0] == nil)
			panic("hwaitproc create semaphore");
		CreateThread(nil, 8192, hwaitthread, hp, 0, nil);
		hproc[i] = hp;
		unlock(&halloclk);
		goto Again;
	}
	unlock(&hproclk);
	w->alt = -1;
	w->nextadd = hp->add;
	hp->add = w;
	hp->nahandle += w->ne;
	unlock(&hp->lk);
	ReleaseSemaphore(hp->h[0], 1, nil);
	return hp;
}

static int
fired(Hwait *w)
{
	return w->alt >= 0;
}

void
handlewait(Hwait *w)
{
	Hproc *hp;

	hp = hwaitproc(w);
	rendsleep(&w->r, fired, w);
	if(w->alt != -1)
		return;
	lock(&hp->lk);
	if(w->alt != -1){
		unlock(&hp->lk);
		return;
	}
	w->nextremove = hp->remove;
	hp->remove = w;
	w->removed = 0;
	unlock(&hp->lk);
	ReleaseSemaphore(hp->h[0], 1, nil);
	while(!w->removed)
		Sleep(0);
}

