/*
 * Mesa 3-D graphics library
 * Version:  6.5
 *
 * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */


#include "glxheader.h"
#include "xmesaP.h"

#ifdef FX
#include "../glide/fxdrv.h"

void
FXcreateContext(XMesaVisual v, XMesaWindow w, XMesaContext c, XMesaBuffer b)
{
   char *fxEnvVar = _mesa_getenv("MESA_GLX_FX");
   if (fxEnvVar) {
     if (fxEnvVar[0]!='d') {
       int attribs[100];
       int numAttribs = 0;
       int hw;
       if (v->mesa_visual.depthBits > 0) {
	 attribs[numAttribs++] = FXMESA_DEPTH_SIZE;
	 attribs[numAttribs++] = v->mesa_visual.depthBits;
       }
       if (v->mesa_visual.doubleBufferMode) {
	 attribs[numAttribs++] = FXMESA_DOUBLEBUFFER;
       }
       if (v->mesa_visual.accumRedBits > 0) {
	 attribs[numAttribs++] = FXMESA_ACCUM_SIZE;
	 attribs[numAttribs++] = v->mesa_visual.accumRedBits;
       }
       if (v->mesa_visual.stencilBits > 0) {
         attribs[numAttribs++] = FXMESA_STENCIL_SIZE;
         attribs[numAttribs++] = v->mesa_visual.stencilBits;
       }
       if (v->mesa_visual.alphaBits > 0) {
         attribs[numAttribs++] = FXMESA_ALPHA_SIZE;
         attribs[numAttribs++] = v->mesa_visual.alphaBits;
       }
       if (1) {
         attribs[numAttribs++] = FXMESA_SHARE_CONTEXT;
         attribs[numAttribs++] = (int) &(c->mesa);
       }
       attribs[numAttribs++] = FXMESA_NONE;

       /* [dBorca] we should take an envvar for `fxMesaSelectCurrentBoard'!!! */
/*       hw = fxMesaSelectCurrentBoard(0); */
       hw = GR_SSTTYPE_Voodoo2;

       /* if these fail, there's a new bug somewhere */
       ASSERT(b->mesa_buffer.Width > 0);
       ASSERT(b->mesa_buffer.Height > 0);

       if ((hw == GR_SSTTYPE_VOODOO) || (hw == GR_SSTTYPE_Voodoo2)) {
         b->FXctx = fxMesaCreateBestContext(0, b->mesa_buffer.Width,
                                            b->mesa_buffer.Height, attribs);
         if ((v->undithered_pf!=PF_Index) && (b->backxrb->ximage)) {
	   b->FXisHackUsable = b->FXctx ? GL_TRUE : GL_FALSE;
	   if (b->FXctx && (fxEnvVar[0]=='w' || fxEnvVar[0]=='W')) {
	     b->FXwindowHack = GL_TRUE;
	     FX_grSstControl(GR_CONTROL_DEACTIVATE);
	   }
           else {
	     b->FXwindowHack = GL_FALSE;
	   }
         }
       }
       else {
         if (fxEnvVar[0]=='w' || fxEnvVar[0]=='W')
	   b->FXctx = fxMesaCreateContext(w, GR_RESOLUTION_NONE,
					  GR_REFRESH_75Hz, attribs);
         else
	   b->FXctx = fxMesaCreateBestContext(0, b->mesa_buffer.Width,
                                              b->mesa_buffer.Height, attribs);
         b->FXisHackUsable = GL_FALSE;
         b->FXwindowHack = GL_FALSE;
       }
       /*
       fprintf(stderr,
               "voodoo %d, wid %d height %d hack: usable %d active %d\n",
               hw, b->mesa_buffer.Width, b->mesa_buffer.Height,
	       b->FXisHackUsable, b->FXwindowHack);
       */
     }
   }
   else {
      _mesa_warning(NULL, "WARNING: This Mesa Library includes the Glide driver but\n");
      _mesa_warning(NULL, "         you have not defined the MESA_GLX_FX env. var.\n");
      _mesa_warning(NULL, "         (check the README.3DFX file for more information).\n\n");
      _mesa_warning(NULL, "         you can disable this message with a 'export MESA_GLX_FX=disable'.\n");
   }
}


void FXdestroyContext( XMesaBuffer b )
{
   if (b && b->FXctx)
      fxMesaDestroyContext(b->FXctx);
}


GLboolean FXmakeCurrent( XMesaBuffer b )
{
   if (b->FXctx) {
      fxMesaMakeCurrent(b->FXctx);

      return GL_TRUE;
   }
   return GL_FALSE;
}


/*
 * Read image from VooDoo frame buffer into X/Mesa's back XImage.
 */
static void FXgetImage( XMesaBuffer b )
{
   GET_CURRENT_CONTEXT(ctx);
   static unsigned short pixbuf[MAX_WIDTH];
   GLuint x, y;
   GLuint width, height;

#ifdef XFree86Server
   x = b->frontxrb->pixmap->x;
   y = b->frontxrb->pixmap->y;
   width = b->frontxrb->pixmap->width;
   height = b->frontxrb->pixmap->height;
   depth = b->frontxrb->pixmap->depth;
#else
   xmesa_get_window_size(b->display, b, &width, &height);
   x = y = 0;
#endif
   if (b->mesa_buffer.Width != width || b->mesa_buffer.Height != height) {
      b->mesa_buffer.Width = MIN2((int)width, b->FXctx->width);
      b->mesa_buffer.Height = MIN2((int)height, b->FXctx->height);
      if (b->mesa_buffer.Width & 1)
         b->mesa_buffer.Width--;  /* prevent odd width */
   }

   /* [dBorca] we're always in the right GR_COLORFORMAT... aren't we? */
   /* grLfbWriteColorFormat(GR_COLORFORMAT_ARGB); */
   if (b->xm_visual->undithered_pf==PF_5R6G5B) {
      /* Special case: 16bpp RGB */
      grLfbReadRegion( GR_BUFFER_FRONTBUFFER,       /* src buffer */
                       0, b->FXctx->height - b->mesa_buffer.Height,  /*pos*/
                       b->mesa_buffer.Width, b->mesa_buffer.Height,  /* size */
                       b->mesa_buffer.Width * sizeof(GLushort), /* stride */
                       b->backxrb->ximage->data);         /* dest buffer */
   }
   else if (b->xm_visual->dithered_pf==PF_Dither
	    && GET_VISUAL_DEPTH(b->xm_visual)==8) {
      /* Special case: 8bpp RGB */
      for (y=0;y<b->mesa_buffer.Height;y++) {
         GLubyte *ptr = (GLubyte*) b->backxrb->ximage->data
                        + b->backxrb->ximage->bytes_per_line * y;
         XDITHER_SETUP(y);

         /* read row from 3Dfx frame buffer */
         grLfbReadRegion( GR_BUFFER_FRONTBUFFER,
                          0, b->FXctx->height-(b->mesa_buffer.Height-y),
                          b->mesa_buffer.Width, 1,
                          0,
                          pixbuf );

         /* write to XImage back buffer */
         for (x=0;x<b->mesa_buffer.Width;x++) {
            GLubyte r = (pixbuf[x] & 0xf800) >> 8;
            GLubyte g = (pixbuf[x] & 0x07e0) >> 3;
            GLubyte b = (pixbuf[x] & 0x001f) << 3;
            *ptr++ = XDITHER( x, r, g, b);
         }
      }
   }
   else {
      /* General case: slow! */
      for (y=0;y<b->mesa_buffer.Height;y++) {
         /* read row from 3Dfx frame buffer */
         grLfbReadRegion( GR_BUFFER_FRONTBUFFER,
                          0, b->FXctx->height-(b->mesa_buffer.Height-y),
                          b->mesa_buffer.Width, 1,
                          0,
                          pixbuf );

         /* write to XImage back buffer */
         for (x=0;x<b->mesa_buffer.Width;x++) {
            XMesaPutPixel(b->backxrb->ximage,x,y,
			  xmesa_color_to_pixel(ctx,
					       (pixbuf[x] & 0xf800) >> 8,
					       (pixbuf[x] & 0x07e0) >> 3,
					       (pixbuf[x] & 0x001f) << 3,
					       0xff,
                                               b->xm_visual->undithered_pf));
         }
      }
   }
   /* grLfbWriteColorFormat(GR_COLORFORMAT_ABGR); */
}


GLboolean FXswapBuffers( XMesaBuffer b )
{
   if (b->FXctx) {
      fxMesaSwapBuffers();

      if (!b->FXwindowHack)
         return GL_TRUE;

      FXgetImage(b);
   }
   return GL_FALSE;
}


/*
 * Switch 3Dfx support hack between window and full-screen mode.
 */
GLboolean XMesaSetFXmode( GLint mode )
{
   const char *fx = _mesa_getenv("MESA_GLX_FX");
   if (fx && fx[0] != 'd') {
      GET_CURRENT_CONTEXT(ctx);
      GrHwConfiguration hw;
      if (!FX_grSstQueryHardware(&hw)) {
         /*fprintf(stderr, "!grSstQueryHardware\n");*/
         return GL_FALSE;
      }
      if (hw.num_sst < 1) {
         /*fprintf(stderr, "hw.num_sst < 1\n");*/
         return GL_FALSE;
      }
      if (ctx) {
         /* [dBorca] Hack alert: 
	  * oh, this is sooo wrong: ctx above is
	  * really an fxMesaContext, not an XMesaContext
	  */
         XMesaBuffer xmbuf = XMESA_BUFFER(ctx->DrawBuffer);
         if (mode == XMESA_FX_WINDOW) {
	    if (xmbuf->FXisHackUsable) {
	       FX_grSstControl(GR_CONTROL_DEACTIVATE);
	       xmbuf->FXwindowHack = GL_TRUE;
	       return GL_TRUE;
	    }
	 }
	 else if (mode == XMESA_FX_FULLSCREEN) {
	    FX_grSstControl(GR_CONTROL_ACTIVATE);
	    xmbuf->FXwindowHack = GL_FALSE;
	    return GL_TRUE;
	 }
	 else {
	    /* Error: Bad mode value */
	 }
      }
   }
   /*fprintf(stderr, "fallthrough\n");*/
   return GL_FALSE;
}
#endif