/*
 * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
 * Copyright (C) 1991-2000 Silicon Graphics, Inc. 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 including the dates of first publication and
 * either this permission notice or a reference to
 * http://oss.sgi.com/projects/FreeB/
 * 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
 * SILICON GRAPHICS, INC. 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.
 *
 * Except as contained in this notice, the name of Silicon Graphics, Inc.
 * shall not be used in advertising or otherwise to promote the sale, use or
 * other dealings in this Software without prior written authorization from
 * Silicon Graphics, Inc.
 */

#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif

#include "dmx.h"
#include <GL/glx.h>
#include <GL/glxproto.h>
#include <X11/extensions/Xext.h>
#include <X11/extensions/extutil.h>

#include "dmx_glxvisuals.h"

__GLXvisualConfig *GetGLXVisualConfigs(Display *dpy, int screen, int *nconfigs)
{
    xGLXGetVisualConfigsReq *req;
    xGLXGetVisualConfigsReply reply;
    __GLXvisualConfig *config, *configs;
    GLint i, j, nvisuals, nprops;
    INT32 *props, *p;
    int   majorOpcode, dummy;
    int   num_good_visuals;

    if (!XQueryExtension(dpy, "GLX", &majorOpcode, &dummy, &dummy)) {
       return NULL;
    }

    /* Send the glXGetVisualConfigs request */
    LockDisplay(dpy);
    GetReq(GLXGetVisualConfigs,req);
    req->reqType = majorOpcode;
    req->glxCode = X_GLXGetVisualConfigs;
    req->screen = screen;
    if (!_XReply(dpy, (xReply*) &reply, 0, False)) {
	/* Something is busted. Punt. */
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }

    nvisuals = (int)reply.numVisuals;
    if (!nvisuals) {
	/* This screen does not support GL rendering */
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }

    /* Check number of properties per visual */
    nprops = (int)reply.numProps;
    if (nprops < __GLX_MIN_CONFIG_PROPS)  {
	/* Huh?  Not in protocol defined limits.  Punt */
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }
    props = (INT32*) Xmalloc(nprops * __GLX_SIZE_CARD32);
    if (!props) {
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }

    /* Allocate memory for our config structure */
    config = (__GLXvisualConfig*)
	Xmalloc(nvisuals * sizeof(__GLXvisualConfig));
    if (!config) {
	free(props);
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }
    memset(config, 0, nvisuals * sizeof(__GLXvisualConfig));
    configs = config;
    num_good_visuals = 0;

    /* Convert config structure into our format */
    for (i=0; i<nvisuals; i++) {

	/* Read config structure */
	_XRead(dpy, (char *)props, (nprops * __GLX_SIZE_CARD32));

	/* fill in default values */
	config->visualRating = GLX_NONE_EXT;
	config->transparentPixel = GLX_NONE_EXT;

	/* Copy in the first set of properties */
	config->vid = props[0];
	config->class = props[1];

	config->rgba = (Bool) props[2];

	config->redSize = props[3];
	config->greenSize = props[4];
	config->blueSize = props[5];
	config->alphaSize = props[6];

	config->accumRedSize = props[7];
	config->accumGreenSize = props[8];
	config->accumBlueSize = props[9];
	config->accumAlphaSize = props[10];

	config->doubleBuffer = (Bool) props[11];
	config->stereo = (Bool) props[12];

	config->bufferSize = props[13];
	config->depthSize = props[14];
	config->stencilSize = props[15];

	config->auxBuffers = props[16];
	config->level = props[17];

	/* Process remaining properties */
	p = &props[18];
	for (j=__GLX_MIN_CONFIG_PROPS; j<nprops; j+=2) {
	    int property = *p++;
	    int value = *p++;

	    switch (property) {
	      case GLX_SAMPLES_SGIS:
		config->multiSampleSize = value;
		break;
	      case GLX_SAMPLE_BUFFERS_SGIS:
		config->nMultiSampleBuffers = value;
		break;

	      case GLX_TRANSPARENT_TYPE_EXT:
		config->transparentPixel = value;
		break;
	      case GLX_TRANSPARENT_INDEX_VALUE_EXT:
		config->transparentIndex = value;
		break;
	      case GLX_TRANSPARENT_RED_VALUE_EXT:
		config->transparentRed = value;
		break;
	      case GLX_TRANSPARENT_GREEN_VALUE_EXT:
		config->transparentGreen = value;
		break;
	      case GLX_TRANSPARENT_BLUE_VALUE_EXT:
		config->transparentBlue = value;
		break;
	      case GLX_TRANSPARENT_ALPHA_VALUE_EXT:
		config->transparentAlpha = value;
		break;

	      case GLX_VISUAL_CAVEAT_EXT:
		config->visualRating = value;
		break;

	      /* visualSelectGroup is an internal used property */
	      case GLX_VISUAL_SELECT_GROUP_SGIX:
		config->visualSelectGroup = value;
		break;

	      default :
		/* Ignore properties we don't recognize */
		break;
	    }
	} /* for j */

	/*
	// filter out overlay visuals (dmx does not support overlays)
	*/
	if (config->level == 0) {
	   config++;
	   num_good_visuals++;
	}

    } /* for i */

    UnlockDisplay(dpy);

    nvisuals = num_good_visuals;

    config = configs;
    for (i=0; i<nvisuals; i++) {
	/* XXX hack to fill-in mask info (need a better way to do this) */
	{
	    XVisualInfo *vis, template;
	    int n;

	    template.screen = screen;
	    template.visualid = config->vid;
	    vis = XGetVisualInfo(dpy, VisualScreenMask|VisualIDMask,
				 &template, &n);

	    if (vis != NULL) {
		config->redMask = vis->red_mask;
		config->greenMask = vis->green_mask;
		config->blueMask = vis->blue_mask;
		config->alphaMask = 0; 	/* XXX */
		free(vis);
	    }
	}
	config++;
    } /* for i */

    XFree(props);
    SyncHandle();

    *nconfigs = nvisuals;
    return configs;
}


__GLXFBConfig *GetGLXFBConfigs(Display *dpy, int glxMajorOpcode, int *nconfigs)
{
    xGLXGetFBConfigsReq *req;
    xGLXGetFBConfigsReply reply;
    __GLXFBConfig *config, *fbconfigs;
    GLint i, j, numFBConfigs, numAttribs;
    INT32 *attrs, *p;
    int screen = DefaultScreen( dpy );
    int numValidConfigs = 0;

    /* Send the glXGetFBConfigs request */
    LockDisplay(dpy);
    GetReq(GLXGetFBConfigs, req);
    req->reqType = glxMajorOpcode;
    req->glxCode = X_GLXGetFBConfigs;
    req->screen = screen;

    *nconfigs = 0;

    if (!_XReply(dpy, (xReply*) &reply, 0, False)) {
	/* Something is busted. Punt. */
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }

    numFBConfigs = (int)reply.numFBConfigs;
    if (!numFBConfigs) {
	/* This screen does not support GL rendering */
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;		
    }

    numAttribs = (int)reply.numAttribs;
    if (!numAttribs)  {
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }

    attrs = (INT32*) Xmalloc(2*numAttribs * __GLX_SIZE_CARD32);
    if (!attrs) {
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }

    /* Allocate memory for our config structure */
    config = (__GLXFBConfig*)
	Xmalloc(numFBConfigs * sizeof(__GLXFBConfig));
    if (!config) {
	free(attrs);
	UnlockDisplay(dpy);
	SyncHandle();
	return NULL;
    }
    memset(config, 0, numFBConfigs * sizeof(__GLXFBConfig));
    fbconfigs = config;

    /* Convert attribute list into our format */
    for (i=0; i<numFBConfigs; i++) {

	/* Fill in default properties */
	config->transparentType = GLX_NONE_EXT;
	config->visualCaveat = GLX_NONE_EXT;
	config->minRed = 0.;
	config->maxRed = 1.;
	config->minGreen = 0.;
	config->maxGreen = 1.;
	config->minBlue = 0.;
	config->maxBlue = 1.;
	config->minAlpha = 0.;
	config->maxAlpha = 1.;

	/* Read attribute list */
	_XRead(dpy, (char *)attrs, (2*numAttribs * __GLX_SIZE_CARD32));

	p = attrs;
	for (j=0; j<numAttribs; j++) {
	    int attribute = *p++;
	    int value = *p++;

	    switch (attribute) {
	      /* core attributes */
	      case GLX_FBCONFIG_ID:
		config->id = value;
		break;
	      case GLX_BUFFER_SIZE:
		config->indexBits = value;
		break;
	      case GLX_LEVEL:
		config->level = value;
		break;
	      case GLX_DOUBLEBUFFER:
		config->doubleBufferMode = value;
		break;
	      case GLX_STEREO:
		config->stereoMode = value;
		break;
	      case GLX_AUX_BUFFERS:
		config->maxAuxBuffers = value;
		break;
	      case GLX_RED_SIZE:
		config->redBits = value;
		break;
	      case GLX_GREEN_SIZE:
		config->greenBits = value;
		break;
	      case GLX_BLUE_SIZE:
		config->blueBits = value;
		break;
	      case GLX_ALPHA_SIZE:
		config->alphaBits = value;
		break;
	      case GLX_DEPTH_SIZE:
		config->depthBits = value;
		break;
	      case GLX_STENCIL_SIZE:
		config->stencilBits = value;
		break;
	      case GLX_ACCUM_RED_SIZE:
		config->accumRedBits = value;
		break;
	      case GLX_ACCUM_GREEN_SIZE:
		config->accumGreenBits = value;
		break;
	      case GLX_ACCUM_BLUE_SIZE:
		config->accumBlueBits = value;
		break;
	      case GLX_ACCUM_ALPHA_SIZE:
		config->accumAlphaBits = value;
		break;
	      case GLX_RENDER_TYPE:
		config->renderType = value;
		break;
	      case GLX_DRAWABLE_TYPE:
		config->drawableType = value;
		break;
	      case GLX_X_VISUAL_TYPE:
		config->visualType = value;
		break;
	      case GLX_CONFIG_CAVEAT:
		config->visualCaveat = value;
		break;
	      case GLX_TRANSPARENT_TYPE:
		config->transparentType = value;
		break;
	      case GLX_TRANSPARENT_INDEX_VALUE:
		config->transparentIndex = value;
		break;
	      case GLX_TRANSPARENT_RED_VALUE:
		config->transparentRed = value;
		break;
	      case GLX_TRANSPARENT_GREEN_VALUE:
		config->transparentGreen = value;
		break;
	      case GLX_TRANSPARENT_BLUE_VALUE:
		config->transparentBlue = value;
		break;
	      case GLX_TRANSPARENT_ALPHA_VALUE:
		config->transparentAlpha = value;
		break;
	      case GLX_MAX_PBUFFER_WIDTH:
		config->maxPbufferWidth = value;
		break;
	      case GLX_MAX_PBUFFER_HEIGHT:
		config->maxPbufferHeight = value;
		break;
	      case GLX_MAX_PBUFFER_PIXELS:
		config->maxPbufferPixels = value;
		break;
	      case GLX_VISUAL_ID:	
		config->associatedVisualId = value;
		break;

	      /* visualSelectGroup is an internal used property */
	      case GLX_VISUAL_SELECT_GROUP_SGIX:
		config->visualSelectGroup = value;
		break;

	      /* SGIS_multisample attributes */
	      case GLX_SAMPLES_SGIS:
		config->multiSampleSize = value;
		break;
	      case GLX_SAMPLE_BUFFERS_SGIS:
		config->nMultiSampleBuffers = value;
		break;

	      /* SGIX_pbuffer specific attributes */
	      case GLX_OPTIMAL_PBUFFER_WIDTH_SGIX:
		config->optimalPbufferWidth = value;
		break;
	      case GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX:
		config->optimalPbufferHeight = value;
		break;

	      default:
		/* Ignore attributes we don't recognize */
		break;
	    }
	} /* for j */

	/* Fill in derived values */
	config->screen = screen;

	config->rgbMode = config->renderType & GLX_RGBA_BIT;
	config->colorIndexMode = !config->rgbMode;

	config->haveAccumBuffer =
	    config->accumRedBits > 0 ||
	    config->accumGreenBits > 0 ||
	    config->accumBlueBits > 0;
	    /* Can't have alpha without color */

	config->haveDepthBuffer = config->depthBits > 0;
	config->haveStencilBuffer =  config->stencilBits > 0;

	/* overlay visuals are not valid for now */
	if (!config->level) {
	   config++;
	   numValidConfigs++;
	}

    } /* for i */
    UnlockDisplay(dpy);

    config = fbconfigs;
    for (i=0; i<numValidConfigs; i++) {

	/* XXX hack to fill-in mask info (need a better way to do this) */
	if (config->associatedVisualId != 0) {
	    XVisualInfo *vis, template;
	    int n;

	    template.screen = screen;
	    template.visualid = config->associatedVisualId;
	    vis = XGetVisualInfo(dpy, VisualScreenMask|VisualIDMask,
				 &template, &n);

	    if (vis != NULL) {
		config->redMask = (GLuint)vis->red_mask;
		config->greenMask = (GLuint)vis->green_mask;
		config->blueMask = (GLuint)vis->blue_mask;
		config->alphaMask = 0; 	/* XXX */
		free(vis);
	    }
	}

	config++;
    } /* for i */

    XFree(attrs);
    SyncHandle();

    *nconfigs = numValidConfigs;
    return fbconfigs;
}

__GLXvisualConfig *
GetGLXVisualConfigsFromFBConfigs(__GLXFBConfig *fbconfigs, int nfbconfigs, 
                                 XVisualInfo *visuals, int nvisuals,
				 __GLXvisualConfig *glxConfigs, int nGlxConfigs,
                                 int *nconfigs)
{
    __GLXvisualConfig *configs = NULL;
    int i;
    
    if (!fbconfigs || !nfbconfigs || !nconfigs) return NULL;
    *nconfigs = 0;

    /* Allocate memory for our config structure */
    configs = (__GLXvisualConfig*)
	Xmalloc(nfbconfigs * sizeof(__GLXvisualConfig));
    if (!configs) {
	return NULL;
    }
    memset(configs, 0, nfbconfigs * sizeof(__GLXvisualConfig));

    for (i=0; i<nfbconfigs; i++) {
       __GLXFBConfig *fbcfg = &fbconfigs[i];

       if (fbcfg->associatedVisualId > 0) {
	  __GLXvisualConfig *cfg = configs + (*nconfigs);
	  int j;
	  XVisualInfo *vinfo = NULL;

	  for (j=0; j<nvisuals; j++) {
	     if (visuals[j].visualid == fbcfg->associatedVisualId) {
		vinfo = &visuals[j];
		break;
	     }
	  }
	  if (!vinfo) continue;

	  /* skip 16 bit colormap visuals */
	  if (vinfo->depth == 16 &&
              vinfo->class != TrueColor &&
              vinfo->class != DirectColor ) {
	     continue;
	  }

	  (*nconfigs)++;

	  /*
           * if the same visualid exists in the glx configs,
	   * copy the glx attributes from the glx config
	   */
	  for (j=0; j<nGlxConfigs; j++) {
	     if (glxConfigs[j].vid == vinfo->visualid) 
		break;
	  }
	  if (j < nGlxConfigs) {
	     memcpy(cfg, &glxConfigs[j], sizeof(__GLXvisualConfig) );
	     continue;
	  }

	  /*
           * make glx attributes from the FB config attributes
	   */
	  cfg->vid = fbcfg->associatedVisualId;
	  cfg->class = vinfo->class;
	  cfg->rgba = !(fbcfg->renderType & GLX_COLOR_INDEX_BIT_SGIX);
	  cfg->redSize = fbcfg->redBits;
	  cfg->greenSize = fbcfg->greenBits;
	  cfg->blueSize = fbcfg->blueBits;
	  cfg->alphaSize = fbcfg->alphaBits;
	  cfg->redMask = fbcfg->redMask;
	  cfg->greenMask = fbcfg->greenMask;
	  cfg->blueMask = fbcfg->blueMask;
	  cfg->alphaMask = fbcfg->alphaMask;
	  cfg->accumRedSize = fbcfg->accumRedBits;
	  cfg->accumGreenSize = fbcfg->accumGreenBits;
	  cfg->accumBlueSize = fbcfg->accumBlueBits;
	  cfg->accumAlphaSize = fbcfg->accumAlphaBits;
	  cfg->doubleBuffer = fbcfg->doubleBufferMode;
	  cfg->stereo = fbcfg->stereoMode;
    	  if (vinfo->class == TrueColor || vinfo->class == DirectColor) {
	     cfg->bufferSize = (fbcfg->rgbMode ? (fbcfg->redBits +
		                               fbcfg->greenBits +
    		                               fbcfg->blueBits +
		                               fbcfg->alphaBits)
	                                    : fbcfg->indexBits );
	  }
	  else {
	     cfg->bufferSize = vinfo->depth;
	  }
      	  cfg->depthSize = fbcfg->depthBits;
	  cfg->stencilSize = fbcfg->stencilBits;
	  cfg->auxBuffers = fbcfg->maxAuxBuffers;
	  cfg->level = fbcfg->level;
	  cfg->visualRating = fbcfg->visualCaveat;
	  cfg->transparentPixel = fbcfg->transparentType;
	  cfg->transparentRed = fbcfg->transparentRed;
	  cfg->transparentGreen = fbcfg->transparentGreen;
	  cfg->transparentBlue = fbcfg->transparentBlue;
	  cfg->transparentAlpha = fbcfg->transparentAlpha;
	  cfg->transparentIndex = fbcfg->transparentIndex;
	  cfg->multiSampleSize = fbcfg->multiSampleSize;
	  cfg->nMultiSampleBuffers = fbcfg->nMultiSampleBuffers;
	  cfg->visualSelectGroup = fbcfg->visualSelectGroup;
       }
    }

    return configs;
}