#include <Python.h>
#include <windows.h>
#include <mmsystem.h>


 /************************** DISPLAY PART ***************************/

typedef struct {
	BITMAPINFOHEADER bmiHeader;
	union {
		DWORD bmiMask[3];
		short bmiIndices[255];
	};
} screenbmp_t;

typedef struct {
  PyObject_HEAD
  HWND win;
  int width, height, bpp;
  HDC dc;
  HPALETTE hpal, hprevpal;
  screenbmp_t screenbmpinfo;
  unsigned char* screenbmpdata;
  unsigned char* screenfirstline;
  int screenscanline;   /* < 0 ! */
  PyObject* keyevents;
  PyObject* mouseevents;
  PyObject* motionevent;
} DisplayObject;

#define DisplayObject_Check(v)	((v)->ob_type == &Display_Type)
staticforward PyTypeObject Display_Type;


static void flush(DisplayObject* self)
{
  /*GdiFlush();*/
}

static void release_window_data(DisplayObject* self)
{
  if (self->hprevpal)
    {
      SelectPalette(self->dc, self->hprevpal, FALSE);
      self->hprevpal = (HPALETTE) NULL;
    }
  if (self->hpal)
    {
      DeleteObject(self->hpal);
      self->hpal = (HPALETTE) NULL;
    }
  if (self->dc && self->win)
    {
      ReleaseDC(self->win, self->dc);
      self->dc = (HDC) NULL;
    }
}

static void display_close(DisplayObject* self)
{
  release_window_data(self);
  if (self->win)
    {
      SetWindowLong(self->win, 0, 0);
      DestroyWindow(self->win);
      self->win = (HWND) NULL;
    }
}

static LRESULT CALLBACK display_proc(HWND hwnd, UINT uMsg,
                                     WPARAM wParam, LPARAM lParam)
{
  DisplayObject* self;
  switch (uMsg) {

  case WM_KEYDOWN:
  case WM_KEYUP:
    self = (DisplayObject*) GetWindowLong(hwnd, 0);
    if (self)
      {
        PyObject* v;
        int etype;
        if (self->keyevents == NULL)
          {
            self->keyevents = PyList_New(0);
            if (self->keyevents == NULL)
              break;
          }
        etype = (uMsg == WM_KEYDOWN) ? 2 : 3;
        v = Py_BuildValue("ii", (int) wParam, etype);
        if (v == NULL)
          break;
        PyList_Append(self->keyevents, v);
        Py_DECREF(v);
      }
    break;

  case WM_LBUTTONDOWN:
    self = (DisplayObject*) GetWindowLong(hwnd, 0);
    if (self)
      {
        PyObject* v;
        if (self->mouseevents == NULL)
          {
            self->mouseevents = PyList_New(0);
            if (self->mouseevents == NULL)
              break;
          }
        v = Py_BuildValue("ii", LOWORD(lParam), HIWORD(lParam));
        if (v == NULL)
          break;
        PyList_Append(self->mouseevents, v);
        Py_DECREF(v);
      }
    break;

  case WM_MOUSEMOVE:
    self = (DisplayObject*) GetWindowLong(hwnd, 0);
    if (self)
      {
        Py_XDECREF(self->motionevent);
        self->motionevent = Py_BuildValue("ii", LOWORD(lParam), HIWORD(lParam));
      }
    break;

  case WM_DESTROY:
    self = (DisplayObject*) GetWindowLong(hwnd, 0);
    if (self)
      {
        release_window_data(self);
        self->win = (HWND) NULL;
      }
    break;

  default:
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }

  return 0;
}

static PyObject* new_display(PyObject* dummy, PyObject* args)
{
  char* CLASSNAME = "winxshm";
  WNDCLASS wcls;
  DisplayObject* self;
  int width, height, bytes, bpp, use_shm=0;
  if (!PyArg_ParseTuple(args, "ii|i", &width, &height, &use_shm))
    return NULL;

  self = PyObject_New(DisplayObject, &Display_Type);
  if (self == NULL)
    return NULL;

  memset(&self->win, 0, ((char*)(self+1)) - ((char*)(&self->win)));
  self->width = width;
  self->height = height;

  /* set window class */
  memset(&wcls, 0, sizeof(wcls));
  wcls.style = CS_BYTEALIGNCLIENT;
  wcls.lpfnWndProc = &display_proc;
  wcls.cbWndExtra = sizeof(DisplayObject*);
  //wcls.hInstance = HINSTANCE;
  wcls.hCursor = LoadCursor(0, IDC_ARROW);
  wcls.lpszClassName = CLASSNAME;
  RegisterClass(&wcls);

  /* Create the window */
  self->win = CreateWindowEx(0, CLASSNAME, NULL,
                             WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
                             WS_MINIMIZEBOX | WS_VISIBLE,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             width + 2*GetSystemMetrics(SM_CXFIXEDFRAME), 
			     height + 2*GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION),
                             (HWND) NULL, (HMENU) NULL,
                             /*HINSTANCE*/ 0, (LPVOID) NULL);
  if (self->win == (HWND) NULL) goto err2;
  SetWindowLong(self->win, 0, (long) self);

  /* Create DC */
  self->dc = GetDC(self->win);
  if (!self->dc) goto err2;
  self->bpp = bpp = GetDeviceCaps(self->dc, BITSPIXEL);
  if (bpp == 8)
  {
	struct {
		WORD         palVersion; 
		WORD         palNumEntries; 
		PALETTEENTRY palPalEntry[255];
	} pal;
	pal.palNumEntries = GetSystemPaletteEntries(self->dc, 0, 255, pal.palPalEntry);
	if (pal.palNumEntries != 0)
	{
		int i;
		pal.palVersion = 0x300;
		self->hpal = CreatePalette((LOGPALETTE*)(&pal));
		self->screenbmpinfo.bmiHeader.biClrUsed = pal.palNumEntries;
		self->hprevpal = SelectPalette(self->dc, self->hpal, FALSE);
		for (i=0; i<pal.palNumEntries; i++) {
			self->screenbmpinfo.bmiIndices[i] = i;
		}
	}
  }
  if (bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32 && !self->hpal)
  {
	bpp = 24;   /* default */
	fprintf(stderr, "WARNING: a hi/true color screen mode of 15, 16, 24 or 32 bits per pixels\n");
	fprintf(stderr, "         is highly recommended !\n");
  }

  /* Allocate screen bitmaps */
  bytes = (bpp+7)/8;          /* per pixel */
  bytes = (bytes*width+3)&~3; /* per scan line */
  self->screenscanline = -bytes;
  bytes = bytes*height;       /* for the whole screen */
  self->screenbmpdata = PyMem_Malloc(bytes);
  self->screenfirstline = self->screenbmpdata + bytes + self->screenscanline;
  if (self->screenbmpdata == NULL)
  {
	PyErr_NoMemory();
	goto err2;
  }
  self->screenbmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  self->screenbmpinfo.bmiHeader.biWidth = self->width;
  self->screenbmpinfo.bmiHeader.biHeight = self->height;
  self->screenbmpinfo.bmiHeader.biPlanes = 1;
  self->screenbmpinfo.bmiHeader.biBitCount = (bpp+7)&~7;
  if (bpp == 16)
  {
	self->screenbmpinfo.bmiHeader.biCompression = BI_BITFIELDS;
	self->screenbmpinfo.bmiMask[0] = 0xF800;
	self->screenbmpinfo.bmiMask[1] = 0x07E0;
	self->screenbmpinfo.bmiMask[2] = 0x001F;
  }

  flush(self);
  return (PyObject*) self;

 err2:
  display_close(self);
  Py_DECREF(self);
  if (!PyErr_Occurred())
	PyErr_SetString(PyExc_IOError, "cannot open window");
  return NULL;
}

static void display_dealloc(DisplayObject* self)
{
  display_close(self);
  Py_XDECREF(self->keyevents);
  Py_XDECREF(self->mouseevents);
  Py_XDECREF(self->motionevent);
  PyMem_Free(self->screenbmpdata);
  PyObject_Del(self);
}

static PyObject* display_close1(DisplayObject* self, PyObject* args)
{
  display_close(self);
  Py_INCREF(Py_None);
  return Py_None;
}

static int checkopen(DisplayObject* self)
{
  if (self->win)
    return 1;
  //PyErr_SetString(PyExc_IOError, "the window was closed");
  PyErr_SetString(PyExc_SystemExit, "window closed.");
  return 0;
}

static PyObject* display_clear1(DisplayObject* self, PyObject* args)
{
  if (!checkopen(self))
    return NULL;
  memset(self->screenbmpdata, 0, (-self->screenscanline) * self->height);
  Py_INCREF(Py_None);
  return Py_None;
}

static void pack_pixel(DisplayObject* self, unsigned char *data, int r, int g, int b,
		       int depth)
{
  unsigned short pixel;
  switch( depth )
    {
    case 8:
      data[0] = GetNearestPaletteIndex(self->hpal, (b<<16) | (g<<8) | r);
      break;
    case 15:
      pixel = ((r<<7) & 0x7c00) | ((g<<2) & 0x03e0) | ((b>>3) & 0x001f);
      data[0] = (pixel) & 0xff;
      data[1] = (pixel>>8) & 0xff;
      break;
    case 16:
      /* assumes 5,6,5 model. */
      pixel = ((r<<8) & 0xf800) | ((g<<3) & 0x07e0) | ((b>>3) & 0x001f);
      data[0] = (pixel) & 0xff;
      data[1] = (pixel>>8) & 0xff;
      break;
    case 24:
      if( 1 )
	{
	  data[0] = b;
	  data[1] = g;
	  data[2] = r;
	  break;
	}
    case 32:
      *((long *)data) = (r<<16) | (g<<8) | b;
      break;
    }
}

static PyObject* display_pixmap1(DisplayObject* self, PyObject* args)
{
  int w,h;
  int length;
  unsigned char* input = NULL;
  long keycol = -1;

  if (!checkopen(self))
    return NULL;
  if (!PyArg_ParseTuple(args, "ii|s#l", &w, &h, &input, &length, &keycol))
    return NULL;

  if (1)     /* SHM */
    {
      int x, y;
      unsigned char *dst;
      int size, bytes_per_pixel;
      long packed_keycol = keycol;
      PyObject* result;
      PyObject* str;

      if (input == NULL )
        {
          Py_INCREF(Py_None);
          return Py_None;
        }

      bytes_per_pixel = self->screenbmpinfo.bmiHeader.biBitCount/8;
      size = bytes_per_pixel*w*h;

      if( 3*w*h != length )
	{
	  PyErr_SetString(PyExc_TypeError, "bad string length");
	  return NULL;
	}
      /* Create a new string and fill it with the correctly packed image */
      str = PyString_FromStringAndSize(NULL, size);
      if (!str)
        return NULL;
      if (keycol >= 0)
	switch( self->bpp )
	  {
	  case 8:
	    packed_keycol = 0xFF;
	    break;
	  case 15:
	    packed_keycol = (1 << 10) | (1 << 5) | 1;
	    break;
	  case 16:
	    packed_keycol = (1 << 11) | (1 << 5) | 1;
	    break;
	  default:
	    packed_keycol = keycol;
	    break;
	  }
      result = Py_BuildValue("iiOl", w, h, str, packed_keycol);
      Py_DECREF(str);  /* one ref left in 'result' */
      if (!result)
        return NULL;
      dst = (unsigned char*) PyString_AS_STRING(str);
      memset(dst,0,size);

      for( y=0; y<h; y++ )
	for( x=0; x<w; x++, input+=3, dst += bytes_per_pixel )
	  {
	    int r = input[0];
	    int g = input[1];
	    int b = input[2];
	    if( ((r<<16)|(g<<8)|b) == keycol )
	      for( b=0; b<bytes_per_pixel; b++ )
		dst[b] = ((unsigned char *)&packed_keycol)[b];
	    else
	      pack_pixel(self, dst, r, g, b, self->bpp);
	  }
      return result;
    }
}

static PyObject* display_putppm1(DisplayObject* self, PyObject* args)
{
  if (!checkopen(self))
    return NULL;

  if (1)     /* SHM */
    {
      int x,y,w,h,scanline;
      int clipx=0, clipy=0, clipw=65536, cliph=65536;
      unsigned char* src;
      int length;
      long keycol;
      int bytes_per_pixel = self->screenbmpinfo.bmiHeader.biBitCount/8;
      unsigned char* data = self->screenfirstline;
      if (!PyArg_ParseTuple(args, "ii(iis#l)|(iiii)",
                            &x, &y, &w, &h, &src, &length, &keycol,
                            &clipx, &clipy, &clipw, &cliph) || !data)
        return NULL;
  
      scanline = bytes_per_pixel*w;
      if (scanline*h != length)
        {
          PyErr_SetString(PyExc_TypeError, "bad string length");
          return NULL;
        }
      x -= clipx;
      y -= clipy;
      clipx += x;
      clipy += y;
      clipw += clipx;
      cliph += clipy;
      if (clipx<0) clipx=0;
      if (clipy<0) clipy=0;
      if (clipw>self->width) clipw=self->width;
      if (cliph>self->height) cliph=self->height;
      if (x<clipx) { src+=(clipx-x)*bytes_per_pixel; w+=x-clipx; x=clipx; }
      if (y<clipy) { src+=(clipy-y)*scanline; h+=y-clipy; y=clipy; }
      if (x+w > clipw) w = clipw-x;
      if (y+h > cliph) h = cliph-y;
      data += bytes_per_pixel*x+y*self->screenscanline;
      while (h>0)
        {
          int i;
	  int b;
          unsigned char* src0 = src;
	  unsigned char* data0 = data;
          if (keycol < 0)
            for (i=0; i<w; i++)
	      for (b=0; b<bytes_per_pixel; b++)
		*data++ = *src++;
          else
	    {
	      unsigned char *keycol_bytes = (unsigned char *)&keycol;
	      for (i=0; i<w; i++)
		{
		  int transparent = 1;
		  for( b=0; b<bytes_per_pixel; b++ )
		    transparent = transparent && (keycol_bytes[b] == src[b]);

		  if (!transparent)
		    for( b=0; b<bytes_per_pixel; b++ )
		      *data++ = *src++;
		  else
		    {
		      data += bytes_per_pixel;
		      src += bytes_per_pixel;
		    }
		}
	    }
          src = src0 + scanline;
          data = data0 + self->screenscanline;
          h--;
        }
    }
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject* display_getppm1(DisplayObject* self, PyObject* args)
{
  if (!checkopen(self))
    return NULL;

  if (1)     /* SHM */
    {
      int x,y,w,h,scanline;
      int clipx=0, clipy=0, clipw=self->width, cliph=self->height;
      unsigned char* dst;
      int length;
      PyObject* ignored;
      PyObject* result;
      PyObject* str;
      int bytes_per_pixel = self->screenbmpinfo.bmiHeader.biBitCount/8;
      unsigned char* data = self->screenfirstline;
      if (!PyArg_ParseTuple(args, "(iiii)|O", &x, &y, &w, &h,
                            &ignored) || !data)
        return NULL;

      scanline = bytes_per_pixel*w;
      length = scanline*h;
      str = PyString_FromStringAndSize(NULL, length);
      if (!str)
        return NULL;
      result = Py_BuildValue("iiOl", w, h, str, -1);
      Py_DECREF(str);  /* one ref left in 'result' */
      if (!result)
        return NULL;
      dst = (unsigned char*) PyString_AS_STRING(str);

      if (x<clipx) { dst+=(clipx-x)*bytes_per_pixel; w+=x-clipx; x=clipx; }
      if (y<clipy) { dst+=(clipy-y)*scanline; h+=y-clipy; y=clipy; }
      if (x+w > clipw) w = clipw-x;
      if (y+h > cliph) h = cliph-y;
      data += bytes_per_pixel*x+y*self->screenscanline;
      while (h>0)
        {
          int i;
	  int b;
          unsigned char* dst0 = dst;
	  unsigned char* data0 = data;
          for (i=0; i<w; i++)
            {
	      for( b=0; b<bytes_per_pixel; b++ )
		*dst++ = *data++;
            }
          dst = dst0 + scanline;
          data = data0 + self->screenscanline;
          h--;
        }
      return result;
    }
}

static int readXevents(DisplayObject* self)
{
  MSG Msg;
  while (PeekMessage(&Msg, (HWND) NULL, 0, 0, PM_REMOVE))
    {
      DispatchMessage(&Msg);
      if (PyErr_Occurred())
        return 0;
    }
  return checkopen(self);
}

#define ENABLE_EVENTS(mask)    do { } while (0)   /* nothing */

static PyObject* display_keyevents1(DisplayObject* self, PyObject* args)
{
  PyObject* result;
  ENABLE_EVENTS(KeyPressMask|KeyReleaseMask);
  if (!readXevents(self))
    return NULL;
  result = self->keyevents;
  if (result == NULL)
    result = PyList_New(0);
  else
    self->keyevents = NULL;
  return result;
}

static PyObject* display_mouseevents1(DisplayObject* self, PyObject* args)
{
  PyObject* result;
  ENABLE_EVENTS(ButtonPressMask);
  result = self->mouseevents;
  if (result == NULL)
    result = PyList_New(0);
  else
    self->mouseevents = NULL;
  return result;
}

static PyObject* display_pointermotion1(DisplayObject* self, PyObject* args)
{
  PyObject* result;
  ENABLE_EVENTS(PointerMotionMask);
  result = self->motionevent;
  if (result == NULL)
    {
      Py_INCREF(Py_None);
      result = Py_None;
    }
  else
    self->motionevent = NULL;
  return result;
}

static PyObject* display_flip1(DisplayObject* self, PyObject* args)
{
  int r;
  if (!checkopen(self))
    return NULL;

  if (self->hpal)
	  RealizePalette(self->dc);

  r = SetDIBitsToDevice(self->dc, 0, 0, self->width, self->height, 0, 0,
	  0, self->height, self->screenbmpdata, (BITMAPINFO*)(&self->screenbmpinfo),
	  DIB_PAL_COLORS);
  if (!r)
  {
	  PyErr_SetString(PyExc_IOError, "SetDIBitsToDevice failed");
	  return NULL;
  }

  flush(self);
  if (!readXevents(self))
    return NULL;
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject* display_shmmode(DisplayObject* self, PyObject *args)
{
  return PyInt_FromLong(0);
}

static PyObject* display_settitle(DisplayObject* self, PyObject* args)
{
  char* title;
  if (!checkopen(self))
    return NULL;
  if (!PyArg_ParseTuple(args, "s", &title))
    return NULL;
  SetWindowText(self->win, title);
  Py_INCREF(Py_None);
  return Py_None;
}

static PyMethodDef display_methods[] = {
  {"close",    (PyCFunction)display_close1,    METH_VARARGS,  NULL},
  {"flip",     (PyCFunction)display_flip1,     METH_VARARGS,  NULL},
  {"clear",    (PyCFunction)display_clear1,    METH_VARARGS,  NULL},
  {"pixmap",   (PyCFunction)display_pixmap1,   METH_VARARGS,  NULL},
  {"putppm",   (PyCFunction)display_putppm1,   METH_VARARGS,  NULL},
  {"getppm",   (PyCFunction)display_getppm1,   METH_VARARGS,  NULL},
  {"keyevents",(PyCFunction)display_keyevents1,METH_VARARGS,  NULL},
  {"mouseevents",(PyCFunction)display_mouseevents1,METH_VARARGS,NULL},
  {"pointermotion",(PyCFunction)display_pointermotion1,METH_VARARGS,NULL},
  {"shmmode",  (PyCFunction)display_shmmode,   METH_VARARGS,  NULL},
  {"settitle", (PyCFunction)display_settitle,  METH_VARARGS,  NULL},
  {NULL,		NULL}		/* sentinel */
};

static PyObject* display_getattr(DisplayObject* self, char* name)
{
  return Py_FindMethod(display_methods, (PyObject*)self, name);
}


statichere PyTypeObject Display_Type = {
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"Display",		/*tp_name*/
	sizeof(DisplayObject),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)display_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)display_getattr, /*tp_getattr*/
	0,			/*tp_setattr*/
	0,			/*tp_compare*/
	0,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
	0,			/*tp_call*/
};


 /************************** AUDIO PART ***************************/

#define NUM_WAVE_HDR    2

typedef struct {
	PyObject_HEAD
	HWAVEOUT waveOut;
	HANDLE doneEvent;
	WAVEHDR waveHdr[NUM_WAVE_HDR];
	int waveHdrCount, waveHdrNext;
} AudioObject;

#define AudioObject_Check(v)	((v)->ob_type == &Audio_Type)
staticforward PyTypeObject Audio_Type;


static PyObject* new_audio(PyObject* dummy, PyObject* args)
{
	WAVEFORMATEX wf;
	int channels, freq, bits, err, bufsize;
	AudioObject* self;
	if (!PyArg_ParseTuple(args, "iiii", &channels, &freq, &bits, &bufsize))
		return NULL;

	self = PyObject_New(AudioObject, &Audio_Type);
	if (self == NULL)
		return NULL;

	self->waveHdrCount = 0;
	self->waveHdrNext  = 0;
	self->waveOut      = 0;
	self->doneEvent    = CreateEvent(NULL, FALSE, TRUE, NULL);

	memset(&wf, 0, sizeof(wf));
	wf.wFormatTag      = WAVE_FORMAT_PCM;
	wf.nChannels       = channels;
	wf.nSamplesPerSec  = freq;
	wf.wBitsPerSample  = bits;
	wf.nBlockAlign     = wf.nChannels * wf.wBitsPerSample / 8;
	wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
	err = waveOutOpen(&self->waveOut, WAVE_MAPPER, &wf, (DWORD) self->doneEvent, 0, CALLBACK_EVENT);
	if (err != MMSYSERR_NOERROR || self->doneEvent == NULL) {
		Py_DECREF(self);
		PyErr_SetString(PyExc_IOError, "cannot open audio device");
		return NULL;
	}

	while (self->waveHdrCount < NUM_WAVE_HDR) {
		WAVEHDR* wh = &self->waveHdr[self->waveHdrCount];
		wh->lpData         = PyMem_Malloc(bufsize);
		wh->dwBufferLength = bufsize;
		wh->dwFlags        = 0;
		if (wh->lpData == NULL || 
		    waveOutPrepareHeader(self->waveOut, wh, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) {
			Py_DECREF(self);
			return NULL;
		}
		wh->dwFlags |= WHDR_DONE;
		self->waveHdrCount++;
	}
	return (PyObject*) self;
}

static void audio_close(AudioObject* self)
{
	if (self->waveOut != 0) {
		waveOutReset(self->waveOut);
		while (self->waveHdrCount > 0) {
			WAVEHDR* wh = &self->waveHdr[--self->waveHdrCount];
			waveOutUnprepareHeader(self->waveOut, wh, sizeof(WAVEHDR));
			PyMem_Free(wh->lpData);
		}
		waveOutClose(self->waveOut);
		self->waveOut = 0;
	}
}

static void audio_dealloc(AudioObject* self)
{
	audio_close(self);
	PyObject_Del(self);
}

static PyObject* audio_wait1(AudioObject* self, PyObject* args)
{
	float delay = -1.0;
	if (!PyArg_ParseTuple(args, "|f", &delay))
		return NULL;
	if (self->waveHdrNext >= self->waveHdrCount) {
		PyErr_SetString(PyExc_IOError, "audio device not ready");
		return NULL;
	}
	Py_BEGIN_ALLOW_THREADS
	while (!(self->waveHdr[self->waveHdrNext].dwFlags & WHDR_DONE)) {
		WaitForSingleObject(self->doneEvent, delay<0.0?INFINITE:(DWORD)(delay*1000.0));
	}
	Py_END_ALLOW_THREADS
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* audio_ready1(AudioObject* self, PyObject* args)
{
	if (!PyArg_ParseTuple(args, ""))
		return NULL;
	return PyInt_FromLong(self->waveHdrNext < self->waveHdrCount &&
			      (self->waveHdr[self->waveHdrNext].dwFlags & WHDR_DONE));
}

static PyObject* audio_write1(AudioObject* self, PyObject* args)
{
	WAVEHDR* wh;
	char* buffer;
	int bufsize;
	if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize))
		return NULL;
	if (self->waveHdrNext >= self->waveHdrCount) {
		PyErr_SetString(PyExc_IOError, "audio device not ready");
		return NULL;
	}
	wh = &self->waveHdr[self->waveHdrNext];
	if (!(wh->dwFlags & WHDR_DONE)) {
		PyErr_SetString(PyExc_IOError, "audio device would block");
		return NULL;
	}
	if ((DWORD) bufsize != wh->dwBufferLength) {
		PyErr_SetString(PyExc_ValueError, "bufsize mismatch");
		return NULL;
	}
	wh->dwFlags &= ~WHDR_DONE;
	memcpy(wh->lpData, buffer, bufsize);
	if (waveOutWrite(self->waveOut, wh, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) {
		PyErr_SetString(PyExc_IOError, "audio device write error");
		return NULL;
	}
	self->waveHdrNext++;
	if (self->waveHdrNext >= self->waveHdrCount)
		self->waveHdrNext = 0;

	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject* audio_close1(AudioObject* self, PyObject* args)
{
	if (!PyArg_ParseTuple(args, ""))
		return NULL;
	audio_close(self);
	Py_INCREF(Py_None);
	return Py_None;
}


static PyMethodDef audio_methods[] = {
  {"ready",    (PyCFunction)audio_ready1,      METH_VARARGS,  NULL},
  {"wait",     (PyCFunction)audio_wait1,       METH_VARARGS,  NULL},
  {"write",    (PyCFunction)audio_write1,      METH_VARARGS,  NULL},
  {"close",    (PyCFunction)audio_close1,      METH_VARARGS,  NULL},
  {NULL,		NULL}		/* sentinel */
};

static PyObject* audio_getattr(AudioObject* self, char* name)
{
  return Py_FindMethod(audio_methods, (PyObject*)self, name);
}


statichere PyTypeObject Audio_Type = {
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"Audio",		/*tp_name*/
	sizeof(AudioObject),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)audio_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)audio_getattr, /*tp_getattr*/
	0,			/*tp_setattr*/
	0,			/*tp_compare*/
	0,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
	0,			/*tp_call*/
};


static PyMethodDef WinMethods[] = {
           {"Display",  new_display,  METH_VARARGS},
           {"Audio",    new_audio,  METH_VARARGS},
           {NULL,       NULL}         /* Sentinel */
       };

void initwingame(void)
{
  Display_Type.ob_type = &PyType_Type;
  Audio_Type.ob_type = &PyType_Type;
  Py_InitModule("wingame", WinMethods);
}
