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

static char magic[] = "9pm.3fe6c2dcaeee8517";
Mach *machp0;
char *argv0;
Mach nomach;

static HANDLE idle;
static HWND window;
static LRESULT CALLBACK NewProcProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
static HANDLE me;
static long tlsmach;
static int debug;

void
noidlehands(void)
{
	ReleaseSemaphore(idle, 1, nil);
}

void
idlehands(void)
{
	WaitForSingleObject(idle, INFINITE);
}

extern WINBASEAPI HWND WINAPI GetConsoleWindow(void);
void
dwriten(char *s, int n)
{
	static HANDLE h;
	DWORD m;

	if(h == INVALID_HANDLE_VALUE || h == NULL){
		AllocConsole();
		h = GetStdHandle(STD_ERROR_HANDLE);
	}
	if(h != INVALID_HANDLE_VALUE && h != NULL)
		WriteFile(h, s, n, &m, nil);
	else
		abort();
}

void
dwrite(char *s)
{
	dwriten(s, strlen(s));
}

/* 
 * alarmthread only calls checkalarms,
 * so it doesn't need its own Mach structure.
 */
DWORD WINAPI
alarmthread(void *a)
{
	USED(a);

	setmach(&nomach);
	print("alarmthread started\n");
	for(;;){
		Sleep(100);
		machp0->ticks = GetTickCount();
		checkalarms();
	}
	return 0;
}

DWORD WINAPI
kernelthread(void *a)
{
	Mach *m;

	m = a;
	print("kernelthread %d started\n", m->machno);
	TlsSetValue(tlsmach, m);
	schedinit();
	abort();
	return 0;
}

Mach*
getmach(void)
{
	return TlsGetValue(tlsmach);
}

void
setmach(Mach *m)
{
	TlsSetValue(tlsmach, m);
}

static int	args(char *argv[], int n, char *p);

int PASCAL
WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR arg, int nshow)
{
	int argc, n;
	char *p, **argv;
	WNDCLASS wc;
	HANDLE inst;
	MSG msg;
	Rune rbuf[256], *r;

	/* conservative guess at the number of args */
	for(argc=5,p=arg; *p; p++)
		if(*p == ' ' || *p == '\t')
			argc++;

	argv = GlobalAlloc(GMEM_FIXED, argc*sizeof(char*));
	argc = args(argv+1, argc, arg);
	argv[0] = "plan9";
	argc++;

	ARGBEGIN{
	case 'd':
		debug = 1;
		break;
	}ARGEND
	machp0 = GlobalAlloc(GMEM_FIXED, sizeof(Mach));
	machp0->ticks = GetTickCount();
	memset(machp0, 0, sizeof(Mach));
	tlsmach = TlsAlloc();
	nomach.machno = -1;
	TlsSetValue(tlsmach, &nomach);
	screenputs = dwriten;
	print("Plan 9 from User Space\n");
	SetConsoleTitleA("Plan 9 from User Space");
	if(!debug)
		ShowWindow(GetConsoleWindow(), SW_HIDE);
	inst = GetModuleHandle(nil);
	wc.style = 0;
	wc.lpfnWndProc = NewProcProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = inst;
	wc.hIcon = LoadIcon(inst, nil);
	wc.hCursor = LoadCursor(nil, IDC_ARROW);
	wc.hbrBackground = GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = 0;
	wc.lpszClassName = L"Plan 9 Kernel";
	RegisterClass(&wc);
	me = GetCurrentProcess();
	idle = CreateSemaphore(nil, 0, 1, nil);

	window = CreateWindowEx(
		WS_EX_TOOLWINDOW,
		L"Plan 9 Kernel",
		L"Plan 9 Kernel",
		WS_OVERLAPPEDWINDOW|
		WS_MINIMIZE|WS_DISABLED,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		100,
		100,
		NULL,
		NULL,
		inst,
		NULL
		);
	if(window == nil){
		oserror();
		panic("can't allocate window: %r");
	}
	n = nelem(rbuf);
	r = rbuf;
	if(!GetUserName(r, &n)){
		n++;
		r = win_malloc(n*sizeof(Rune));
		if(!GetUserName(r, &n))
			r = L"glenda";
	}
	eve = win_wstr2utf(r);
	adddev(&devroot);	/* must be first */
	adddev(&devcons);
	adddev(&devdup);
	adddev(&devenv);
	adddev(&devfs);
	adddev(&devmnt);
	adddev(&devpipe);
	adddev(&devproc);
	adddev(&devsrv);
	adddev(&devssl);

	procinit0();
	userinit();
	up->slash = namec("#/", Atodir, 0, 0);
	cnameclose(up->slash->name);
	up->slash->name = newcname("/");
	up->dot = namec("/", Atodir, 0, 0);

	CreateThread(nil, 8192, kernelthread, machp0, 0, nil);
	CreateThread(nil, 8192, alarmthread, nil, 0, nil);
	while(GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	ExitProcess(0);
}

void
runsyscall(Syscallmem *c)
{
	int i;
	char *e;
	long ret;

	if(!waserror()){
		if(c->nr < 0 || c->nr >= Nsyscall || systab[c->nr].fn==nil){
			print("pid %d bad sys call number %d\n", up->pid, c->nr);
			delay(1000);
			pprint("bad sys call number %d\n", c->nr);
			delay(1000);
			postnote(up, 1, "sys: bad sys call", NDebug);	/* Ndebug?  BUG? */
			delay(1000);
			error(Ebadarg);
		}
		validargs(c->arg, &c[1], (uchar*)c+CallMem, systab[c->nr].fmt);
	//	print("%d calling %s\n", up->pid, systab[c->nr].psstate);
		up->psstate = systab[c->nr].psstate;
		ret = (*systab[c->nr].fn)(c->arg);
		poperror();
	//	print("%d %s ret = %d\n", up->pid, systab[c->nr].psstate, ret);
	}else{
		/* failure: save the error buffer for errstr */
		print("%d syscall %s failed '%s'\n", up->pid, systab[c->nr].psstate, up->errstr);
		e = up->errstr;
		up->syserrstr = up->errstr;
		up->errstr = e;
		ret = -1;
	}
	if(up->nerrlab){
		print("bad errstack [%d]: %d extra\n", c->nr, up->nerrlab);
		for(i=0; i<NERR; i++)
			print("sp=%lux pc=%lux\n", up->errlab[i].jb[JMPBUFSP], up->errlab[i].jb[JMPBUFPC]);
		panic("error stack");
	}
	up->psstate = nil;
	c->ret = ret;

	/* BUG notify etc. */
}

void
procdie(Proc *p, char *exitstr)
{
	Syscallmem *c;

	c = p->sysaux;
	if(!c->exitnow){
		c->exitnow = 1;
		strcpy(c->exitstatus, exitstr);
	}
	if(c->nr == Sexits)
		ReleaseSemaphore(c->hs.hclientsig, 1, nil);
	CloseHandle(c->hs.hserversig);
	CloseHandle(c->hs.hclientsig);
	CloseHandle(c->hs.hmem);
	CloseHandle(c->hs.hkillsig);
	UnmapViewOfFile(c);
}

void
userproc(void *arg)
{
	DWORD status;
	char err[ERRMAX];
	Hwait w;
	Hevent e[2];
	Syscallmem *c;

	c = arg;
	up->sysaux = c;
	up->die = procdie;
	print("userproc %lud [%d] started\n", up->pid, up->index);
	memset(e, 0, sizeof e);
	e[0].h = c->hs.hserversig;
	e[1].h = c->hs.hclientproc;
	w.e = e;
	w.ne = 2;
	for(;;){
	//	print("userproc %lud wait\n", up->pid);
		handlewait(&w);
	//	print("userproc %lud wait ended\n", up->pid);
		switch(w.alt){
		default:
			/* interrupted; give up */
			print("%lud handlewait interrupted\n", up->pid);
			strcpy(up->exitstatus, "handlewait interrupted");
			goto End;
		case 0:
			/* dispatch system call */
		//	print("%lud syscall %d\n", up->pid, c->nr);
			runsyscall(c);
		//	print("ReleasingSemaphore\n");
			ReleaseSemaphore(c->hs.hclientsig, 1, nil);
			break;
		case 1:
			/* process has exited */
			print("%lud process exited (handleproc=%p)\n", up->pid, c->hs.hclientproc);
			status = 10101010;
			if(!GetExitCodeProcess(c->hs.hclientproc, &status)){
				osrerrstr(err, sizeof err);
				snprint(up->exitstatus, ERRMAX, "GetExitCodeProcess failed: %s", err);
				print("%s\n", up->exitstatus);
				goto End;
			}
			if(status == STILL_ACTIVE){
				print("GetExitCodeProcess returned STILL_ACTIVE?\n");
				strcpy(up->exitstatus, "bad GetExitCodeProcess call");
				goto End;
			}
			if(status != 99)
				snprint(up->exitstatus, ERRMAX, "%d", status);
		End:
			print("userproc %lud is exiting; status=%s\n", up->pid, up->exitstatus);
			pexit(up->exitstatus, 1);
			abort();
		}
	}
}

LRESULT CALLBACK
NewProcProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	Handles *h, *x;
	Proc *p;
	Syscallmem *c;
	char err[ERRMAX];
	HANDLE hproc, hmem;

	print("msg %d\n", msg);
	switch(msg){
	case WM_USER:
		p = findproc(wparam);
		if(p == nil){
			print("Attach as pid %d failed\n", wparam);
			return 1;
		}
		hproc = OpenProcess(PROCESS_ALL_ACCESS, 0, lparam);
		if(hproc == nil){
			osrerrstr(err, sizeof err);
			print("OpenProcess %d: %s\n", lparam, err);
		Err0:
			return 0;
		}
		if(0){
		Err1:
			CloseHandle(hproc);
			goto Err0;
		}

		hmem = CreateFileMapping(nil, nil, PAGE_READWRITE, 0, CallMem, nil);
		if(hmem == nil){
			osrerrstr(err, sizeof err);
			print("CreateFileMapping for %d: %s\n", lparam, err);
			goto Err1;
		}
		if(0){
		Err2:
			CloseHandle(hmem);
			goto Err1;
		}

		c = MapViewOfFile(hmem, FILE_MAP_WRITE, 0, 0, 0);
		if(c == nil){
			osrerrstr(err, sizeof err);
			print("MapViewOfFile for %d: %s\n", lparam, err);
			goto Err2;
		}
		if(0){
		Err3:
			UnmapViewOfFile(c);
			goto Err2;
		}

		h = &c->hs;
		memset(h, 0, sizeof(*h));
		h->hmem = hmem;
		h->hclientproc = hproc;
		if((h->hserversig = CreateSemaphore(nil, 0, 1, nil)) == nil){
			osrerrstr(err, sizeof err);
			print("CreateSemaphore: %s\n", err);
			goto Err3;
		}
		if(0){
		Err4:
			CloseHandle(h->hserversig);
			goto Err3;
		}

		if((h->hclientsig = CreateSemaphore(nil, 0, 1, nil)) == nil){
			osrerrstr(err, sizeof err);
			print("CreateSemaphore: %s\n", err);
			goto Err4;
		}
		if(0){
		Err5:
			CloseHandle(h->hclientsig);
			goto Err4;
		}

		if((h->hkillsig = CreateSemaphore(nil, 0, 1, nil)) == nil){
			osrerrstr(err, sizeof err);
			print("CreateSemaphore: %s\n", err);
			goto Err5;
		}
		if(0){
		Err6:
			CloseHandle(h->hclientsig);
			goto Err5;
		}
		x = &c->hc;
		memset(x, 0, sizeof(*x));
		/*
		 * We can't close the other guy's handles, so if an error occurs
		 * we have to just leave them and only clean up after ourselves.
		 */
		if(!DuplicateHandle(me, h->hserversig, hproc, &x->hserversig, 0, 0, DUPLICATE_SAME_ACCESS)){
			osrerrstr(err, sizeof err);
			print("DuplicateHandle 0 %p for %d: %s\n", h->hserversig, lparam, err);
			goto Err6;
		}
		if(!DuplicateHandle(me, h->hclientsig, hproc, &x->hclientsig, 0, 0, DUPLICATE_SAME_ACCESS)){
			osrerrstr(err, sizeof err);
			print("DuplicateHandle 1 %p for %d: %s\n", h->hclientsig, lparam, err);
			goto Err6;
		}
		if(!DuplicateHandle(me, h->hmem, hproc, &x->hmem, 0, 0, DUPLICATE_SAME_ACCESS)){
			osrerrstr(err, sizeof err);
			print("DuplicateHandle 2 %p for %d: %s\n", h->hmem, lparam, err);
			goto Err6;
		}
		if(!DuplicateHandle(me, h->hkillsig, hproc, &x->hkillsig, 0, 0, DUPLICATE_SAME_ACCESS)){
			osrerrstr(err, sizeof err);
			print("DuplicateHandle 2 %p for %d: %s\n", h->hkillsig, lparam, err);
			goto Err6;
		}
		c->p = p;
		kprocchild(c->p, userproc, c);
		ready(c->p);
		return (LRESULT)x->hmem;

	default:
		return DefWindowProc(hwnd, msg, wparam, lparam);
	}
}

/*
 * Break the command line into arguments
 * The rules for this are not documented but appear to be the following
 * according to the source for the microsoft C library.
 * Words are seperated by space or tab
 * Words containing a space or tab can be quoted using "
 * 2N backslashes + " ==> N backslashes and end quote
 * 2N+1 backslashes + " ==> N backslashes + literal "
 * N backslashes not followed by " ==> N backslashes
 */
static int
args(char *argv[], int n, char *p)
{
	char *p2;
	int i, j, quote, nbs;

	for(i=0; *p && i<n-1; i++) {
		while(*p == ' ' || *p == '\t')
			p++;
		quote = 0;
		argv[i] = p2 = p;
		for(;*p; p++) {
			if(!quote && (*p == ' ' || *p == '\t'))
				break;
			for(nbs=0; *p == '\\'; p++,nbs++)
				;
			if(*p == '"') {
				for(j=0; j<(nbs>>1); j++)
					*p2++ = '\\';
				if(nbs&1)
					*p2++ = *p;
				else
					quote = !quote;
			} else {
				for(j=0; j<nbs; j++)
					*p2++ = '\\';
				*p2++ = *p;
			}
		}
		/* move p up one to avoid pointing to null at end of p2 */
		if(*p)
			p++;
		*p2 = 0;	
	}
	argv[i] = 0;

	return i;
}

void
ossched(void)
{
	Sleep(0);
}

static int
is9pm(char *s, int ns)
{
	char *e;
	int len;

	len = strlen(magic);
	e = s+ns-len;
	for(; s<e; s++)
		if(memcmp(s, magic, len)==0)
			return 1;
	return 0;
}

long
sysexec(ulong *arg)
{
	int indir, n, m;
	char *buf, *p;
	Chan *tc;
	char *file;
	char **args;
	Rune *r;

	file = (char*)arg[0];
	indir = 0;
	elem = nil;
	if(waserror()){
		free(elem);
		nexterror();
	}
	for(;;){
		tc = namec(file, Aopen, OEXEC, 0);
		if(waserror()){
			cclose(tc);
			nexterror();
		}
		if(!indir)
			kstrdup(&elem, up->genbuf);

		n = devtab[tc->type]->read(tc, &exec, sizeof(Exec), 0);
		if(n < 2)
			error(Ebadexec);
		magic = l2be(exec.magic);
		text = l2be(exec.text);
		entry = l2be(exec.entry);
		if(n==sizeof(Exec) && (magic == AOUT_MAGIC)){
			if((text&KZERO) == KZERO
			|| entry < UTZERO+sizeof(Exec)
			|| entry >= UTZERO+sizeof(Exec)+text)
				error(Ebadexec);
			break; /* for binary */
		}

		/*
		 * Process #! /bin/sh args ...
		 */
		memmove(line, &exec, sizeof(Exec));
		if(indir || line[0]!='#' || line[1]!='!')
			error(Ebadexec);
		n = shargs(line, n, progarg);
		if(n == 0)
			error(Ebadexec);
		indir = 1;
		/*
		 * First arg becomes complete file name
		 */
		progarg[n++] = file;
		progarg[n] = 0;
		validaddr(arg[1], BY2WD, 1);
		arg[1] += BY2WD;
		file = progarg[0];
		if(strlen(elem) >= sizeof progelem)
			error(Ebadexec);
		strcpy(progelem, elem);
		progarg[0] = progelem;
		poperror();
		cclose(tc);
	}

	buf = smalloc(2048);
	indir = 0;
	tc = namec(prog, Aopen, AEXEC, 0);
	if(waserror()){
		free(buf):
		cclose(c);
		nexterror();
	}
	m = 0;
	while(m < 2048){
		n = devtab[tc->type]->read(tc, buf+m, 2048-m, m);
		if(n <= 0)
			break;
		m += n;
	}

	if(m[0]=='#' && m[1]=='!'){		/* shell script */
		m[2047] = '\0';
		p = strchr(m, '\n');
		*p = '\0';
		prog = m+2;
		cclose(tc);
		poperror();
	}

	if(m[0]!='M' || m[1]!='Z')
		error(Ebadexec);

	if(is9pm(m, 2048))
}
