/* Copyright © 2013 Intel Corporation
 *
 * 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 (including the next
 * paragraph) 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
 * THE AUTHORS OR COPYRIGHT HOLDERS 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 "piglit-util-gl.h"
#include "piglit-glx-util.h"
#include "query-renderer-common.h"

struct test_vector {
	const char *name_string;
	int attribute;
	int value_count;
};

#define ENUM(name, count) { # name, name, count }

static const struct test_vector all_valid_integer_enums[] = {
        ENUM(GLX_RENDERER_VENDOR_ID_MESA, 1),
        ENUM(GLX_RENDERER_DEVICE_ID_MESA, 1),
        ENUM(GLX_RENDERER_VERSION_MESA, 3),
        ENUM(GLX_RENDERER_ACCELERATED_MESA, 1),
        ENUM(GLX_RENDERER_VIDEO_MEMORY_MESA, 1),
        ENUM(GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA, 1),
        ENUM(GLX_RENDERER_PREFERRED_PROFILE_MESA, 1),
        ENUM(GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA, 2),
        ENUM(GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA, 2),
        ENUM(GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA, 2),
        ENUM(GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA, 2),
};

static const struct test_vector all_valid_string_enums[] = {
        ENUM(GLX_RENDERER_VENDOR_ID_MESA, 0),
        ENUM(GLX_RENDERER_DEVICE_ID_MESA, 0),
};

static bool
verify_integer_values(const char *name, Bool success,
		      const struct test_vector *test,
		      const unsigned *buffer, unsigned buffer_size,
		      bool silent)
{
	char text[512];
	unsigned text_size;
	bool pass = true;
	unsigned j;

	if (!success) {
		fprintf(stderr, "%s(%s) failed.\n", name, test->name_string);

		/* If the call failed, don't bother checking that the correct
		 * number of values were written.
		 */
		return false;
	}

	if (!silent) {
		text_size = snprintf(text, sizeof(text), "%s(%s) values:\n    ",
				     name,
				     test->name_string);
		for (j = 0; j < test->value_count; j++) {
			text_size += snprintf(&text[text_size],
					      sizeof(text) - text_size,
					      "%d ",
					      buffer[j]);
		}

		printf("%s\n", text);
	}

	for (j = 0; j < test->value_count; j++) {
		if (buffer[j] == 0xDEADBEEF) {
			fprintf(stderr,
				"%s(%s) only wrote %d values, expected %d.\n",
				name,
				test->name_string,
				j,
				test->value_count);
			pass = false;
			break;
		}
	}

	for (j = test->value_count; j < buffer_size; j++) {
		if (buffer[j] != 0xDEADBEEF) {
			fprintf(stderr,
				"%s(%s) wrote at least %d values, expected "
				"only %d.\n",
				name,
				test->name_string,
				j + 1,
				test->value_count);
			pass = false;
			break;
		}
	}

	/* value sanity check */
	switch (test->attribute) {
	case GLX_RENDERER_PREFERRED_PROFILE_MESA: {
	    unsigned legal = GLX_CONTEXT_CORE_PROFILE_BIT_ARB |
			     GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB |
			     GLX_CONTEXT_ES_PROFILE_BIT_EXT |
			     GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
	    if (buffer[0] & ~legal) {
		fprintf(stderr, "Unknown bits set in preferred profile "
			"bitmask: 0x%x\n", buffer[0] & ~legal);
		pass = false;
	    }
            if (!buffer[0]) {
                fprintf(stderr, "No preferred API profile!\n");
                pass = false;
            } else if (buffer[0] & (buffer[0] - 1)) {
                fprintf(stderr, "More than one preferred API profile\n");
                pass = false;
            }
	    break;
	    }
	default:
	    break;
	}

	return pass;
}

static bool
subtest_QueryRendererInteger(Display *dpy)
{
	static const char subtest_name[] =
		"glXQueryRendererIntegerMESA and "
		"glXQueryCurrentRendererIntegerMESA";

	bool pass = true;
	unsigned i;

	for (i = 0; i < ARRAY_SIZE(all_valid_integer_enums); i++) {
		unsigned buffer_a[16];
		unsigned buffer_b[ARRAY_SIZE(buffer_a)];
		Bool success;
		unsigned j;

		for (j = 0; j < ARRAY_SIZE(buffer_a); j++) {
			buffer_a[j] = 0xDEADBEEF;
			buffer_b[j] = 0xDEADBEEF;
		}

		success =
			glXQueryRendererIntegerMESA(dpy, 0, 0,
						    all_valid_integer_enums[i].attribute,
						    buffer_a);
		pass = verify_integer_values("glXQueryRendererIntegerMESA",
					     success,
					     &all_valid_integer_enums[i],
					     buffer_a,
					     ARRAY_SIZE(buffer_a),
					     false)
			&& pass;

		success =
			glXQueryCurrentRendererIntegerMESA(
						    all_valid_integer_enums[i].attribute,
						    buffer_b);
		pass = verify_integer_values("glXQueryCurrentRendererIntegerMESA",
					     success,
					     &all_valid_integer_enums[i],
					     buffer_b,
					     ARRAY_SIZE(buffer_b),
					     true)
			&& pass;

		for (j = 0; j < all_valid_integer_enums[i].value_count; j++) {
			if (buffer_a[j] != buffer_b[j]) {
				fprintf(stderr,
					"glXQueryRendererIntegerMESA and "
					"glXQueryCurrentRendererIntegerMESA "
					"disagree about %s value %d: "
					"%d != %d\n",
					all_valid_integer_enums[i].name_string,
					j,
					buffer_a[j],
					buffer_b[j]);
				pass = false;
			}
		}
	}

	piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
				     subtest_name);
	return pass;
}

static bool
subtest_QueryRendererString(Display *dpy)
{
	static const char subtest_name[] =
		"glXQueryRendererStringMESA and "
		"glXQueryCurrentRendererStringMESA";

	bool pass = true;
	unsigned i;

	for (i = 0; i < ARRAY_SIZE(all_valid_string_enums); i++) {
		const char *string_a;
		const char *string_b;

		string_a =
			glXQueryRendererStringMESA(dpy, 0, 0,
						   all_valid_string_enums[i].attribute);
		if (string_a == NULL) {
			fprintf(stderr,
				"glXQueryRendererStringMESA(%s) failed.\n",
				all_valid_string_enums[i].name_string);
			pass = false;
		} else {
			printf("glXQueryRendererStringMESA(%s) value:\n"
			       "    %s\n",
			       all_valid_string_enums[i].name_string,
			       string_a);
		}

		string_b = glXQueryCurrentRendererStringMESA(all_valid_string_enums[i].attribute);
		if (string_b == NULL) {
			fprintf(stderr,
				"glXQueryRendererStringMESA(%s) failed.\n",
				all_valid_string_enums[i].name_string);
			pass = false;
		}

		if (string_a != NULL && string_b != NULL
		    && strcmp(string_a, string_b) != 0) {
			fprintf(stderr,
				"glXQueryRendererIntegerMESA and "
				"glXQueryCurrentRendererIntegerMESA "
				"disagree about %s: %s != %s\n",
				all_valid_string_enums[i].name_string,
				string_a,
				string_b);
			pass = false;
		}
	}

	piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
				     subtest_name);
	return pass;
}

int main(int argc, char **argv)
{
	bool pass = true;
	Display *dpy = NULL;
	GLXFBConfig fbconfig = None;
	XVisualInfo *visinfo = NULL;
	Window win = None;
	GLXWindow glxWin = None;
	GLXContext ctx;

	dpy = piglit_get_glx_display();

	piglit_require_glx_version(dpy, 1, 4);

	initialize_function_pointers(dpy);

	visinfo = piglit_get_glx_visual(dpy);
	fbconfig = piglit_glx_get_fbconfig_for_visinfo(dpy, visinfo);

	win = piglit_get_glx_window_unmapped(dpy, visinfo);
	glxWin = glXCreateWindow(dpy, fbconfig, win, NULL);

	ctx = glXCreateNewContext(dpy, fbconfig, GLX_RGBA_TYPE, NULL, True);
	if (ctx == NULL) {
		fprintf(stderr,	"Unable to create OpenGL context!\n");
		piglit_report_result(PIGLIT_FAIL);
	}

        glXMakeContextCurrent(dpy, glxWin, glxWin, ctx);
        piglit_dispatch_default_init(PIGLIT_DISPATCH_GL);

	pass = subtest_QueryRendererInteger(dpy) && pass;
	pass = subtest_QueryRendererString(dpy) && pass;

        glXMakeContextCurrent(dpy, 0, 0, 0);

	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
	return 0;
}
