/*	SCCS Id: @(#)nhprocs.c	3.2	96/02/02	*/
/* Copyright (c) NetHack Windows Porting Team 1995      */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"

#ifdef WIN32_GRAPHICS

# ifndef NO_SIGNAL
#include <signal.h>
# endif
#include <ctype.h>
#include <sys\stat.h>
#include "win32api.h"
#include "nhwin32.h"

#define DEBUG

/* 
 *  Interface definition, for windows.c 
 */
struct window_procs win32_procs = {
    "win32",
    win32_init_nhwindows,
    win32_player_selection,
    win32_askname,
    win32_get_nh_event,
    win32_exit_nhwindows,
    win32_suspend_nhwindows,
    win32_resume_nhwindows,
    win32_create_nhwindow,
    win32_clear_nhwindow,
    win32_display_nhwindow,
    win32_destroy_nhwindow,
    win32_curs,
    win32_putstr,
    win32_display_file,
    win32_start_menu,
    win32_add_menu,
    win32_end_menu,
    win32_select_menu,
    genl_message_menu,
    win32_update_inventory,
    win32_mark_synch,
    win32_wait_synch,
#ifdef CLIPPING
    win32_cliparound,
#endif
#ifdef POSITIONBAR
    donull,
#endif
    win32_print_glyph,
    win32_raw_print,
    win32_raw_print_bold,
    win32_nhgetch,
    win32_nh_poskey,
    win32_nhbell,
    win32_doprev_message,
    win32_yn_function,
    win32_getlin,
    win32_get_ext_cmd,
    win32_number_pad,
    win32_delay_output,

    /* other defs that really should go away (they're tty specific) */
    win32_start_screen,
    win32_end_screen,
    genl_outrip,
};

/* Local functions */
static void win32_putsym(winid, int, int, int, unsigned char, boolean);
static int  has_color(int color);
static int key_from_buf();
static HANDLE new_dialog();
static void deferred_init(winid, int);

struct win32_WinDesc *wins[MAXWIN];
struct win32_DisplayDesc *win32Display;	/* the win32 display descriptor */
winid  WIN_BASE;

static int maxwin = 0;			/* number of windows in use */
static char winpanicstr[] = "Bad window id %d";
# ifdef CLIPPING
static boolean clipping = FALSE;	/* clipping on? */
static int clipx = 0, clipy = 0, clipxmax = 0, clipymax = 0;
# endif
#define PICK_PROMPT "Shall I pick a character for you? [Y, N, or Q(quit)] "
static char to_continue[] = "to continue";
winid window;
int x, y;
int color;
unsigned char ch;

#ifdef DEBUG_FULL
static char debugbuf[256];
#endif

void
win32_init_nhwindows(argcp, argv)
int *argcp;
char **argv;
{
	int status;
	static FARPROC pfnNameDialog;

        hDefFnt = GetStockObject(OEM_FIXED_FONT);
        if (!hDefFnt) hDefFnt = GetStockObject(SYSTEM_FIXED_FONT);
	WIN_BASE = win32_create_nhwindow(NHW_BASE);
	pfnNameDialog = MakeProcInstance(CopyrightProc,
					 wcNetHack.hInstance);
	DialogBox(wcNetHack.hInstance,"HACK_COPYRIGHT",BasehWnd,
				pfnNameDialog);
	status = InitTextWindow();
	status = InitPopupWindow();
	status = InitListboxWindow();
	ShowWindow(BasehWnd,SW_SHOWDEFAULT);
	UpdateWindow(BasehWnd);
	

	pchBuf = (char *)alloc(RINGBUFSIZE);   /* allocate the input buffer */
	pchGet = pchBuf;
	pchPut = pchBuf;
	pchCount = 0;
	return;
}

void
win32_player_selection()
{
	char pc;


	if ((pc = highc(pl_character[0])) != 0) {
		if (index(pl_classes, pc) != (char *)0) {
			pl_character[0] = pc;
			return;
		}
	}
	DialogBox(wcNetHack.hInstance,"CHAR_SEL_DIALOG",BasehWnd,
			(DLGPROC)PlayerSelectProc);
	return;	
}

/*
 * plname is filled either by an option (-u Player  or  -uPlayer) or
 * explicitly (by being the wizard) or by askname.
 * It may still contain a suffix denoting pl_character.
 * Always called after init_nhwindows() and before display_gamewindows().
 */
void
win32_askname()
{
	input_text_size = 0;
	while (input_text_size == 0) {
		DialogBox(wcNetHack.hInstance,"ASK_NAME_DIAL",BasehWnd,
			(DLGPROC)AskNameProc);
	}
	strncpy(plname, input_text, input_text_size);
	*(plname + input_text_size) = '\0';
	return;	
}

void
win32_get_nh_event()
{
        MSG  lpMsg;

        if (PeekMessage(&lpMsg,0,0,0,PM_REMOVE)) {
/*                if (msg.message == WM_QUIT) */
                TranslateMessage(&lpMsg);
                DispatchMessage(&lpMsg);
        }
}

void
win32_suspend_nhwindows(str)
    const char *str;
{
}

void
win32_resume_nhwindows()
{
}

void
win32_exit_nhwindows(str)
    const char *str;
{
}

winid
win32_create_nhwindow(type)
int type;
{
	RECT rcClient;
	struct win32_WinDesc *newwin;
	winid newid;
	char WindowName[80];
	int status;
	int msize = 0;
	int i;

	/* allocate the Window Descriptor */
	newwin = (struct win32_WinDesc *)alloc(sizeof(struct win32_WinDesc));
	memset(newwin,0,sizeof(struct win32_WinDesc));
	newwin->type = TYPE_INVALID;

	/* Get an unused Window ID number */
	for(newid = 0; newid < MAXWIN; newid++) {
		if(wins[newid] == 0) {
			wins[newid] = newwin;
			break;
		}
	}
	if (newid == MAXWIN) {
		panic("No free window slots!");
		return WIN_ERR;
	}

	newwin->WindowWidth = 0;
	newwin->WindowHeight = 0;
	newwin->dwCharX = DefCharWidth;
	newwin->dwCharY = DefCharHeight;
	newwin->morestr = (char *)0;

	/* Build the appropriate type of Win32 Window */
	switch(type) {	
		
		case NHW_BASE:
				InitBaseWindow();
				newwin->hWnd = BasehWnd;
				newwin->type = NHW_BASE;
				newwin->wflags = 0L;
				newwin->BackGroundColor = colormap[CLR_BLACK];
				newwin->NormalTextColor = colormap[CLR_GRAY];
				break;
	
		case NHW_MESSAGE:
				strcpy(WindowName,"NetHack Message Window");
 				newwin->hWnd=CreateWindowEx(
					0,
					NHTextClassName,
					WindowName,
					WS_CHILD|WS_VISIBLE
#if 0
					|WS_BORDER|WS_VSCROLL,
#endif
					|WS_BORDER,
					0,0,
					newwin->WindowWidth,
					newwin->WindowHeight,
					BasehWnd,(HMENU)(int)newid,
					wcNetHack.hInstance,
					(LPSTR)0);
				if (!newwin->hWnd) status = GetLastError();
				newwin->type = NHW_MESSAGE;
				newwin->wflags = 0L;
				newwin->maxrows = 5;
				newwin->maxcols = COLNO;
				newwin->BackGroundColor = colormap[CLR_BLACK];
				newwin->NormalTextColor = colormap[CLR_GRAY];
				newwin->cursy = 0;
				newwin->cursx = 0;
				break;
		case NHW_STATUS:
				strcpy(WindowName,"NetHack Status Window");
 				newwin->hWnd=CreateWindowEx(
					0,
					NHTextClassName,
					WindowName,
					WS_CHILD|WS_VISIBLE|WS_BORDER,
					0,0,
					newwin->WindowWidth,
					newwin->WindowHeight,
					BasehWnd,(HMENU)(int)newid,
					wcNetHack.hInstance,
					(LPSTR)0);
				if (!newwin->hWnd) status = GetLastError();
				newwin->type = NHW_STATUS;
				newwin->wflags = 0L;
				newwin->maxrows = 2;
				newwin->maxcols = COLNO;
				newwin->cursy = 0;
				newwin->cursx = 0;
				newwin->BackGroundColor = colormap[CLR_BLACK];
				newwin->NormalTextColor = colormap[CLR_GRAY];
				break;
		case NHW_MAP:
				strcpy(WindowName,"NetHack Map Window");
 				newwin->hWnd=CreateWindowEx(
					0,
					NHTextClassName,
					WindowName,
					WS_CHILD|WS_VISIBLE|WS_BORDER,
					0,0,
					newwin->WindowWidth,
					newwin->WindowHeight,
					BasehWnd,(HMENU)(int)newid,
					wcNetHack.hInstance,
					(LPSTR)0);
				if (!newwin->hWnd) status = GetLastError();
				newwin->type = NHW_MAP;
				newwin->wflags = 0;
				newwin->maxrows = ROWNO;
				newwin->maxcols = COLNO;
#if 0
				newwin->BackGroundColor = RGB(80,80,80);
				newwin->NormalTextColor = RGB(175,175,175);
#endif
				newwin->BackGroundColor = colormap[CLR_BLACK];
				newwin->NormalTextColor = colormap[CLR_GRAY];
				if (tiles_on)
					newwin->wflags |= WFLAGS_TILED;
				break;
		case NHW_TEXT:
		case NHW_MENU:
				/* This will be a menu or text window
				 * The first call to place something into 
				 * it is what determines the type, so all
				 * we can do for now is allocate the structure
			         * and flag that the type has not been
				 * determined yet.  The window will actually
				 * be created later, after type determination.
				 */
				newwin->type = TYPE_UNDETERMINED;
				newwin->BackGroundColor = colormap[CLR_BLACK];
				newwin->NormalTextColor = colormap[CLR_GRAY];
				newwin->extra = newid;
				newwin->widest = 0;
				newwin->wflags = 0L;
				newwin->morestr = "--more--";
				break;
	}
	if ((newwin->type != TYPE_UNDETERMINED) && 
	   ((newwin == (struct win32_WinDesc *)0) || (!newwin->hWnd))) {
		panic("Win32 Window creation failure!");
		return WIN_ERR;
	}
#if 0
	ShowWindow(newwin->hWnd,SW_SHOWDEFAULT);
	UpdateWindow(newwin->hWnd);
#endif
	if (newwin->type != TYPE_UNDETERMINED) {
		msize = newwin->maxrows * newwin->maxcols;
		newwin->data  = (uchar *)alloc(msize * sizeof(uchar));
		if (newwin->wflags & WFLAGS_TILED) 
			newwin->glyph = (int *)alloc(msize * sizeof(int));
		memset(newwin->data,' ',msize * sizeof(uchar));
		newwin->color = (int *)alloc(msize * sizeof(int));
		for (i = 0; i < msize; ++i)
			*(newwin->color + i) = newwin->NormalTextColor;
		newwin->hFnt = hDefFnt;
		if (newid == 3) {	/* the last of the default windows */
		    GetClientRect(BasehWnd, &rcClient);
		    EnumChildWindows(BasehWnd, EnumChildProc,(LPARAM)&rcClient);
		}
		newwin->active = TRUE;
	}
	return newid; 
}

void
win32_clear_nhwindow(window)
winid window;
{
	int i;
	int msize;

	if (window != NHW_MESSAGE) {
		msize = wins[window]->maxrows * wins[window]->maxcols;
		memset(wins[window]->data,' ',msize * sizeof(uchar));
		for (i = 0; i < msize ; ++i)
			*(wins[window]->color + i) =
				wins[window]->BackGroundColor;
	} else {
		/* gray old messages out */
		msize = (wins[window]->maxrows) * wins[window]->maxcols;
		for (i = 0; i < msize ; ++i)
			*(wins[window]->color + i) =
				RGB(140,140,140);
	}
	InvalidateRect(wins[window]->hWnd, (RECT *)0, FALSE);
}

/*ARGSUSED*/
void
win32_display_nhwindow(window, blocking)
winid window;
boolean blocking;
{
	if (window == WIN_MESSAGE) iflags.window_inited = TRUE;
	if (blocking && wins[window]->morestr) 
		win32_putstr(window, 1, wins[window]->morestr);

	if (wins[window]->type == NHW_TEXT && !wins[window]->active) {
		deferred_init(window, 2);
	}
	ShowWindow(wins[window]->hWnd,SW_SHOWNORMAL);
	if (blocking) {
		win32_nhgetch();
	}
}

void
win32_destroy_nhwindow(window)
winid window;
{
	int i;
	int stat;

	if (wins[window]->type == NHW_MENU) {
		for (i = 0; i < MenuCount[window]; ++i) {
			if (MenuPtr[window][i]) {
				if (MenuPtr[window][i]->str)
				    free((genericptr_t)MenuPtr[window][i]->str);
				free((genericptr_t)MenuPtr[window][i]);
				MenuPtr[window][i] = (struct win32_menuitem *)0;
			}
		}	
		MenuCount[window] = 0;
		--MenuWindowCount;
	}
	if (wins[window]->hWnd != 0) {
		stat = DestroyWindow(wins[window]->hWnd);
	}
	if (wins[window]) free(wins[window]);
	else impossible("attempt to free wins[window] = 0 [%d]", window);
	wins[window] = 0;
}

void
win32_curs(window, col, row)
winid window;
register int col, row;	/* not xchar: perhaps xchar is unsigned and
			   curx-x would be unsigned as well */
{
	wins[window]->cursx = col;
	wins[window]->cursy = row;
	wins[window]->nCaretPosX = col * wins[window]->dwCharX;
	wins[window]->nCaretPosY = row * wins[window]->dwCharY;
	SetCaretPos(wins[window]->nCaretPosX,wins[window]->nCaretPosY);
}

static void
win32_putsym(window, col, row, rgbcolor, ch, flushflag)
winid window;
int col, row;
int rgbcolor;
unsigned char ch;
boolean flushflag;
{
	RECT rect;
/*	char tch[2]="X"; */
	int offset;
	uchar *pch;
	int *pcolor;

	offset = (row * wins[window]->maxcols) + col;
	pch = wins[window]->data + offset;
	pcolor = wins[window]->color + offset;
	*pch = ch;
	*pcolor = rgbcolor;

	if (flushflag) {
		/* signal to windows that the display is outdated */
		rect.left   = col * DefCharWidth;
		rect.top    = row * DefCharHeight;
		rect.right  = col * DefCharWidth + DefCharWidth;
		rect.bottom = row * DefCharHeight + DefCharHeight;

		InvalidateRect(wins[window]->hWnd, &rect, FALSE);
	}
}

void
win32_putstr(window, attr, str)
winid window;
int attr;
const char *str;
{
	int i, j;
	RECT rect;
	int cnt,start;
	char *tmp;

	int offset;
	uchar *pch;
	int *pcolor;

	if (wins[window]->type == TYPE_UNDETERMINED) {
	/* Okay, now we know...its destined to be a text window */
		deferred_init(window, 1);
	}

	switch(wins[window]->type) {
	    case NHW_MESSAGE:
		tmp = (char *)alloc(strlen(str)+1);
		Strcpy(toplines, str); 		/* for Norep() */
		Strcpy(tmp, str);
		if (MessageCount >= MAX_MESSAGE_COUNT) {
			free((genericptr_t)MessagePtr[0]);
			for (i = 0; i < (MessageCount - 1); ++i) {
				MessagePtr[i] = MessagePtr[i + 1];
			}
			MessagePtr[MessageCount - 1] = tmp;
		} else {
			MessagePtr[MessageCount++] = tmp;
		}
		/* clear out old data */
		i = wins[window]->maxrows * wins[window]->maxcols;
		memset(wins[window]->data,' ',i * sizeof(uchar));

		/* add the new data */
		for (i = 0; i < MessageCount; ++i) {
			offset = (i * wins[window]->maxcols);
			pch = wins[window]->data + offset;
			pcolor = wins[window]->color + offset;
			if (i == MessageCount - 1)  {
			    for (j = 0; j < wins[window]->maxcols; ++j)
				*(wins[window]->color + (offset + j)) =
					RGB(200,200,200);
			}
			memcpy(pch, MessagePtr[i], strlen(MessagePtr[i]));
		}
		InvalidateRect(wins[window]->hWnd, (RECT *)0, FALSE);
		break;
	    case NHW_MENU:
		impossible("putstr to a menu window (%d)", window);
		break;
	    case NHW_STATUS:
		for (cnt=0; cnt < strlen(str); ++cnt) {
		win32_putsym(window,wins[window]->cursx++,wins[window]->cursy,
				wins[window]->NormalTextColor,*(str + cnt), 
				TRUE);
		}
		break;
	    case NHW_TEXT:
		/* fall through */
	    default:
		if (strlen(str) > wins[window]->widest) {
			wins[window]->widest = strlen(str);
		}
		for (cnt=0; cnt < (int)strlen(str); ++cnt) {
		win32_putsym(window,wins[window]->cursx++,wins[window]->cursy,
				wins[window]->NormalTextColor,*(str + cnt), 
				FALSE);
		}
		win32_curs(window,0,wins[window]->cursy+1);
	}
}

void
#ifdef FILE_AREAS
win32_display_file(farea, fname, complain)
const char *farea;
#else
win32_display_file(fname, complain)
#endif
const char *fname;
boolean complain;
{
}


void
win32_update_inventory()
{
#ifdef DEBUG_FULL
	DEBUG_MSG("into update_inventory");
#endif
}

void
win32_mark_synch()
{
}

void
win32_wait_synch()
{
}

void
#ifdef CLIPPING
win32_setclipped()
{
}

void
win32_cliparound(x, y)
int x, y;
{
}
#endif /* CLIPPING */

/*
 *  win32_print_glyph
 *
 *  Print the glyph to the output device.  Don't flush the output device.
 *
 *  Since this is only called from show_glyph(), it is assumed that the
 *  position and glyph are always correct (checked there)!
 */
void
win32_print_glyph(window, col, row, glyph)
winid window;
xchar col, row;
int glyph;
{
    uchar   ch;
    register int offset;
    int *ip;
    int color;

    /* In this routine 'color' is a NetHack color, not a WIN32 color */
    color = NO_COLOR;
    if (wins[window]->wflags & WFLAGS_TILED) {
	ip = wins[window]->glyph + ((row * wins[window]->maxcols) + col); 
	*ip = glyph;
    } else {
	    /*
	     *  Map the glyph back to a character.
	     *
	     *  Warning:  For speed, this makes an assumption on the order of
	     *		  offsets.  The order is set in display.h.
	     */
	    if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {	  /* swallow */
		/* see swallow_to_glyph() in display.c */
		ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
		mon_color(offset >> 3);
	    } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) { /* zap beam*/
		/* see zapdir_to_glyph() in display.c */
		ch = showsyms[S_vbeam + (offset & 0x3)];
		zap_color((offset >> 2));
	    } else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) { /* cmap */
		ch = showsyms[offset];
		cmap_color(offset);
	    } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {  /* object */
		ch = oc_syms[(int)objects[offset].oc_class];
		obj_color(offset);
	    } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) { /* corpse */
		ch = oc_syms[(int)objects[CORPSE].oc_class];
		mon_color(offset);
	    } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) {  /* pet */
		ch = monsyms[(int)mons[offset].mlet];
		pet_color(offset);
	    } else {						   /* monster */
		ch = monsyms[(int)mons[glyph].mlet];
		mon_color(glyph);
	    }
#ifdef REINCARNATION
    	    if (Is_rogue_level(&u.uz)) color = NO_COLOR;
#endif
	    win32_curs(window,col,row);
	    win32_putsym(window,wins[window]->cursx,
			 wins[window]->cursy,colormap[color],ch, TRUE);
    }
        
}

void
win32_raw_print(str)
const char *str;
{
	int button;

	if (str)
		if (strlen(str) != 0) {
			button = MessageBox((HWND)0,str,"raw_print",
			    MB_APPLMODAL|MB_ICONSTOP
#ifndef DEBUG
				|MB_OK);
#else
				|MB_OKCANCEL);
			if (button == IDCANCEL) 
				button = *((int *)0); /* Bombs away ! */
#endif
		}
}

void
win32_raw_print_bold(str)
const char *str;
{
	win32_raw_print(str);
}

int
win32_nhgetch()
{
	return key_from_buf();
}

/*
 * return a key, or 0, in which case a mouse button was pressed
 * mouse events should be returned as character postitions in the map window.
 */
/*ARGSUSED*/
int
win32_nh_poskey(x, y, mod)
int *x, *y, *mod;
{
	return key_from_buf();	/* mouse support to come later */
}

int
win32_doprev_message()
{
    register struct win32_WinDesc *cw = wins[WIN_MESSAGE];

    return 0;
}

char
win32_yn_function(query, choices, def)
const char *query,*choices;
char def;
/*
 *   Generic yes/no function. 'def' is the default (returned by space or
 *   return; 'esc' returns 'q', or 'n', or the default, depending on
 *   what's in the string. The 'query' string is printed before the user
 *   is asked about the string.
 *   If choices is NULL, any single character is accepted and returned.
 */
{
	unsigned int style;
	int button;
	char buf[QBUFSZ];
	int i;

	if (choices) {
	    char *cb, choicebuf[QBUFSZ];

	    Strcpy(choicebuf, choices);	/* anything beyond <esc> is hidden */
	    if ((cb = index(choicebuf, '\033')) != 0) *cb = '\0';
	    if ((1 + strlen(query) + 2 + strlen(choicebuf) + 4) >= QBUFSZ)
		panic("yn_function: question too long");
	    if (query) {
		Sprintf(buf, "%s [%s] ", query, choicebuf);
		if (def) Sprintf(eos(buf), "(%c) ", def);
	    }
	}else
		return key_from_buf();
	i = strlen(buf);
	style =	MB_APPLMODAL|MB_ICONQUESTION|MB_YESNOCANCEL;
	if ((def == 'y') || (def == 'Y'))
		style |= MB_DEFBUTTON1;
	else if((def == 'n') || (def == 'N'))
		style |= MB_DEFBUTTON2;
	else if((def == 'q') || (def == 'Q'))
		style |= MB_DEFBUTTON3;

	button = MessageBox(
			BasehWnd,
			buf,
			"Question",
			style);
	if (button == IDYES)
		return 'y';
	else if (button == IDNO)
		return 'n';
	else if (button == IDCANCEL)
		return 'q';
	else return button;
}
void
win32_delay_output()
{
}

void
win32_end_screen()
{
}

int
win32_get_ext_cmd()
{
	return 0;
}

void
win32_getlin(pr,inline)
const char *pr;
char *inline;
{
}

void
win32_nhbell()
{
}

void
win32_number_pad(x)
int x;
{
}
void
win32_start_screen()
{
}

static int has_color(int color)
{
	return 1;
}

int
key_from_buf()
{
	int ch;

	inputstatus = WAITING_FOR_KEY;
	while (!pchCount) {
		win32_get_nh_event();
	}
	inputstatus = 0;
	ch = *pchGet++;
	if (pchGet >= pchBuf + (RINGBUFSIZE - 1)) pchGet = pchBuf; /* wrap */
	--pchCount;
	return ch;
}

void deferred_init(window,part)
winid window;
int part;
{
	int i, msize, status;
	char WindowName[80];

#ifdef DEBUG_FULL
	DEBUG_MSG("Initializing a text window");
#endif
	/* Do all the initialization that we couldn't do earlier */
	if (part == 1) {
		/* the stuff NetHack depends on */
#ifdef DEBUG_FULL
		DEBUG_MSG("About to do deferred init part 1");
#endif
		wins[window]->active = FALSE;
		wins[window]->type = NHW_TEXT;
		wins[window]->wflags = 0L;
		wins[window]->maxrows = ROWNO;
		wins[window]->maxcols = COLNO;
		wins[window]->hFnt = hDefFnt;
		msize = wins[window]->maxrows * wins[window]->maxcols;
		wins[window]->data  = 
			(uchar *)alloc(msize * sizeof(uchar));
		memset(wins[window]->data,' ',msize * sizeof(uchar));
		wins[window]->color = 
			(int *)alloc(msize * sizeof(int));
		for (i = 0; i < msize; ++i)
		     *(wins[window]->color + i) = wins[window]->NormalTextColor;
	} else if (part == 2) {
		/* the stuff WIN32 depends on */
#ifdef DEBUG_FULL
		DEBUG_MSG("About to do deferred init part 2");
#endif
		wins[window]->WindowWidth = max((BaseWidth / COLNO) *
					wins[window]->widest, 
					(BaseWidth / COLNO) * 40);
		wins[window]->WindowHeight = (BaseHeight / ROWNO) *
					wins[window]->cursy;
		wins[window]->nWindowX = 0;
		wins[window]->nWindowY = 0;

		Sprintf(WindowName,"NetHack Text Window %d",window);
		wins[window]->hWnd=CreateWindowEx(
			0,
			NHTextClassName,
			WindowName,
			WS_CHILD|WS_VISIBLE|WS_BORDER,
			wins[window]->nWindowX,
			wins[window]->nWindowY,
			wins[window]->WindowWidth,
			wins[window]->WindowHeight,
			BasehWnd,(HMENU)(int)window,
			wcNetHack.hInstance,
			(LPSTR)0);
		if (!wins[window]->hWnd) {
			status = GetLastError();
			DEBUG_MSG("Deferred init: Init of Window failed");
		}
		wins[window]->active = TRUE;	/* mark it active */
	} 
#ifdef DEBUG_FULL
	else
		DEBUG_MSG("Deferred init: Unrecognized part");
#endif
}

/*
 * -----------------------------------------------------------------------
 * Menu support routines
 * -----------------------------------------------------------------------
 */

void
win32_start_menu(window)
winid window;
{
	int i,status;
	char WindowName[80];

	if (wins[window]->type == TYPE_UNDETERMINED) {
	/* Okay, now we know...its destined to be a menu window */
		strcpy(WindowName,"NetHack Menu");
		wins[window]->type = NHW_MENU;
		wins[window]->hFnt = hDefFnt;
		wins[window]->wflags = 0L;
		wins[window]->maxrows = 52;
		wins[window]->maxcols = COLNO;
		wins[window]->cursy = 0;
		wins[window]->cursx = 0;
		wins[window]->data = (uchar *)0;
		wins[window]->color = (int *)0;
 		wins[window]->hWnd = CreateWindowEx(
			0,
			NHListboxClassName,
			WindowName,
			WS_POPUP | WS_VISIBLE | WS_BORDER,
			CW_USEDEFAULT, CW_USEDEFAULT,
			CW_USEDEFAULT, CW_USEDEFAULT,
			(HWND)0,(HMENU)0,
			wcNetHack.hInstance,
			(LPSTR)0);
		if (!wins[window]->hWnd) status = GetLastError();
		else
		/* Add the list box to the window */
			wins[window]->hDlg = CreateWindowEx(
				0,
				"LISTBOX",
				"NetHack List",
LBS_NOTIFY | WS_BORDER | WS_VISIBLE | LBS_WANTKEYBOARDINPUT | LBS_USETABSTOPS,
				CW_USEDEFAULT, CW_USEDEFAULT,
				CW_USEDEFAULT, CW_USEDEFAULT,
				wins[window]->hWnd, (HMENU)0,
				wcNetHack.hInstance, (LPSTR)0);
		if (!wins[window]->hDlg) DEBUG_MSG("Problem with CreateDialog");
		MenuWindowCount++;
	} else if (wins[window]->type = NHW_MENU) {
#ifdef DEBUG_FULL
		DEBUG_MSG("Clearing already used menu out");
#endif
		for (i = 0; i < MenuCount[window]; ++i) {
			if (MenuPtr[window][i]) {
				if (MenuPtr[window][i]->str)
				    free((genericptr_t)MenuPtr[window][i]->str);
				free((genericptr_t)MenuPtr[window][i]);
				MenuPtr[window][i] = (struct win32_menuitem *)0;
			}
		}	
		MenuCount[window] = 0;
		SendMessage(wins[window]->hDlg,
				LB_RESETCONTENT, (WPARAM)0, (LPARAM)0);
	} else
		impossible("Start_menu on window type %d", wins[window]->type);
}

/*
 * Add a text line str to the given menu window.  If identifier
 * is 0, then the line cannot be selected (e.g. a title).
 * Otherwise, identifier is the value returned if the line is
 * selected.  Accelerator (ch) is a keyboard key that can be used
 * to select the line.  If the accelerator of a selectable
 * item is 0, the window system is free to select its own
 * accelerator.  It is up to the window-port to make the
 * accelerator visible to the user (e.g. put "a - " in front
 * of str).  The value attr is the same as in putstr().
 * Glyph is an optional glyph to accompany the line.  If
 * the window port cannot or does not want to display it, this
 * is OK.  If there is no glyph applicable, then this
 * value will be NO_GLYPH.
 * All accelerators should be in the range [A-Za-z].
 * It is expected that callers do not mix accelerator
 * choices.  Either all selectable items have an accelerator
 * or let the window system pick them.  Don't do both.
 */

void
win32_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
winid window;
int glyph;                  /* unused (for now) */
const anything *identifier;
char ch;
char gch;		    /* group accelerator, 0 => none */
int attr;
const char *str;
boolean preselected;
{
	struct win32_menuitem *menuitem;

/*	char buf[BUFSZ]; */
	char *tmp;
	char *extra = "? - ";
#ifdef DEBUG_FULL
	DEBUG_MSG("into add_menu");
#endif
	if (wins[window]->type == TYPE_UNDETERMINED) {
		impossible("Trying to add to a non-existant menu window %d",
				window);
		return;
	}
	if (wins[window]->type != NHW_MENU)
		impossible("Window type %d in add_menu", 
				wins[window]->type);
	if (str) {
		tmp = (char *)alloc(strlen(str) + 1 + strlen(extra));
	} else
		tmp = (char *)0;
	menuitem = 
		(struct win32_menuitem *)alloc(sizeof(struct win32_menuitem));
	if (!menuitem) return;
	menuitem->glyph = glyph;
	menuitem->identifier = *identifier;
	menuitem->ch = ch;
	menuitem->attr = attr;
	menuitem->str = tmp;
	if (MenuCount[window] >= MAX_INVENTORY) {
		impossible("Too many inventory items in add_menu (%d)",
				MenuCount[window]);
		return;
	}
	if (menuitem->identifier.a_void) {
	    if (menuitem->str)
		strcpy(menuitem->str, (str != (char *)0) ? str : "Oops!");
	} else 
		Sprintf(menuitem->str,"%s",
			 (str != (char *)0) ? str : "Oops");
	MenuPtr[window][MenuCount[window]] = menuitem;
	++MenuCount[window];
}

/*
 * Stop adding entries to the menu.
 * Prompt is a prompt to give the user.  If prompt is NULL, no prompt will
 * be printed.
 */
#if 0
char mbuf[BUFSZ * 8];
#endif

void
win32_end_menu(window, ch, str, morestr)
winid window;
char ch;
const char *str;
const char *morestr;
{
	int i, j, junk;
	char buf[BUFSZ];
#ifdef DEBUG_FULL
	char buf2[BUFSZ];
#endif
	boolean sflag;

	if (wins[window]->type != NHW_MENU)
		impossible("Window type %d in end_menu", 
				wins[window]->type);
/*	sprintf(buf,"Total items in menu: %d\r", MenuCount[window]); */
	for (i = 0, j = 'a', junk = 0; i < MenuCount[window]; ++i) {
		sflag = FALSE;
		if (MenuPtr[window][i]) {
			if (MenuPtr[window][i]->str) {
				if (MenuPtr[window][i]->identifier.a_void) {
					if (!MenuPtr[window][i]->ch)
						MenuPtr[window][i]->ch = j++;
					Sprintf(buf,"%c - %s",
						MenuPtr[window][i]->ch,
						MenuPtr[window][i]->str);
				} else {
					Sprintf(buf,"\t%s",
						MenuPtr[window][i]->str);
					MenuPtr[window][i]->ch = junk++;
				}
				/* Win32 Listbox control messages */
				SendMessage(wins[window]->hDlg,
					LB_ADDSTRING, (WPARAM)0, 
					(LPARAM)buf);
				SendMessage(wins[window]->hDlg,
					LB_SETITEMDATA, (WPARAM)i,
					(LPARAM)MenuPtr[window][i]->ch);
			}
#ifdef DEBUG_FULL
			else {
				Sprintf(buf2,"Listbox empty string: %d",i);
				DEBUG_MSG(buf2);
			}
#endif
		}
		if (j > 'z') j = 'A';
		if ((j > 'Z') && (j < 'a')) j = '?';
	}	
#ifdef DEBUG_FULL
	DEBUG_MSG(buf);
#endif
	/* TODO: add code to move menu data to the display buffer here */
}

/*
 * Return the number of items selected.  If the value is
 * zero, none were selected.  If items were selected, then
 * selected is filled in with an allocated array of menu_item
 * structures, one for each selected line.  The caller must
 * free this array when done with it.  The "count" field
 * of selected is a user supplied count.  If the user did
 * not supply a count, then the count field is filled with
 * -1 (meaning all).  A count of zero is equivalent to not
 * being selected and should not be in the list.  If no items
 * were selected, then selected is NULL'ed out.  
 *
 * How is the mode of the menu.
 * PICK_NONE	Nothing is selectable,
 * PICK_ONE	Only one thing is selectable
 * PICK_N	Any number valid items may selected 
 * (If how is PICK_NONE, this function should never return anything but 0)
 *
 * You may call select_menu() on a window multiple times --
 * the menu is saved until start_menu() or destroy_nhwindow()
 * is called on the window.
 * Note that NHW_MENU windows need not have select_menu()
 * called for them. There is no way of knowing whether
 * select_menu() will be called for the window at
 * create_nhwindow() time.
 */

int
win32_select_menu(window, how, menu_list)
winid window;
int how;
menu_item **menu_list;
{
/*	win32_menu_item *curr; */
/*	struct menu_info_t *menu_info; */

#if 0
	ShowWindow(wins[window]->hWnd,SW_SHOWDEFAULT);
#endif
	if (wins[window]->type != NHW_MENU)
		impossible("Window type %d in select_menu", 
				wins[window]->type);
	ShowWindow(wins[window]->hWnd,SW_SHOWDEFAULT);
#ifdef DEBUG_FULL
	POP_MESSAGE(mbuf);
#endif
	return 0;
}

#endif /* WIN32_GRAPHICS */
