// windows interface functions

#include <winsock.h>
#include <ctype.h>
#include "WScreen.h"
#include "defs.h"
#include "display.h"
#include "winid.h"
#include "MsgQ.h"
#include "net.h"
#include "server.h"
#include "world.h"

const int WM_SENDQUEUE = WM_USER;
// all messages beyond this are read notifies
const int WM_DATAREADY = WM_USER+1;

const int SEND_QUEUE = 0;
const int DATA_SENT = 1;

const int NOT_SENDING = 0;
const int SENDING_SIZE = 1;
const int SENDING_MESSAGE = 2;
const int SENDING_QSIZE_SIZE = 3;
const int SENDING_QSIZE = 4;

const int NOT_RECEIVING = 0;
const int RECV_SIZE = 1;
const int RECV_MESG = 2;

const int yOffset = 50;

const int LIST_BOX_ID = 5143;

// private data
static HWND hWnd;
static HDC hDC, hMemDC;
static HWND hMsgWin;
static HPALETTE hPal;
static HWND hList;
static HFONT hFont;

struct QStruct
{
  QStruct(MsgQ *queue, int d, int fd) { q = queue; destroy = d; sock = fd; }

  MsgQ *q;
  int destroy, sock;
};

struct PixmapInfo {
  HBITMAP pixmap;
  char *mask;
  int w, h;
};

static List<QStruct*> sendQ;
static Lister<QStruct*> sendQtail(sendQ);

static Lister<Msg> currQl;

static List<HBITMAP> bitmaps;

static RecvInfo **recvInfos;

LONG FAR PASCAL WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL FAR PASCAL DialogProc(HWND hDlg, UINT wMessage, WPARAM wParam, LPARAM lParam)
{
  char name[80];
  char server[128];
  char str[10];
  switch (wMessage) {
  case WM_INITDIALOG:
    SetDlgItemText(hDlg, SERVER_NAME, "ksingh.student.harvard.edu");
    SetDlgItemText(hDlg, PLAYER_NAME, "");
    SetDlgItemText(hDlg, NUM_PLAYERS, "0");
    return TRUE;
  case WM_COMMAND:
    if (wParam == NEW_GAME || wParam == RESTORE_GAME) {
      GetDlgItemText(hDlg, SERVER_NAME, server, 128);
      GetDlgItemText(hDlg, PLAYER_NAME, name, 80);
      GetDlgItemText(hDlg, NUM_PLAYERS, str, 10);
      numPlayers = atoi(str);
      myName = strdup(name);
      serverName = strdup(server);
      if (wParam == NEW_GAME)
	savedGame = 0;
      else
	savedGame = 1;
      EndDialog(hDlg, TRUE);
    }
    break;
  }
  return FALSE;
}

long FAR PASCAL MessageBoxProc(HWND hWnd, UINT wMessage, WPARAM wParam, LPARAM lParam)
{
  HANDLE hInstance;
  CREATESTRUCT *cs;
  LPSTR str;
  switch (wMessage) {
    case WM_CREATE:
      hInstance = GetWindowWord(hWnd, GWW_HINSTANCE);
      cs = (CREATESTRUCT *)lParam;
      str = (LPSTR)cs->lpCreateParams;
      CreateWindow("STATIC", str, WS_CHILD | SS_CENTER | WS_VISIBLE,
		   0, 0, 200, 40, hWnd, NULL, hInstance, NULL);
      CreateWindow("BUTTON", "Ok", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE,
		   80, 45, 40, 30, hWnd, NULL, hInstance, NULL);
      delete str;
      break;
    case WM_COMMAND:
      DestroyWindow(hWnd);
      (*screen->KeyPressed)(MESG_READ, 0, 0, NULL);
      break;
    default:
      return DefWindowProc(hWnd, wMessage, wParam, lParam);
  }
  return 0L;
}

typedef ReadHandler *ReadHandlerP;

WScreen::WScreen(void (*key)(int, int, int, char *),
		 HANDLE hInstance, HANDLE hPrevInstance, int nCmdShow)
  : Graphics(key), colors(1001)
{
  screen = this;
  readHandlers = new ReadHandlerP[FD_SETSIZE];
  recvInfos = new RecvInfoP[FD_SETSIZE];
  for (int i = 0; i < FD_SETSIZE; ++i) {
    readHandlers[i] = NULL;
    recvInfos[i] = NULL;
  }
  WNDCLASS wndclass;
  if (!hPrevInstance) {
    wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = "CivClass";
    if (!RegisterClass(&wndclass))
      return;
    wndclass.style = CS_HREDRAW|CS_VREDRAW;
    wndclass.lpfnWndProc = MessageBoxProc;
    wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
    wndclass.lpszClassName = "CivMessage";
    if (!RegisterClass(&wndclass))
      return;
  }
  hWnd = CreateWindow("CivClass", "Civilization",
		      WS_OVERLAPPEDWINDOW | CS_BYTEALIGNWINDOW,
		      CW_USEDEFAULT, CW_USEDEFAULT, 332, 288,
		      NULL, NULL, hInstance, NULL);

  FILE *f = fopen("c:\\windows\\rgb.txt", "r");
  int numRead = 0;
  while (f != NULL && !feof(f)) {
    char name[80];
    int r, g, b;
    if (fscanf(f, "%d %d %d %s", &r, &g, &b, name) != 4) break;
    ColorInfo *clr = new ColorInfo;
    clr->r = r; clr->g = g; clr->b = b;
    clr->id = -1;
    clr->name = strdup(name);
    colors.Insert(clr, StrKey(clr->name));
    ++numRead;
  }
  fclose(f);
  nextColor = 1;

  WSADATA WSAData;
  WSAStartup(0x300, &WSAData);

  // show the dialog to get the player info
  DialogBox(hInstance, "InitDialog", hWnd, DLGPROC(DialogProc));
}

WScreen::~WScreen()
{
  delete [] readHandlers;
  delete [] recvInfos;
}

void WScreen::AddReadNotify(int fd, void (*func)(MsgQ *, int, int),
			    int info, int q)
{
  ReadHandler *ptr = new ReadHandler;
  readHandlers[fd] = ptr;
  ptr->fd = fd;
  ptr->func = func;
  ptr->info = info;
  ptr->q = q;
  RecvInfo *recvPtr = new RecvInfo;
  recvInfos[fd] = recvPtr;
  recvPtr->stat = NOT_RECEIVING;
  recvPtr->q = NULL;
  long event;
  if (q)
    event = FD_READ;
  else
    event = FD_ACCEPT;
  int err;
  if ((err = WSAAsyncSelect(fd, hWnd, WM_DATAREADY, event)) == SOCKET_ERROR) {
    Debug('n', "Got network error %d\n", err);
    PostQuitMessage(0);
  }
}

int WScreen::AllocColor(char *name)
{
  ColorInfo *clr = colors.Find(StrKey(name));
  if (clr == NULL && *name != '#')
    return -1;
  if (clr == NULL) {
    clr = new ColorInfo;
    char str[5];
    str[4] = 0;
    strncpy(str, name+1, 4);
    clr->r = strtoul(str, NULL, 16)/256L;
    strncpy(str, name+5, 4);
    clr->g = strtoul(str, NULL, 16)/256L;
    strncpy(str, name+9, 4);
    clr->b = strtoul(str, NULL, 16)/256L;
    clr->name = strdup(name);
    clr->id = -1;
    colors.Insert(clr, StrKey(clr->name));
  }
  if (clr->id == -1) {
    clr->id = nextColor++;
    if (nextColor >= 128)
      ::MessageBox(NULL, "Out of Colors", "Error", MB_OK);
    PALETTEENTRY entry;
    entry.peRed = clr->r;
    entry.peGreen = clr->g;
    entry.peBlue = clr->b;
    entry.peFlags = PC_RESERVED;
    AnimatePalette(hPal, clr->id, 1, &entry);
  }
  return clr->id;
}

void WScreen::DisplayMessage(char *str, int color)
{
  SetWindowText(hMsgWin, str);
}

void WScreen::WriteStr(int x, int y, char *str, int color)
{
  int mode = GetBkMode(hDC);
  SetBkMode(hDC, TRANSPARENT);
  SetTextColor(hDC, PALETTEINDEX(color));
  TextOut(hDC, x, y+yOffset, str, strlen(str));
  SetBkMode(hDC, mode);
}

void WScreen::WriteChar(int x, int y, int ch, int color)
{
  char str[2];
  str[0] = ch;
  str[1] = 0;
  WriteStr(x, y, str, color);
}

void WScreen::Clear()
{
  FillRect(0, yOffset, 320, 200, AllocColor("#000000000000"));
}

void WScreen::FillRect(int x, int y, int width, int ht, int col)
{
  if (width == 1 && ht == 1)
    SetPixel(hDC, x, y+yOffset, PALETTEINDEX(col));
  else {
    HBRUSH hBrush = CreateSolidBrush(PALETTEINDEX(col));
    HBRUSH hOld = SelectObject(hDC, hBrush);
    Rectangle(hDC, x, y+yOffset, x+width, y+ht+yOffset);
    SelectObject(hDC, hOld);
    DeleteObject(hBrush);
  }
}

void WScreen::Rect(int x, int y, int width, int ht, int col)
{
  HPEN hPen = CreatePen(PS_SOLID, 1, PALETTEINDEX(col));
  HPEN hOldPen = SelectObject(hDC, hPen);
  MoveTo(hDC, x, y);
  LineTo(hDC, x, y+ht);
  LineTo(hDC, x+width, y+ht);
  LineTo(hDC, x+width, y);
  LineTo(hDC, x, y);
  SelectObject(hDC, hOldPen);
  DeleteObject(hPen);
}

void WScreen::BitBlt(int sx, int sy, int w, int h, int dx, int dy)
{
  ::BitBlt(hDC, dx, dy+yOffset, w, h, hDC, sx, sy+yOffset, SRCCOPY);
}

void WScreen::FreePixmap(long handle)
{
  PixmapInfo *info = (PixmapInfo *)handle;
  bitmaps.Delete(info->pixmap);
  DeleteObject(info->pixmap);
  if (info->mask != NULL)
    delete info->mask;
  delete info;
}

void WScreen::DrawPixmap(int xc, int yc, long handle)
{
  PixmapInfo *info = (PixmapInfo *)handle;
  HBITMAP hOld;
  if (info->mask != NULL) {
    for (int x = 0; x < info->w; ++x) for (int y = 0; y < info->h; ++y) {
      int c = info->mask[x+y*info->w];
      if (c != 0)
	SetPixel(hDC, xc+x, yc+y+yOffset, PALETTEINDEX(c));
    }
  }
  else {
    hOld = SelectObject(hMemDC, info->pixmap);
    ::BitBlt(hDC, xc, yc+yOffset, info->w, info->h, hMemDC, 0, 0, SRCCOPY);
    SelectObject(hMemDC, hOld);
  }
}

int WScreen::MainLoop()
{
  MSG msg;
  ShowWindow(hWnd, SW_SHOWNORMAL);
  while (GetMessage(&msg, NULL, NULL, NULL)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  WSACleanup();
  return msg.wParam;
}

long WScreen::CompilePixmap(char **pixmap)
{
  static BYTE color_map[256];
  int width, ht, ncolors, nchars, makeMask = 0;
  char **ptr = pixmap;
  sscanf(*ptr++, "%d %d %d %d", &width, &ht, &ncolors, &nchars);

  for (int i = 0; i < ncolors; ++i) {
    char name[80];
    char *line = *ptr++;
    int ch;
    ch = *line;
    ++line;
    while (isspace(*line)) ++line;
    ++line;
    while (isspace(*line)) ++line;
    for (char *str = name; *line != 0 && !isspace(*line); *str++ = *line++);
    *str = 0;
    if (strcmpi(name, "none") == 0) {
      makeMask = 1;
      color_map[ch] = 0;
    }
    else
      color_map[ch] = AllocColor(name);
  }
  char *mask = NULL;
  if (makeMask)
    mask = new char[width*ht];

  HBITMAP hbmp = CreateCompatibleBitmap(hDC, width, ht);
  HBITMAP hold = SelectObject(hMemDC, hbmp);
  char **startData = ptr;
  for (int y = 0; y < ht; ++y) {
    char *line = *ptr++;
    for (int x = 0; x < width; ++x)
      SetPixel(hMemDC, x, y, PALETTEINDEX(color_map[*line++]));
  }
  if (makeMask) {
    ptr = startData;
    for (int y = 0; y < ht; ++y) {
      char *line = *ptr++;
      for (int x = 0; x < width; ++x)
	mask[x+y*width] = color_map[*line++];
    }
  }

  PixmapInfo *info = new PixmapInfo;
  info->w = width;
  info->h = ht;
  info->pixmap = hbmp;
  info->mask = mask;
  SelectObject(hMemDC, hold);
  bitmaps.Insert(hbmp);

  return long(info);
}

void WScreen::GetPixmapInfo(long handle, int &w, int &h)
{
  PixmapInfo *info = (PixmapInfo *)handle;
  w = info->w;
  h = info->h;
}

void WScreen::StartTimer(int which, int milli, HandlerFunc func)
{
  if (which < 0 || which > 2) return;
  KillTimer(hWnd, which);
  Timers[which] = func;
  SetTimer(hWnd, which, milli, NULL);
}

void WScreen::StopTimer(int which)
{
  if (which < 0 || which > 2) return;
  KillTimer(hWnd, which);
}

static char *dataToSend;
static ushort dataSize;
static int sendSock;

void SendDataASync(int timerId)
{
  int n = send(sendSock, dataToSend, dataSize, 0);
  if (n == -1) n = 0;
  if (n >= dataSize) {
    screen->StopTimer(timerId);
    PostMessage(hWnd, WM_SENDQUEUE, DATA_SENT, (LPARAM)NULL);
    return;
  }
  dataToSend += n;
  dataSize -= n;
}

// routine to send data, if it fails it will startup a timer
// and keep trying, when it succeeds a WM_SENDQUEUE message will
// be posted with par DATA_SENT
int SendData(char *buf, ushort len)
{
  int n = send(sendSock, buf, len, 0);
  if (n == -1) n = 0;
  if (n >= len) {
    return 1; // sent all the bytes successfully
  }
  // couldn't send, schedule it
  dataToSend = buf+n;
  dataSize = len-n;
  screen->StartTimer(DATA_SEND_TIMER, 10, SendDataASync); // 10 ms timer
  return 0;
}

void ReadMessage(char *buf, ushort len, int fd, ReadHandler *rd, RecvInfo *rcv)
{
  Debug('n', "%d byte message on sock %d\n", len, fd);
  if (rcv->q == NULL) {
    rcv->q = new MsgQ;
    rcv->qLen = ntohs(*(ushort *)buf);
    delete buf;
  }
  else {
    *rcv->q << Msg(buf, len);
    --rcv->qLen;
    if (rcv->qLen <= 0) {
      Debug('n', "Got %d message queue from sock %d\n", rcv->q->Count(), fd);
      (*rd->func)(rcv->q, fd, rd->info);
      rcv->q = NULL;
    }
  }
}

void GotData(int fd, RecvInfo *rcv, ReadHandler *rd)
{
  if (!rd->q) { // doesn't wan't a queue so just tell him he got data
    (*rd->func)(NULL, fd, rd->info);
    return;
  }
  // ok, this guy wants the works
  if (rcv->stat == NOT_RECEIVING) {
    rcv->ptr = (char *)&rcv->len;
    rcv->left = sizeof(rcv->len);
    int n = recv(fd, rcv->ptr, rcv->left, 0);
    if (n == -1) return;
    rcv->stat = RECV_SIZE;
    rcv->left -= n;
    if (rcv->left > 0) {
      rcv->ptr += n;
      return;
    }
  }
  if (rcv->stat == RECV_SIZE) {
    if (rcv->left > 0) {
      int n = recv(fd, rcv->ptr, rcv->left, 0);
      if (n == -1) return;
      rcv->left -= n;
      if (rcv->left > 0) {
	rcv->ptr += n;
	return;
      }
    }
    rcv->len = ntohs(rcv->len); // convert to host order
    rcv->left = rcv->len;
    rcv->ptr = rcv->buf = new char[rcv->len];
    rcv->stat = RECV_MESG;
  }
  // getting message
  if (rcv->left > 0) {
    int n = recv(fd, rcv->ptr, rcv->left, 0);
    if (n == -1) return;
    rcv->left -= n;
    rcv->ptr += n;
  }
  if (rcv->left <= 0) {
    rcv->stat = NOT_RECEIVING;
    ReadMessage(rcv->buf, rcv->len, fd, rd, rcv);
  }
}

LONG FAR PASCAL WndProc(HWND hWnd, UINT wMessage, WPARAM wParam, LPARAM lParam)
{
  static MsgQ *currQ;
  static Msg currM;
  static int destroy;
  static int sendQstat = NOT_SENDING;
  static ushort sendLen;
  static char strBuf[256];

  HANDLE hInstance;
  PAINTSTRUCT ps;
  switch (wMessage) {
  case WM_CREATE:
    hInstance = GetWindowWord(hWnd, GWW_HINSTANCE);
    hMsgWin = CreateWindow("STATIC", "Initializing", WS_CHILD|SS_LEFT,
			      0, 0, 320, yOffset, hWnd, NULL, hInstance, NULL);
    ShowWindow(hMsgWin, SW_SHOWNORMAL);
    hDC = GetDC(hWnd);
    hMemDC = CreateCompatibleDC(hDC);
    LOGPALETTE *plogpal = (LOGPALETTE*)
      (new char[sizeof(LOGPALETTE)+128*sizeof(PALETTEENTRY)]);
    plogpal->palNumEntries = 128;
    plogpal->palVersion = 0x300;
    for (int i = 0; i < 128; ++i) {
      plogpal->palPalEntry[i].peRed = plogpal->palPalEntry[i].peGreen =
	plogpal->palPalEntry[i].peBlue = 0;
      plogpal->palPalEntry[i].peFlags = PC_RESERVED;
    }
    hPal = CreatePalette(plogpal);
    delete (char *)plogpal;
    SelectPalette(hDC, hPal, 0);
    SelectPalette(hMemDC, hPal, 0);
    hFont = CreateFont(8, 8, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
		       ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		       DEFAULT_QUALITY, VARIABLE_PITCH | FF_ROMAN,
		       NULL);
    SelectObject(hDC, hFont); 
    break;
  case WM_TIMER:
    (*screen->Timers[wParam])(wParam);
    break;
  case WM_SENDQUEUE:
    if (wParam == SEND_QUEUE) {
      // insert the new queue at the end of the list
      QStruct *qs = (QStruct *)lParam;
      if (qs != NULL) {
	sendQtail.Insert(qs);
	sendQtail.Next();
      }
      if (sendQstat != NOT_SENDING)
	break; // already sending, will send this one later
    }
    if (sendQstat == NOT_SENDING) { // send a queue
      if (sendQ.Empty()) break; // no Q's to send
      QStruct *qHead = sendQ.RemoveHead();
      currQ = qHead->q;
      destroy = qHead->destroy;
      sendSock = qHead->sock;
      if (destroy)
	Debug('n', "Doing destructive send of %d message queue to sock %d\n",
	  currQ->Count(), sendSock);
      else
	Debug('n', "Doing non destructive send of %d message queue to sock %d\n",
	  currQ->Count(), sendSock);
      delete qHead;
      if (sendQ.Empty()) // if it emptied, reset the tail
	sendQtail = sendQ;
      sendQstat = SENDING_QSIZE_SIZE;
    }
    if (sendQstat == SENDING_QSIZE_SIZE) {
      sendQstat = SENDING_QSIZE;
      sendLen = htons(sizeof(ushort));
      if (!SendData((char *)&sendLen, sizeof(sendLen)))
	return 0L;
    }
    if (sendQstat == SENDING_QSIZE) {
      sendQstat = SENDING_SIZE;
      sendLen = htons(currQ->Count());
      currQl = currQ->mesgs; // set to first message
      if (!SendData((char *)&sendLen, sizeof(sendLen)))
	return 0L;
    }
    while (currQl) {
      if (sendQstat == SENDING_SIZE) {
	currM = currQl.Elem();
	sendQstat = SENDING_MESSAGE;
	sendLen = htons(currM.len);
	// if SendData fails then break because it will return to us later
	// with the data sent
	if (!SendData((char *)&sendLen, sizeof(sendLen)))
          return 0L;
      }
      else { // sending message
	if (currM.len > 0) { // bytes left to send
	  int howMany = currM.len > 1024 ? 1024 : currM.len;
	  char *ptr = currM.buf;
	  currM.len -= howMany; // mark these as sent
	  currM.buf += howMany;
	  if (!SendData(ptr, howMany))
            return 0L; // will return when it succeeds
	}
	else { // sent this message
	  currQl.Next(); // go to next message
	  sendQstat = SENDING_SIZE;
	}
      } // end sending message
    } // end sending queue
    if (destroy) delete currQ;
    sendQstat = NOT_SENDING;
    if (sendQ) // if more Q's to send, post a message
      PostMessage(hWnd, WM_SENDQUEUE, SEND_QUEUE, (LPARAM)NULL);
    break;
  case WM_QUERYNEWPALETTE: {
    UINT i = RealizePalette(hDC);
    if (i > 0)
      InvalidateRect(hWnd, NULL, TRUE);
    return i;
  }
  case WM_PAINT:
    BeginPaint(hWnd, &ps);
    if (display != NULL) {
      display->Update();
    }
    EndPaint(hWnd, &ps);
    break; 
  case WM_KEYDOWN: {
    int key = wParam;
    if (key == 36) key = KEY_UPLEFT;
    else if (key == 38) key = KEY_UP;
    else if (key == 33) key = KEY_UPRIGHT;
    else if (key == 37) key = KEY_LEFT;
    else if (key == 39) key = KEY_RIGHT;
    else if (key == 35) key = KEY_DOWNLEFT;
    else if (key == 40) key = KEY_DOWN;
    else if (key == 34) key = KEY_DOWNRIGHT;
    else key = tolower(key);
    (*screen->KeyPressed)(KEYBOARD, key, 0, NULL);
    break;
  }
  case WM_LBUTTONUP:
    (*screen->KeyPressed)(MOUSE_LEFT, LOWORD(lParam),
    		          HIWORD(lParam)-yOffset, NULL);
    break;
  case WM_RBUTTONUP:
    (*screen->KeyPressed)(MOUSE_RIGHT, LOWORD(lParam),
			  HIWORD(lParam)-yOffset, NULL);
    break;
  case WM_COMMAND:
    if (wParam == LIST_BOX_ID && HIWORD(lParam) == LBN_SELCHANGE) {
      int n = (int)SendMessage(hList, LB_GETCURSEL, 0, 0L);
      SendMessage(hList, LB_GETTEXT, n, (DWORD)(LPSTR)strBuf);
      DestroyWindow(hList);
      (*screen->KeyPressed)(SELECT, 0, 0, strBuf);
    }
    break;
  case WM_DESTROY:
    DeleteObject(hPal);
    DeleteObject(hFont);
    DeleteDC(hMemDC);
    while (bitmaps)
      DeleteObject(bitmaps.RemoveHead());
    PostQuitMessage(0);
    break;
  case WM_DATAREADY:
    if (screen->readHandlers[wParam] != NULL)
      GotData(wParam, recvInfos[wParam], screen->readHandlers[wParam]);
    break;
  default:
      return DefWindowProc(hWnd, wMessage, wParam, lParam);
  }
  return 0L;
}

// schedule to send a message Q
void SendQ(int sock, MsgQ *q)
{
  PostMessage(hWnd, WM_SENDQUEUE, SEND_QUEUE, (LPARAM)new QStruct(q, 1, sock));
}

void NonDestructiveSendQ(int sock, MsgQ *q)
{
  PostMessage(hWnd, WM_SENDQUEUE, SEND_QUEUE, (LPARAM)new QStruct(q, 0, sock));
}

void WScreen::MessageBox(char *mesg)
{
  HANDLE hInstance = GetWindowWord(hWnd, GWW_HINSTANCE); 
  CreateWindow("CivMessage", "Civilization", WS_CHILD | WS_VISIBLE,
	       60, 65, 200, 70, hWnd, NULL, hInstance, mesg);
}

void WScreen::CreateChoiceFrame(char *mesg, List<charp> choices)
{
  HANDLE hInstance = GetWindowWord(hWnd, GWW_HINSTANCE);
  hList = CreateWindow("LISTBOX", mesg,
  		       WS_CHILD | WS_VISIBLE | WS_CAPTION | LBS_STANDARD,
		       60, 20, 200, 160, hWnd, LIST_BOX_ID, hInstance, NULL);
  SendMessage(hList, LB_RESETCONTENT, 0, 0L);
  for (Lister<charp> l = choices; l; l.Next())
    SendMessage(hList, LB_ADDSTRING, 0, (DWORD)(LPSTR)l.Elem());
}
