/*
 * Windows (Win32/Win64) device driver for Mesa
 *
 */

#include "wmesadef.h"
#include "colors.h"
#include "GL/wmesa.h"
#include <winuser.h>
#include "main/context.h"
#include "main/extensions.h"
#include "main/framebuffer.h"
#include "main/renderbuffer.h"
#include "main/macros.h"
#include "drivers/common/driverfuncs.h"
#include "drivers/common/meta.h"
#include "vbo/vbo.h"
#include "swrast/swrast.h"
#include "swrast/s_renderbuffer.h"
#include "swrast_setup/swrast_setup.h"
#include "tnl/tnl.h"
#include "tnl/t_context.h"
#include "tnl/t_pipeline.h"


/* linked list of our Framebuffers (windows) */
static WMesaFramebuffer FirstFramebuffer = NULL;


/**
 * Create a new WMesaFramebuffer object which will correspond to the
 * given HDC (Window handle).
 */
static WMesaFramebuffer
wmesa_new_framebuffer(HDC hdc, struct gl_config *visual)
{
    WMesaFramebuffer pwfb
        = (WMesaFramebuffer) malloc(sizeof(struct wmesa_framebuffer));
    if (pwfb) {
        _mesa_initialize_window_framebuffer(&pwfb->Base, visual);
        pwfb->hDC = hdc;
        /* insert at head of list */
        pwfb->next = FirstFramebuffer;
        FirstFramebuffer = pwfb;
    }
    return pwfb;
}

/**
 * Given an hdc, free the corresponding WMesaFramebuffer
 */
static void
wmesa_free_framebuffer(HDC hdc)
{
    WMesaFramebuffer pwfb, prev;
    for (pwfb = FirstFramebuffer; pwfb; pwfb = pwfb->next) {
        if (pwfb->hDC == hdc)
            break;
	prev = pwfb;
    }
    if (pwfb) {
        struct gl_framebuffer *fb;
	if (pwfb == FirstFramebuffer)
	    FirstFramebuffer = pwfb->next;
	else
	    prev->next = pwfb->next;
        fb = &pwfb->Base;
        _mesa_reference_framebuffer(&fb, NULL); 
    }
}

/**
 * Given an hdc, return the corresponding WMesaFramebuffer
 */
static WMesaFramebuffer
wmesa_lookup_framebuffer(HDC hdc)
{
    WMesaFramebuffer pwfb;
    for (pwfb = FirstFramebuffer; pwfb; pwfb = pwfb->next) {
        if (pwfb->hDC == hdc)
            return pwfb;
    }
    return NULL;
}


/**
 * Given a struct gl_framebuffer, return the corresponding WMesaFramebuffer.
 */
static WMesaFramebuffer wmesa_framebuffer(struct gl_framebuffer *fb)
{
    return (WMesaFramebuffer) fb;
}


/**
 * Given a struct gl_context, return the corresponding WMesaContext.
 */
static WMesaContext wmesa_context(const struct gl_context *ctx)
{
    return (WMesaContext) ctx;
}


/*
 * Every driver should implement a GetString function in order to
 * return a meaningful GL_RENDERER string.
 */
static const GLubyte *wmesa_get_string(struct gl_context *ctx, GLenum name)
{
    return (name == GL_RENDERER) ? 
	(GLubyte *) "Mesa Windows GDI Driver" : NULL;
}


/*
 * Determine the pixel format based on the pixel size.
 */
static void wmSetPixelFormat(WMesaFramebuffer pwfb, HDC hDC)
{
    pwfb->cColorBits = GetDeviceCaps(hDC, BITSPIXEL);

    /* Only 16 and 32 bit targets are supported now */
    assert(pwfb->cColorBits == 0 ||
	   pwfb->cColorBits == 16 || 
	   pwfb->cColorBits == 24 || 
	   pwfb->cColorBits == 32);

    switch(pwfb->cColorBits){
    case 8:
	pwfb->pixelformat = PF_INDEX8;
	break;
    case 16:
	pwfb->pixelformat = PF_5R6G5B;
	break;
    case 24:
    case 32:
	pwfb->pixelformat = PF_8R8G8B;
	break;
    default:
	pwfb->pixelformat = PF_BADFORMAT;
    }
}


/**
 * Create DIB for back buffer.
 * We write into this memory with the span routines and then blit it
 * to the window on a buffer swap.
 */
static BOOL wmCreateBackingStore(WMesaFramebuffer pwfb, long lxSize, long lySize)
{
    LPBITMAPINFO pbmi = &(pwfb->bmi);
    HDC          hic;

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = lxSize;
    pbmi->bmiHeader.biHeight= -lySize;
    pbmi->bmiHeader.biPlanes = 1;
    pbmi->bmiHeader.biBitCount = GetDeviceCaps(pwfb->hDC, BITSPIXEL);
    pbmi->bmiHeader.biCompression = BI_RGB;
    pbmi->bmiHeader.biSizeImage = 0;
    pbmi->bmiHeader.biXPelsPerMeter = 0;
    pbmi->bmiHeader.biYPelsPerMeter = 0;
    pbmi->bmiHeader.biClrUsed = 0;
    pbmi->bmiHeader.biClrImportant = 0;
    
    pwfb->cColorBits = pbmi->bmiHeader.biBitCount;
    pwfb->ScanWidth = (lxSize * (pwfb->cColorBits / 8) + 3) & ~3;
    
    hic = CreateIC("display", NULL, NULL, NULL);
    pwfb->dib_hDC = CreateCompatibleDC(hic);
    
    pwfb->hbmDIB = CreateDIBSection(hic,
				   &pwfb->bmi,
				   DIB_RGB_COLORS,
				   (void **)&(pwfb->pbPixels),
				   0,
				   0);
    pwfb->hOldBitmap = SelectObject(pwfb->dib_hDC, pwfb->hbmDIB);
    
    DeleteDC(hic);

    wmSetPixelFormat(pwfb, pwfb->hDC);
    return TRUE;
}


static void wmDeleteBackingStore(WMesaFramebuffer pwfb)
{
    if (pwfb->hbmDIB) {
	SelectObject(pwfb->dib_hDC, pwfb->hOldBitmap);
	DeleteDC(pwfb->dib_hDC);
	DeleteObject(pwfb->hbmDIB);
    }
}


/**
 * Find the width and height of the window named by hdc.
 */
static void
get_window_size(HDC hdc, GLuint *width, GLuint *height)
{
    if (WindowFromDC(hdc)) {
        RECT rect;
        GetClientRect(WindowFromDC(hdc), &rect);
        *width = rect.right - rect.left;
        *height = rect.bottom - rect.top;
    }
    else { /* Memory context */
        /* From contributed code - use the size of the desktop
         * for the size of a memory context (?) */
        *width = GetDeviceCaps(hdc, HORZRES);
        *height = GetDeviceCaps(hdc, VERTRES);
    }
}


static void
wmesa_get_buffer_size(struct gl_framebuffer *buffer, GLuint *width, GLuint *height)
{
    WMesaFramebuffer pwfb = wmesa_framebuffer(buffer);
    get_window_size(pwfb->hDC, width, height);
}


static void wmesa_flush(struct gl_context *ctx)
{
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->WinSysDrawBuffer);

    if (ctx->Visual.doubleBufferMode == 1) {
	BitBlt(pwfb->hDC, 0, 0, pwfb->Base.Width, pwfb->Base.Height,
	       pwfb->dib_hDC, 0, 0, SRCCOPY);
    }
    else {
	/* Do nothing for single buffer */
    }
}


/**********************************************************************/
/*****                   CLEAR Functions                          *****/
/**********************************************************************/

/* 
 * Clear the color/depth/stencil buffers.
 */ 
static void clear(struct gl_context *ctx, GLbitfield mask)
{
#define FLIP(Y)  (ctx->DrawBuffer->Height - (Y) - 1)
    const GLint x = ctx->DrawBuffer->_Xmin;
    const GLint y = ctx->DrawBuffer->_Ymin;
    const GLint height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
    const GLint width  = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;

    WMesaContext pwc = wmesa_context(ctx);
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    int done = 0;

    /* Let swrast do all the work if the masks are not set to
     * clear all channels. */
    if (!ctx->Color.ColorMask[0][0] ||
	!ctx->Color.ColorMask[0][1] ||
	!ctx->Color.ColorMask[0][2] ||
	!ctx->Color.ColorMask[0][3]) {
	_swrast_Clear(ctx, mask);
	return;
    }

    if (mask & BUFFER_BITS_COLOR) {
       /* setup the clearing color */
       const union gl_color_union color = ctx->Color.ClearColor;
       GLubyte col[3];
       UNCLAMPED_FLOAT_TO_UBYTE(col[0], color.f[0]);
       UNCLAMPED_FLOAT_TO_UBYTE(col[1], color.f[1]);
       UNCLAMPED_FLOAT_TO_UBYTE(col[2], color.f[2]);
       pwc->clearColorRef = RGB(col[0], col[1], col[2]);
       DeleteObject(pwc->clearPen);
       DeleteObject(pwc->clearBrush);
       pwc->clearPen = CreatePen(PS_SOLID, 1, pwc->clearColorRef); 
       pwc->clearBrush = CreateSolidBrush(pwc->clearColorRef); 
    }

    /* Back buffer */
    if (mask & BUFFER_BIT_BACK_LEFT) { 
	
	int     i, rowSize; 
	UINT    bytesPerPixel = pwfb->cColorBits / 8; 
	LPBYTE  lpb, clearRow;
	LPWORD  lpw;
	BYTE    bColor; 
	WORD    wColor; 
	BYTE    r, g, b; 
	DWORD   dwColor; 
	LPDWORD lpdw; 
	
	/* Try for a fast clear - clearing entire buffer with a single
	 * byte value. */
	if (width == ctx->DrawBuffer->Width &&
            height == ctx->DrawBuffer->Height) { /* entire buffer */
	    /* Now check for an easy clear value */
	    switch (bytesPerPixel) {
	    case 1:
		bColor = BGR8(GetRValue(pwc->clearColorRef), 
			      GetGValue(pwc->clearColorRef), 
			      GetBValue(pwc->clearColorRef));
		memset(pwfb->pbPixels, bColor, 
		       pwfb->ScanWidth * height);
		done = 1;
		break;
	    case 2:
		wColor = BGR16(GetRValue(pwc->clearColorRef), 
			       GetGValue(pwc->clearColorRef), 
			       GetBValue(pwc->clearColorRef)); 
		if (((wColor >> 8) & 0xff) == (wColor & 0xff)) {
		    memset(pwfb->pbPixels, wColor & 0xff, 
			   pwfb->ScanWidth * height);
		    done = 1;
		}
		break;
	    case 3:
		/* fall through */
	    case 4:
		if (GetRValue(pwc->clearColorRef) == 
		    GetGValue(pwc->clearColorRef) &&
		    GetRValue(pwc->clearColorRef) == 
		    GetBValue(pwc->clearColorRef)) {
		    memset(pwfb->pbPixels, 
			   GetRValue(pwc->clearColorRef), 
			   pwfb->ScanWidth * height);
		    done = 1;
		}
		break;
	    default:
		break;
	    }
	} /* all */

	if (!done) {
	    /* Need to clear a row at a time.  Begin by setting the first
	     * row in the area to be cleared to the clear color. */
	    
	    clearRow = pwfb->pbPixels + 
		pwfb->ScanWidth * FLIP(y) +
		bytesPerPixel * x; 
	    switch (bytesPerPixel) {
	    case 1:
		lpb = clearRow;
		bColor = BGR8(GetRValue(pwc->clearColorRef), 
			      GetGValue(pwc->clearColorRef), 
			      GetBValue(pwc->clearColorRef));
		memset(lpb, bColor, width);
		break;
	    case 2:
		lpw = (LPWORD)clearRow;
		wColor = BGR16(GetRValue(pwc->clearColorRef), 
			       GetGValue(pwc->clearColorRef), 
			       GetBValue(pwc->clearColorRef)); 
		for (i=0; i<width; i++)
		    *lpw++ = wColor;
		break;
	    case 3: 
		lpb = clearRow;
		r = GetRValue(pwc->clearColorRef); 
		g = GetGValue(pwc->clearColorRef); 
		b = GetBValue(pwc->clearColorRef); 
		for (i=0; i<width; i++) {
		    *lpb++ = b; 
		    *lpb++ = g; 
		    *lpb++ = r; 
		} 
		break;
	    case 4: 
		lpdw = (LPDWORD)clearRow; 
		dwColor = BGR32(GetRValue(pwc->clearColorRef), 
				GetGValue(pwc->clearColorRef), 
				GetBValue(pwc->clearColorRef)); 
		for (i=0; i<width; i++)
		    *lpdw++ = dwColor;
		break;
	    default:
		break;
	    } /* switch */
	    
	    /* copy cleared row to other rows in buffer */
	    lpb = clearRow - pwfb->ScanWidth;
	    rowSize = width * bytesPerPixel;
	    for (i=1; i<height; i++) { 
		memcpy(lpb, clearRow, rowSize); 
		lpb -= pwfb->ScanWidth; 
	    } 
	} /* not done */
	mask &= ~BUFFER_BIT_BACK_LEFT;
    } /* back buffer */ 

    /* front buffer */
    if (mask & BUFFER_BIT_FRONT_LEFT) { 
	HDC DC = pwc->hDC; 
	HPEN Old_Pen = SelectObject(DC, pwc->clearPen); 
	HBRUSH Old_Brush = SelectObject(DC, pwc->clearBrush);
	Rectangle(DC,
		  x,
		  FLIP(y) + 1,
		  x + width + 1,
		  FLIP(y) - height + 1);
	SelectObject(DC, Old_Pen); 
	SelectObject(DC, Old_Brush); 
	mask &= ~BUFFER_BIT_FRONT_LEFT;
    } /* front buffer */ 
    
    /* Call swrast if there is anything left to clear (like DEPTH) */ 
    if (mask) 
	_swrast_Clear(ctx, mask);
  
#undef FLIP
} 


/**********************************************************************/
/*****                   PIXEL Functions                          *****/
/**********************************************************************/

#define FLIP(Y)  (rb->Height - (Y) - 1)


/**
 ** Front Buffer reading/writing
 ** These are slow, but work with all non-indexed visual types.
 **/

/* Write a horizontal span of RGBA color pixels with a boolean mask. */
static void write_rgba_span_front(struct gl_context *ctx, 
				  struct gl_renderbuffer *rb, 
				  GLuint n, GLint x, GLint y,
				  const void *values, 
				  const GLubyte *mask)
{
   const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
   WMesaContext pwc = wmesa_context(ctx);
   WMesaFramebuffer pwfb = wmesa_lookup_framebuffer(pwc->hDC);
   HBITMAP bmp=0;
   HDC mdc=0;
   typedef union
   {
      unsigned i;
      struct {
         unsigned b:8, g:8, r:8, a:8;
      };
   } BGRA;
   BGRA *bgra, c;
   GLuint i;

   if (n < 16) {   // the value 16 is just guessed
      y=FLIP(y);
      if (mask) {
         for (i=0; i<n; i++)
            if (mask[i])
               SetPixel(pwc->hDC, x+i, y,
                        RGB(rgba[i][RCOMP], rgba[i][GCOMP], rgba[i][BCOMP]));
      }
      else {
         for (i=0; i<n; i++)
            SetPixel(pwc->hDC, x+i, y,
                     RGB(rgba[i][RCOMP], rgba[i][GCOMP], rgba[i][BCOMP]));
      }
   }
   else {
      if (!pwfb) {
         _mesa_problem(NULL, "wmesa: write_rgba_span_front on unknown hdc");
         return;
      }
      bgra=malloc(n*sizeof(BGRA));
      if (!bgra) {
         _mesa_problem(NULL, "wmesa: write_rgba_span_front: out of memory");
         return;
      }
      c.a=0;
      if (mask) {
         for (i=0; i<n; i++) {
            if (mask[i]) {
               c.r=rgba[i][RCOMP];
               c.g=rgba[i][GCOMP];
               c.b=rgba[i][BCOMP];
               c.a=rgba[i][ACOMP];
               bgra[i]=c;
            }
            else
               bgra[i].i=0;
         }
      }
      else {
         for (i=0; i<n; i++) {
            c.r=rgba[i][RCOMP];
            c.g=rgba[i][GCOMP];
            c.b=rgba[i][BCOMP];
            c.a=rgba[i][ACOMP];
            bgra[i]=c;
         }
      }
      bmp=CreateBitmap(n, 1,  1, 32, bgra);
      mdc=CreateCompatibleDC(pwfb->hDC);
      SelectObject(mdc, bmp);
      y=FLIP(y);
      BitBlt(pwfb->hDC, x, y, n, 1, mdc, 0, 0, SRCCOPY);
      SelectObject(mdc, 0);
      DeleteObject(bmp);
      DeleteDC(mdc);
      free(bgra);
   }
}


/* Write an array of RGBA pixels with a boolean mask. */
static void write_rgba_pixels_front(struct gl_context *ctx, 
                                    struct gl_renderbuffer *rb,
                                    GLuint n, 
                                    const GLint x[], const GLint y[],
                                    const void *values, 
                                    const GLubyte *mask)
{
    const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
    GLuint i;
    WMesaContext pwc = wmesa_context(ctx);
    (void) ctx;
    for (i=0; i<n; i++)
	if (mask[i])
	    SetPixel(pwc->hDC, x[i], FLIP(y[i]), 
		     RGB(rgba[i][RCOMP], rgba[i][GCOMP], 
			 rgba[i][BCOMP]));
}



/* Read a horizontal span of color pixels. */
static void read_rgba_span_front(struct gl_context *ctx, 
                                 struct gl_renderbuffer *rb,
                                 GLuint n, GLint x, GLint y,
                                 void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    WMesaContext pwc = wmesa_context(ctx);
    GLuint i;
    COLORREF Color;
    y = FLIP(y);
    for (i=0; i<n; i++) {
	Color = GetPixel(pwc->hDC, x+i, y);
	rgba[i][RCOMP] = GetRValue(Color);
	rgba[i][GCOMP] = GetGValue(Color);
	rgba[i][BCOMP] = GetBValue(Color);
	rgba[i][ACOMP] = 255;
    }
}


/* Read an array of color pixels. */
static void read_rgba_pixels_front(struct gl_context *ctx, 
                                   struct gl_renderbuffer *rb,
                                   GLuint n, const GLint x[], const GLint y[],
                                   void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    WMesaContext pwc = wmesa_context(ctx);
    GLuint i;
    COLORREF Color;
    for (i=0; i<n; i++) {
        GLint y2 = FLIP(y[i]);
        Color = GetPixel(pwc->hDC, x[i], y2);
        rgba[i][RCOMP] = GetRValue(Color);
        rgba[i][GCOMP] = GetGValue(Color);
        rgba[i][BCOMP] = GetBValue(Color);
        rgba[i][ACOMP] = 255;
    }
}

/*********************************************************************/

/* DOUBLE BUFFER 32-bit */

#define WMSETPIXEL32(pwc, y, x, r, g, b) { \
LPDWORD lpdw = ((LPDWORD)((pwc)->pbPixels + (pwc)->ScanWidth * (y)) + (x)); \
*lpdw = BGR32((r),(g),(b)); }



/* Write a horizontal span of RGBA color pixels with a boolean mask. */
static void write_rgba_span_32(struct gl_context *ctx, 
			       struct gl_renderbuffer *rb, 
			       GLuint n, GLint x, GLint y,
			       const void *values, 
			       const GLubyte *mask)
{
    const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    GLuint i;
    LPDWORD lpdw;

    (void) ctx;
    
    y=FLIP(y);
    lpdw = ((LPDWORD)(pwfb->pbPixels + pwfb->ScanWidth * y)) + x;
    if (mask) {
	for (i=0; i<n; i++)
	    if (mask[i])
                lpdw[i] = BGR32(rgba[i][RCOMP], rgba[i][GCOMP], 
				rgba[i][BCOMP]);
    }
    else {
	for (i=0; i<n; i++)
                *lpdw++ = BGR32(rgba[i][RCOMP], rgba[i][GCOMP], 
				rgba[i][BCOMP]);
    }
}


/* Write an array of RGBA pixels with a boolean mask. */
static void write_rgba_pixels_32(struct gl_context *ctx, 
				 struct gl_renderbuffer *rb,
				 GLuint n, const GLint x[], const GLint y[],
				 const void *values, 
				 const GLubyte *mask)
{
    const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
    GLuint i;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    for (i=0; i<n; i++)
	if (mask[i])
	    WMSETPIXEL32(pwfb, FLIP(y[i]), x[i],
			 rgba[i][RCOMP], rgba[i][GCOMP], rgba[i][BCOMP]);
}


/* Read a horizontal span of color pixels. */
static void read_rgba_span_32(struct gl_context *ctx, 
			      struct gl_renderbuffer *rb,
			      GLuint n, GLint x, GLint y,
			      void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    GLuint i;
    DWORD pixel;
    LPDWORD lpdw;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    
    y = FLIP(y);
    lpdw = ((LPDWORD)(pwfb->pbPixels + pwfb->ScanWidth * y)) + x;
    for (i=0; i<n; i++) {
	pixel = lpdw[i];
	rgba[i][RCOMP] = (GLubyte)((pixel & 0x00ff0000) >> 16);
	rgba[i][GCOMP] = (GLubyte)((pixel & 0x0000ff00) >> 8);
	rgba[i][BCOMP] = (GLubyte)(pixel & 0x000000ff);
	rgba[i][ACOMP] = 255;
    }
}


/* Read an array of color pixels. */
static void read_rgba_pixels_32(struct gl_context *ctx, 
				struct gl_renderbuffer *rb,
				GLuint n, const GLint x[], const GLint y[],
				void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    GLuint i;
    DWORD pixel;
    LPDWORD lpdw;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);

    for (i=0; i<n; i++) {
	GLint y2 = FLIP(y[i]);
	lpdw = ((LPDWORD)(pwfb->pbPixels + pwfb->ScanWidth * y2)) + x[i];
	pixel = *lpdw;
	rgba[i][RCOMP] = (GLubyte)((pixel & 0x00ff0000) >> 16);
	rgba[i][GCOMP] = (GLubyte)((pixel & 0x0000ff00) >> 8);
	rgba[i][BCOMP] = (GLubyte)(pixel & 0x000000ff);
	rgba[i][ACOMP] = 255;
  }
}


/*********************************************************************/

/* DOUBLE BUFFER 24-bit */

#define WMSETPIXEL24(pwc, y, x, r, g, b) { \
LPBYTE lpb = ((LPBYTE)((pwc)->pbPixels + (pwc)->ScanWidth * (y)) + (3 * x)); \
lpb[0] = (b); \
lpb[1] = (g); \
lpb[2] = (r); }

/* Write a horizontal span of RGBA color pixels with a boolean mask. */
static void write_rgba_span_24(struct gl_context *ctx, 
			       struct gl_renderbuffer *rb, 
			       GLuint n, GLint x, GLint y,
			       const void *values, 
			       const GLubyte *mask)
{
    const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    GLuint i;
    LPBYTE lpb;

    (void) ctx;
    
    y=FLIP(y);
    lpb = ((LPBYTE)(pwfb->pbPixels + pwfb->ScanWidth * y)) + (3 * x);
    if (mask) {
	for (i=0; i<n; i++)
	    if (mask[i]) {
                lpb[3*i] = rgba[i][BCOMP];
                lpb[3*i+1] = rgba[i][GCOMP];
                lpb[3*i+2] = rgba[i][RCOMP];
	    }
    }
    else {
	    for (i=0; i<n; i++) {
            *lpb++ = rgba[i][BCOMP];
            *lpb++ = rgba[i][GCOMP];
            *lpb++ = rgba[i][RCOMP];
	    }
    }
}


/* Write an array of RGBA pixels with a boolean mask. */
static void write_rgba_pixels_24(struct gl_context *ctx, 
				 struct gl_renderbuffer *rb,
				 GLuint n, const GLint x[], const GLint y[],
				 const void *values, 
				 const GLubyte *mask)
{
    const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
    GLuint i;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    for (i=0; i<n; i++)
	if (mask[i])
	    WMSETPIXEL24(pwfb, FLIP(y[i]), x[i],
			 rgba[i][RCOMP], rgba[i][GCOMP], rgba[i][BCOMP]);
}


/* Read a horizontal span of color pixels. */
static void read_rgba_span_24(struct gl_context *ctx, 
			      struct gl_renderbuffer *rb,
			      GLuint n, GLint x, GLint y,
			      void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    GLuint i;
    LPBYTE lpb;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    
    y = FLIP(y);
    lpb = ((LPBYTE)(pwfb->pbPixels + pwfb->ScanWidth * y)) + (3 * x);
    for (i=0; i<n; i++) {
	rgba[i][RCOMP] = lpb[3*i+2];
	rgba[i][GCOMP] = lpb[3*i+1];
	rgba[i][BCOMP] = lpb[3*i];
	rgba[i][ACOMP] = 255;
    }
}


/* Read an array of color pixels. */
static void read_rgba_pixels_24(struct gl_context *ctx, 
				struct gl_renderbuffer *rb,
				GLuint n, const GLint x[], const GLint y[],
				void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    GLuint i;
    LPBYTE lpb;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);

    for (i=0; i<n; i++) {
	GLint y2 = FLIP(y[i]);
	lpb = ((LPBYTE)(pwfb->pbPixels + pwfb->ScanWidth * y2)) + (3 * x[i]);
	rgba[i][RCOMP] = lpb[3*i+2];
	rgba[i][GCOMP] = lpb[3*i+1];
	rgba[i][BCOMP] = lpb[3*i];
	rgba[i][ACOMP] = 255;
  }
}


/*********************************************************************/

/* DOUBLE BUFFER 16-bit */

#define WMSETPIXEL16(pwc, y, x, r, g, b) { \
LPWORD lpw = ((LPWORD)((pwc)->pbPixels + (pwc)->ScanWidth * (y)) + (x)); \
*lpw = BGR16((r),(g),(b)); }



/* Write a horizontal span of RGBA color pixels with a boolean mask. */
static void write_rgba_span_16(struct gl_context *ctx, 
			       struct gl_renderbuffer *rb, 
			       GLuint n, GLint x, GLint y,
			       const void *values, 
			       const GLubyte *mask)
{
    const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    GLuint i;
    LPWORD lpw;

    (void) ctx;
    
    y=FLIP(y);
    lpw = ((LPWORD)(pwfb->pbPixels + pwfb->ScanWidth * y)) + x;
    if (mask) {
	for (i=0; i<n; i++)
	    if (mask[i])
                lpw[i] = BGR16(rgba[i][RCOMP], rgba[i][GCOMP], 
			       rgba[i][BCOMP]);
    }
    else {
	for (i=0; i<n; i++)
                *lpw++ = BGR16(rgba[i][RCOMP], rgba[i][GCOMP], 
			       rgba[i][BCOMP]);
    }
}



/* Write an array of RGBA pixels with a boolean mask. */
static void write_rgba_pixels_16(struct gl_context *ctx, 
				 struct gl_renderbuffer *rb,
				 GLuint n, const GLint x[], const GLint y[],
				 const void *values, 
				 const GLubyte *mask)
{
    const GLubyte (*rgba)[4] = (const GLubyte (*)[4])values;
    GLuint i;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    (void) ctx;
    for (i=0; i<n; i++)
	if (mask[i])
	    WMSETPIXEL16(pwfb, FLIP(y[i]), x[i],
			 rgba[i][RCOMP], rgba[i][GCOMP], rgba[i][BCOMP]);
}


/* Read a horizontal span of color pixels. */
static void read_rgba_span_16(struct gl_context *ctx, 
			      struct gl_renderbuffer *rb,
			      GLuint n, GLint x, GLint y,
			      void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    GLuint i, pixel;
    LPWORD lpw;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);
    
    y = FLIP(y);
    lpw = ((LPWORD)(pwfb->pbPixels + pwfb->ScanWidth * y)) + x;
    for (i=0; i<n; i++) {
	pixel = lpw[i];
	/* Windows uses 5,5,5 for 16-bit */
	rgba[i][RCOMP] = (pixel & 0x7c00) >> 7;
	rgba[i][GCOMP] = (pixel & 0x03e0) >> 2;
	rgba[i][BCOMP] = (pixel & 0x001f) << 3;
	rgba[i][ACOMP] = 255;
    }
}


/* Read an array of color pixels. */
static void read_rgba_pixels_16(struct gl_context *ctx, 
				struct gl_renderbuffer *rb,
				GLuint n, const GLint x[], const GLint y[],
				void *values)
{
    GLubyte (*rgba)[4] = (GLubyte (*)[4])values;
    GLuint i, pixel;
    LPWORD lpw;
    WMesaFramebuffer pwfb = wmesa_framebuffer(ctx->DrawBuffer);

    for (i=0; i<n; i++) {
	GLint y2 = FLIP(y[i]);
	lpw = ((LPWORD)(pwfb->pbPixels + pwfb->ScanWidth * y2)) + x[i];
	pixel = *lpw;
	/* Windows uses 5,5,5 for 16-bit */
	rgba[i][RCOMP] = (pixel & 0x7c00) >> 7;
	rgba[i][GCOMP] = (pixel & 0x03e0) >> 2;
	rgba[i][BCOMP] = (pixel & 0x001f) << 3;
	rgba[i][ACOMP] = 255;
  }
}




/**********************************************************************/
/*****                   BUFFER Functions                         *****/
/**********************************************************************/




static void
wmesa_delete_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
{
    _mesa_delete_renderbuffer(ctx, rb);
}


/**
 * This is called by Mesa whenever it determines that the window size
 * has changed.  Do whatever's needed to cope with that.
 */
static GLboolean
wmesa_renderbuffer_storage(struct gl_context *ctx, 
			   struct gl_renderbuffer *rb,
			   GLenum internalFormat, 
			   GLuint width, 
			   GLuint height)
{
    rb->Width = width;
    rb->Height = height;
    return GL_TRUE;
}


/**
 * Called by ctx->Driver.ResizeBuffers()
 * Resize the front/back colorbuffers to match the latest window size.
 */
static void
wmesa_resize_buffers(struct gl_context *ctx, struct gl_framebuffer *buffer,
                     GLuint width, GLuint height)
{
    WMesaFramebuffer pwfb = wmesa_framebuffer(buffer);

    if (pwfb->Base.Width != width || pwfb->Base.Height != height) {
	/* Realloc back buffer */
	if (ctx->Visual.doubleBufferMode == 1) {
	    wmDeleteBackingStore(pwfb);
	    wmCreateBackingStore(pwfb, width, height);
	}
    }
    _mesa_resize_framebuffer(ctx, buffer, width, height);
}


/**
 * Called by glViewport.
 * This is a good time for us to poll the current window size and adjust
 * our renderbuffers to match the current window size.
 * Remember, we have no opportunity to respond to conventional
 * resize events since the driver has no event loop.
 * Thus, we poll.
 * MakeCurrent also ends up making a call here, so that ensures
 * we get the viewport set correctly, even if the app does not call
 * glViewport and relies on the defaults.
 */
static void wmesa_viewport(struct gl_context *ctx, 
			   GLint x, GLint y, 
			   GLsizei width, GLsizei height)
{
    GLuint new_width, new_height;

    wmesa_get_buffer_size(ctx->WinSysDrawBuffer, &new_width, &new_height);

    /**
     * Resize buffers if the window size changed.
     */
    wmesa_resize_buffers(ctx, ctx->WinSysDrawBuffer, new_width, new_height);
    ctx->NewState |= _NEW_BUFFERS;  /* to update scissor / window bounds */
}




/**
 * Called when the driver should update it's state, based on the new_state
 * flags.
 */
static void wmesa_update_state(struct gl_context *ctx, GLuint new_state)
{
    _swrast_InvalidateState(ctx, new_state);
    _swsetup_InvalidateState(ctx, new_state);
    _vbo_InvalidateState(ctx, new_state);
    _tnl_InvalidateState(ctx, new_state);

    /* TODO - This code is not complete yet because I 
     * don't know what to do for all state updates.
     */

    if (new_state & _NEW_BUFFERS) {
    }
}





/**********************************************************************/
/*****                   WMESA Functions                          *****/
/**********************************************************************/

WMesaContext WMesaCreateContext(HDC hDC, 
				HPALETTE* Pal,
				GLboolean rgb_flag,
				GLboolean db_flag,
				GLboolean alpha_flag)
{
    WMesaContext c;
    struct dd_function_table functions;
    GLint red_bits, green_bits, blue_bits, alpha_bits;
    struct gl_context *ctx;
    struct gl_config *visual;

    (void) Pal;
    
    /* Indexed mode not supported */
    if (!rgb_flag)
	return NULL;

    /* Allocate wmesa context */
    c = CALLOC_STRUCT(wmesa_context);
    if (!c)
	return NULL;

#if 0
    /* I do not understand this contributed code */
    /* Support memory and device contexts */
    if(WindowFromDC(hDC) != NULL) {
	c->hDC = GetDC(WindowFromDC(hDC)); /* huh ???? */
    }
    else {
	c->hDC = hDC;
    }
#else
    c->hDC = hDC;
#endif

    /* Get data for visual */
    /* Dealing with this is actually a bit of overkill because Mesa will end
     * up treating all color component size requests less than 8 by using 
     * a single byte per channel.  In addition, the interface to the span
     * routines passes colors as an entire byte per channel anyway, so there
     * is nothing to be saved by telling the visual to be 16 bits if the device
     * is 16 bits.  That is, Mesa is going to compute colors down to 8 bits per
     * channel anyway.
     * But we go through the motions here anyway.
     */
    switch (GetDeviceCaps(c->hDC, BITSPIXEL)) {
    case 16:
	red_bits = green_bits = blue_bits = 5;
	alpha_bits = 0;
	break;
    default:
	red_bits = green_bits = blue_bits = 8;
	alpha_bits = 8;
	break;
    }
    /* Create visual based on flags */
    visual = _mesa_create_visual(db_flag,    /* db_flag */
                                 GL_FALSE,   /* stereo */
                                 red_bits, green_bits, blue_bits, /* color RGB */
                                 alpha_flag ? alpha_bits : 0, /* color A */
                                 DEFAULT_SOFTWARE_DEPTH_BITS, /* depth_bits */
                                 8,          /* stencil_bits */
                                 16,16,16,   /* accum RGB */
                                 alpha_flag ? 16 : 0, /* accum A */
                                 1);         /* num samples */
    
    if (!visual) {
	free(c);
	return NULL;
    }

    /* Set up driver functions */
    _mesa_init_driver_functions(&functions);
    functions.GetString = wmesa_get_string;
    functions.UpdateState = wmesa_update_state;
    functions.GetBufferSize = wmesa_get_buffer_size;
    functions.Flush = wmesa_flush;
    functions.Clear = clear;
    functions.ResizeBuffers = wmesa_resize_buffers;
    functions.Viewport = wmesa_viewport;

    /* initialize the Mesa context data */
    ctx = &c->gl_ctx;
    _mesa_initialize_context(ctx, API_OPENGL, visual,
                             NULL, &functions, (void *)c);

    /* visual no longer needed - it was copied by _mesa_initialize_context() */
    _mesa_destroy_visual(visual);

    _mesa_enable_sw_extensions(ctx);
    _mesa_enable_1_3_extensions(ctx);
    _mesa_enable_1_4_extensions(ctx);
    _mesa_enable_1_5_extensions(ctx);
    _mesa_enable_2_0_extensions(ctx);
    _mesa_enable_2_1_extensions(ctx);
  
    _mesa_meta_init(ctx);

    /* Initialize the software rasterizer and helper modules. */
    if (!_swrast_CreateContext(ctx) ||
        !_vbo_CreateContext(ctx) ||
        !_tnl_CreateContext(ctx) ||
	!_swsetup_CreateContext(ctx)) {
	_mesa_free_context_data(ctx);
	free(c);
	return NULL;
    }
    _swsetup_Wakeup(ctx);
    TNL_CONTEXT(ctx)->Driver.RunPipeline = _tnl_run_pipeline;

    return c;
}


void WMesaDestroyContext( WMesaContext pwc )
{
    struct gl_context *ctx = &pwc->gl_ctx;
    WMesaFramebuffer pwfb;
    GET_CURRENT_CONTEXT(cur_ctx);

    if (cur_ctx == ctx) {
        /* unbind current if deleting current context */
        WMesaMakeCurrent(NULL, NULL);
    }

    /* clean up frame buffer resources */
    pwfb = wmesa_lookup_framebuffer(pwc->hDC);
    if (pwfb) {
	if (ctx->Visual.doubleBufferMode == 1)
	    wmDeleteBackingStore(pwfb);
	wmesa_free_framebuffer(pwc->hDC);
    }

    /* Release for device, not memory contexts */
    if (WindowFromDC(pwc->hDC) != NULL)
    {
      ReleaseDC(WindowFromDC(pwc->hDC), pwc->hDC);
    }
    DeleteObject(pwc->clearPen); 
    DeleteObject(pwc->clearBrush); 
    
    _mesa_meta_free(ctx);

    _swsetup_DestroyContext(ctx);
    _tnl_DestroyContext(ctx);
    _vbo_DestroyContext(ctx);
    _swrast_DestroyContext(ctx);
    
    _mesa_free_context_data(ctx);
    free(pwc);
}


/**
 * Create a new color renderbuffer.
 */
static struct gl_renderbuffer *
wmesa_new_renderbuffer(void)
{
    struct gl_renderbuffer *rb = CALLOC_STRUCT(gl_renderbuffer);
    if (!rb)
        return NULL;

    _mesa_init_renderbuffer(rb, (GLuint)0);
    
    rb->_BaseFormat = GL_RGBA;
    rb->InternalFormat = GL_RGBA;
    rb->Delete = wmesa_delete_renderbuffer;
    rb->AllocStorage = wmesa_renderbuffer_storage;
    return rb;
}


void WMesaMakeCurrent(WMesaContext c, HDC hdc)
{
    WMesaFramebuffer pwfb;

    {
        /* return if already current */
        GET_CURRENT_CONTEXT(ctx);
        WMesaContext pwc = wmesa_context(ctx);
        if (pwc && c == pwc && pwc->hDC == hdc)
            return;
    }

    pwfb = wmesa_lookup_framebuffer(hdc);

    /* Lazy creation of framebuffers */
    if (c && !pwfb && hdc) {
        struct gl_renderbuffer *rb;
        struct gl_config *visual = &c->gl_ctx.Visual;
        GLuint width, height;

        get_window_size(hdc, &width, &height);

	c->clearPen = CreatePen(PS_SOLID, 1, 0); 
	c->clearBrush = CreateSolidBrush(0); 

        pwfb = wmesa_new_framebuffer(hdc, visual);

	/* Create back buffer if double buffered */
	if (visual->doubleBufferMode == 1) {
	    wmCreateBackingStore(pwfb, width, height);
	}
	
        /* make render buffers */
        if (visual->doubleBufferMode == 1) {
            rb = wmesa_new_renderbuffer();
            _mesa_add_renderbuffer(&pwfb->Base, BUFFER_BACK_LEFT, rb);
	}
        rb = wmesa_new_renderbuffer();
        _mesa_add_renderbuffer(&pwfb->Base, BUFFER_FRONT_LEFT, rb);

	/* Let Mesa own the Depth, Stencil, and Accum buffers */
        _swrast_add_soft_renderbuffers(&pwfb->Base,
                                       GL_FALSE, /* color */
                                       visual->depthBits > 0,
                                       visual->stencilBits > 0,
                                       visual->accumRedBits > 0,
                                       visual->alphaBits >0, 
                                       GL_FALSE);
    }

    if (c && pwfb)
	_mesa_make_current(&c->gl_ctx, &pwfb->Base, &pwfb->Base);
    else
        _mesa_make_current(NULL, NULL, NULL);
}


void WMesaSwapBuffers( HDC hdc )
{
    GET_CURRENT_CONTEXT(ctx);
    WMesaContext pwc = wmesa_context(ctx);
    WMesaFramebuffer pwfb = wmesa_lookup_framebuffer(hdc);

    if (!pwfb) {
        _mesa_problem(NULL, "wmesa: swapbuffers on unknown hdc");
        return;
    }

    /* If we're swapping the buffer associated with the current context
     * we have to flush any pending rendering commands first.
     */
    if (pwc->hDC == hdc) {
	_mesa_notifySwapBuffers(ctx);

	BitBlt(pwfb->hDC, 0, 0, pwfb->Base.Width, pwfb->Base.Height,
	       pwfb->dib_hDC, 0, 0, SRCCOPY);
    }
    else {
        /* XXX for now only allow swapping current window */
        _mesa_problem(NULL, "wmesa: can't swap non-current window");
    }
}

void WMesaShareLists(WMesaContext ctx_to_share, WMesaContext ctx)
{
	_mesa_share_state(&ctx->gl_ctx, &ctx_to_share->gl_ctx);	
}