/*
 * 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_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include <GL/glxtokens.h>
#include <string.h>
#include <windowstr.h>
#include <os.h>
#include <colormapst.h>

#include "privates.h"
#include "glxserver.h"
#include "glxutil.h"
#include "glxext.h"
#include "protocol-versions.h"

static DevPrivateKeyRec glxScreenPrivateKeyRec;
#define glxScreenPrivateKey (&glxScreenPrivateKeyRec)

const char GLServerVersion[] = "1.4";
static const char GLServerExtensions[] = 
			"GL_ARB_depth_texture "
			"GL_ARB_draw_buffers "
			"GL_ARB_fragment_program "
			"GL_ARB_fragment_program_shadow "
			"GL_ARB_imaging "
			"GL_ARB_multisample "
			"GL_ARB_multitexture "
			"GL_ARB_occlusion_query "
			"GL_ARB_point_parameters "
			"GL_ARB_point_sprite "
			"GL_ARB_shadow "
			"GL_ARB_shadow_ambient "
			"GL_ARB_texture_border_clamp "
			"GL_ARB_texture_compression "
			"GL_ARB_texture_cube_map "
			"GL_ARB_texture_env_add "
			"GL_ARB_texture_env_combine "
			"GL_ARB_texture_env_crossbar "
			"GL_ARB_texture_env_dot3 "
			"GL_ARB_texture_mirrored_repeat "
			"GL_ARB_texture_non_power_of_two "
			"GL_ARB_transpose_matrix "
			"GL_ARB_vertex_program "
			"GL_ARB_window_pos "
			"GL_EXT_abgr "
			"GL_EXT_bgra "
 			"GL_EXT_blend_color "
			"GL_EXT_blend_equation_separate "
			"GL_EXT_blend_func_separate "
			"GL_EXT_blend_logic_op "
 			"GL_EXT_blend_minmax "
 			"GL_EXT_blend_subtract "
			"GL_EXT_clip_volume_hint "
			"GL_EXT_copy_texture "
			"GL_EXT_draw_range_elements "
			"GL_EXT_fog_coord "
			"GL_EXT_framebuffer_object "
			"GL_EXT_multi_draw_arrays "
			"GL_EXT_packed_pixels "
			"GL_EXT_paletted_texture "
			"GL_EXT_point_parameters "
			"GL_EXT_polygon_offset "
			"GL_EXT_rescale_normal "
			"GL_EXT_secondary_color "
			"GL_EXT_separate_specular_color "
			"GL_EXT_shadow_funcs "
			"GL_EXT_shared_texture_palette "
 			"GL_EXT_stencil_two_side "
			"GL_EXT_stencil_wrap "
			"GL_EXT_subtexture "
			"GL_EXT_texture "
			"GL_EXT_texture3D "
			"GL_EXT_texture_compression_dxt1 "
			"GL_EXT_texture_compression_s3tc "
			"GL_EXT_texture_edge_clamp "
 			"GL_EXT_texture_env_add "
 			"GL_EXT_texture_env_combine "
 			"GL_EXT_texture_env_dot3 "
 			"GL_EXT_texture_filter_anisotropic "
			"GL_EXT_texture_lod "
 			"GL_EXT_texture_lod_bias "
 			"GL_EXT_texture_mirror_clamp "
			"GL_EXT_texture_object "
			"GL_EXT_texture_rectangle "
			"GL_EXT_vertex_array "
			"GL_3DFX_texture_compression_FXT1 "
			"GL_APPLE_packed_pixels "
			"GL_ATI_draw_buffers "
			"GL_ATI_texture_env_combine3 "
			"GL_ATI_texture_mirror_once "
 			"GL_HP_occlusion_test "
			"GL_IBM_texture_mirrored_repeat "
			"GL_INGR_blend_func_separate "
			"GL_MESA_pack_invert "
			"GL_MESA_ycbcr_texture "
			"GL_NV_blend_square "
			"GL_NV_depth_clamp "
			"GL_NV_fog_distance "
			"GL_NV_fragment_program "
			"GL_NV_fragment_program_option "
			"GL_NV_fragment_program2 "
			"GL_NV_light_max_exponent "
			"GL_NV_multisample_filter_hint "
			"GL_NV_point_sprite "
			"GL_NV_texgen_reflection "
			"GL_NV_texture_compression_vtc "
			"GL_NV_texture_env_combine4 "
			"GL_NV_texture_expand_normal "
			"GL_NV_texture_rectangle "
			"GL_NV_vertex_program "
			"GL_NV_vertex_program1_1 "
			"GL_NV_vertex_program2 "
			"GL_NV_vertex_program2_option "
			"GL_NV_vertex_program3 "
			"GL_OES_compressed_paletted_texture "
			"GL_SGI_color_matrix "
			"GL_SGI_color_table "
			"GL_SGIS_generate_mipmap "
			"GL_SGIS_multisample "
			"GL_SGIS_point_parameters "
			"GL_SGIS_texture_border_clamp "
			"GL_SGIS_texture_edge_clamp "
			"GL_SGIS_texture_lod "
			"GL_SGIX_depth_texture "
			"GL_SGIX_shadow "
			"GL_SGIX_shadow_ambient "
			"GL_SUN_slice_accum "
			;

/*
** We have made the simplifying assuption that the same extensions are 
** supported across all screens in a multi-screen system.
*/
static char GLXServerVendorName[] = "SGI";
unsigned glxMajorVersion = SERVER_GLX_MAJOR_VERSION;
unsigned glxMinorVersion = SERVER_GLX_MINOR_VERSION;
static char GLXServerExtensions[] =
			"GLX_ARB_multisample "
			"GLX_EXT_visual_info "
			"GLX_EXT_visual_rating "
			"GLX_EXT_import_context "
                        "GLX_EXT_texture_from_pixmap "
			"GLX_OML_swap_method "
			"GLX_SGI_make_current_read "
#ifndef __APPLE__
			"GLX_SGIS_multisample "
#endif
			"GLX_SGIX_fbconfig "
			"GLX_SGIX_pbuffer "
			"GLX_MESA_copy_sub_buffer "
                        "GLX_INTEL_swap_event"
			;

static Bool
glxCloseScreen (int index, ScreenPtr pScreen)
{
    __GLXscreen *pGlxScreen = glxGetScreen(pScreen);

    pScreen->CloseScreen = pGlxScreen->CloseScreen;

    pGlxScreen->destroy(pGlxScreen);

    return pScreen->CloseScreen(index, pScreen);
}

__GLXscreen *
glxGetScreen(ScreenPtr pScreen)
{
    return dixLookupPrivate(&pScreen->devPrivates, glxScreenPrivateKey);
}

_X_EXPORT void GlxSetVisualConfigs(int nconfigs,
                         void *configs, void **privates)
{
    /* We keep this stub around for the DDX drivers that still
     * call it. */
}

GLint glxConvertToXVisualType(int visualType)
{
    static const int x_visual_types[] = {
	TrueColor,   DirectColor,
	PseudoColor, StaticColor,
	GrayScale,   StaticGray
    };

    return ( (unsigned) (visualType - GLX_TRUE_COLOR) < 6 )
	? x_visual_types[ visualType - GLX_TRUE_COLOR ] : -1;
}

/* This code inspired by composite/compinit.c.  We could move this to
 * mi/ and share it with composite.*/

static VisualPtr
AddScreenVisuals(ScreenPtr pScreen, int count, int d)
{
    int		 i;
    DepthPtr	 depth;

    depth = NULL;
    for (i = 0; i < pScreen->numDepths; i++) {
	if (pScreen->allowedDepths[i].depth == d) {
	    depth = &pScreen->allowedDepths[i];
	    break;
	}
    }
    if (depth == NULL)
	return NULL;

    if (ResizeVisualArray(pScreen, count, depth) == FALSE)
        return NULL;

    /* Return a pointer to the first of the added visuals. */ 
    return pScreen->visuals + pScreen->numVisuals - count;
}

static int
findFirstSet(unsigned int v)
{
    int i;

    for (i = 0; i < 32; i++)
	if (v & (1 << i))
	    return i;

    return -1;
}

static void
initGlxVisual(VisualPtr visual, __GLXconfig *config)
{
    int maxBits;
    maxBits = max(config->redBits, max(config->greenBits, config->blueBits));

    config->visualID = visual->vid;
    visual->class = glxConvertToXVisualType(config->visualType);
    visual->bitsPerRGBValue = maxBits;
    visual->ColormapEntries = 1 << maxBits;
    visual->nplanes = config->redBits + config->greenBits + config->blueBits;

    visual->redMask = config->redMask;
    visual->greenMask = config->greenMask;
    visual->blueMask = config->blueMask;
    visual->offsetRed = findFirstSet(config->redMask);
    visual->offsetGreen = findFirstSet(config->greenMask);
    visual->offsetBlue = findFirstSet(config->blueMask);
}

static __GLXconfig *
pickFBConfig(__GLXscreen *pGlxScreen, VisualPtr visual)
{
    __GLXconfig *best = NULL, *config;
    int best_score = 0;

    for (config = pGlxScreen->fbconfigs; config != NULL; config = config->next) {
	int score = 0;

	if (config->redMask != visual->redMask ||
	    config->greenMask != visual->greenMask ||
	    config->blueMask != visual->blueMask)
	    continue;
	if (config->visualRating != GLX_NONE)
	    continue;
	if (glxConvertToXVisualType(config->visualType) != visual->class)
	    continue;
	/* If it's the 32-bit RGBA visual, demand a 32-bit fbconfig. */
	if (visual->nplanes == 32 && config->rgbBits != 32)
	    continue;
	/* Can't use the same FBconfig for multiple X visuals.  I think. */
	if (config->visualID != 0)
	    continue;

	if (config->doubleBufferMode > 0)
	    score += 8;
	if (config->depthBits > 0)
	    score += 4;
	if (config->stencilBits > 0)
	    score += 2;
	if (config->alphaBits > 0)
	    score++;

	if (score > best_score) {
	    best = config;
	    best_score = score;
	}
    }

    return best;
}

void __glXScreenInit(__GLXscreen *pGlxScreen, ScreenPtr pScreen)
{
    __GLXconfig *m;
    __GLXconfig *config;
    int i;

    if (!dixRegisterPrivateKey(&glxScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
	return;

    pGlxScreen->pScreen       = pScreen;
    pGlxScreen->GLextensions  = strdup(GLServerExtensions);
    pGlxScreen->GLXvendor     = strdup(GLXServerVendorName);
    pGlxScreen->GLXextensions = strdup(GLXServerExtensions);

    /* All GLX providers must support all of the functionality required for at
     * least GLX 1.2.  If the provider supports a higher version, the GLXminor
     * version can be changed in the provider's screen-probe routine.  For
     * most providers, the screen-probe routine is the caller of this
     * function.
     */
    pGlxScreen->GLXmajor      = 1;
    pGlxScreen->GLXminor      = 2;

    pGlxScreen->CloseScreen = pScreen->CloseScreen;
    pScreen->CloseScreen = glxCloseScreen;

    i = 0;
    for (m = pGlxScreen->fbconfigs; m != NULL; m = m->next) {
	m->fbconfigID = FakeClientID(0);
	m->visualID = 0;
	i++;
    }
    pGlxScreen->numFBConfigs = i;

    pGlxScreen->visuals =
	calloc(pGlxScreen->numFBConfigs, sizeof (__GLXconfig *));

    /* First, try to choose featureful FBconfigs for the existing X visuals.
     * Note that if multiple X visuals end up with the same FBconfig being
     * chosen, the later X visuals don't get GLX visuals (because we want to
     * prioritize the root visual being GLX).
     */
    for (i = 0; i < pScreen->numVisuals; i++) {
	VisualPtr visual = &pScreen->visuals[i];

	config = pickFBConfig(pGlxScreen, visual);
	if (config) {
	    pGlxScreen->visuals[pGlxScreen->numVisuals++] = config;
	    config->visualID = visual->vid;
	}
    }

    /* Then, add new visuals corresponding to all FBconfigs that didn't have
     * an existing, appropriate visual.
     */
    for (config = pGlxScreen->fbconfigs; config != NULL; config = config->next) {
	int depth;

	VisualPtr visual;

	if (config->visualID != 0)
	    continue;

	/* Only count RGB bits and not alpha, as we're not trying to create
	 * visuals for compositing (that's what the 32-bit composite visual
	 * set up above is for.
	 */
	depth = config->redBits + config->greenBits + config->blueBits;

	/* Make sure that our FBconfig's depth can actually be displayed
	 * (corresponds to an existing visual).
	 */
	for (i = 0; i < pScreen->numVisuals; i++) {
	    if (depth == pScreen->visuals[i].nplanes)
		break;
	}
	if (i == pScreen->numVisuals)
	    continue;

	/* Create a new X visual for our FBconfig. */
	visual = AddScreenVisuals(pScreen, 1, depth);
	if (visual == NULL)
	    continue;

	pGlxScreen->visuals[pGlxScreen->numVisuals++] = config;
	initGlxVisual(visual, config);
    }

    dixSetPrivate(&pScreen->devPrivates, glxScreenPrivateKey, pGlxScreen);
}

void __glXScreenDestroy(__GLXscreen *screen)
{
    free(screen->GLXvendor);
    free(screen->GLXextensions);
    free(screen->GLextensions);
}
