/**
 * Copyright 2017 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.
 */

/** @file named-framebuffer-read-buffer-errors.c
 *
 * Test that NamedFramebufferReadBuffer() returns correct error
 * message for different values.
 *
 * All spec quotes come from OpenGL 4.5 spec, section 18.2.1
 * "Selecting Buffers for Reading", page 502 (524 on PDF).
 *
 * Note that for 4.5, they are the same errors that for ReadBuffer, so
 * we get that method tested it, although indirectly. Also some spec
 * quotes could mention ReadBuffer too.
 *
 * From OpenGL 4.5, section 18.2.1 "Selecting Buffers for Reading",
 * page 502:
 *
 *  "When reading pixels from a color buffer of a framebuffer object,
 *   the buffer selected for reading is termed the read buffer, and is
 *   controlled with the commands:
 *
 *   void ReadBuffer( enum src );
 *   void NamedFramebufferReadBuffer( uint framebuffer, enum src );
 *
 *   For ReadBuffer, the target framebuffer object is that bound to
 *   READ_- FRAMEBUFFER . For NamedFramebufferReadBuffer, framebuffer
 *   is zero or the name of the target framebuffer object. If
 *   framebuffer is zero, then the default read framebuffer is
 *   affected."
 */

#include "piglit-util-gl.h"

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_core_version = 45;

	config.window_visual = PIGLIT_GL_VISUAL_RGBA |
		PIGLIT_GL_VISUAL_DOUBLE;

	config.khr_no_error_support = PIGLIT_HAS_ERRORS;

PIGLIT_GL_TEST_CONFIG_END

static const GLenum table_17_4[] = {
	GL_NONE,
	GL_FRONT_LEFT,
	GL_FRONT_RIGHT,
	GL_BACK_LEFT,
	GL_BACK_RIGHT,
	GL_FRONT,
	GL_BACK,
	GL_LEFT,
	GL_RIGHT,
	GL_FRONT_AND_BACK
};

void
piglit_init(int argc, char **argv)
{
	bool pass = true;
	GLuint framebuffer;
	GLuint default_framebuffer = 0;
	bool subtest_pass;
	int i;

	glCreateFramebuffers(1, &framebuffer);
	piglit_check_gl_error(GL_NO_ERROR);

	/*
	 * "An INVALID_OPERATION error is generated by
	 * NamedFramebuffer- ReadBuffer if framebuffer is not zero or
	 * the name of an existing framebuffer object.
	 */
	glNamedFramebufferReadBuffer(5, GL_BACK);
	PIGLIT_SUBTEST_ERROR(GL_INVALID_OPERATION, pass, "INVALID_OPERATION if "
			     "framebuffer is not zero or the name of an existing "
			     "framebuffer");

	/*
	 * "An INVALID_ENUM error is generated if src is not one of the values in
	 *  tables 17.4 or 17.5."
	 */
	subtest_pass = true;
	glNamedFramebufferReadBuffer(default_framebuffer, GL_RED);
	subtest_pass = subtest_pass && piglit_check_gl_error(GL_INVALID_ENUM);
	glNamedFramebufferReadBuffer(framebuffer, GL_RED);
	subtest_pass = subtest_pass && piglit_check_gl_error(GL_INVALID_ENUM);
	PIGLIT_SUBTEST_CONDITION(subtest_pass, pass, "INVALID_ENUM error is "
				 "generated if any value in bufs is not one of "
				 "the values in tables 17.4 or 17.5.");
	/*
	 * "An INVALID_OPERATION error is generated if the default
	 * framebuffer is affected and src is a value (other than
	 * NONE) that does not indicate any of the color buffers
	 * allocated to the default framebuffer."
	 *
	 * So for the default framebuffer the value returned should be
	 * NO_ERROR or INVALID_OPERATION.
	 */
	subtest_pass = true;
	for (i = 0; i < ARRAY_SIZE(table_17_4); i++) {
		GLenum err = 0;

		glNamedFramebufferReadBuffer(default_framebuffer, table_17_4[i]);
		err = glGetError();
		if (err != GL_NO_ERROR && err != GL_INVALID_OPERATION) {
			printf("Expected GL_NO_ERROR or GL_INVALID_OPERATION "
			       "with %s but received: %s\n",
			       piglit_get_gl_enum_name(table_17_4[i]),
			       piglit_get_gl_error_name(err));
			subtest_pass = false;
		}
	}
	PIGLIT_SUBTEST_CONDITION(subtest_pass, pass, "An INVALID_OPERATION error"
				 " is generated if the default framebuffer is "
				 "affected and src is a value (other than NONE) "
				 "that does not indicate any of the color buffers"
				 " allocated to the default framebuffer.");

	/*
	 * "An INVALID_OPERATION error is generated if a framebuffer
	 *  object is affected, and src is one of the constants from
	 *  table 17.4 (other than NONE , or COLOR_ATTACHMENTm where m
	 *  is greater than or equal to the value of
	 *  MAX_COLOR_ATTACHMENTS ).
	 */
	int max_attachments;
	glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_attachments);
	subtest_pass = true;

	/* Starting at 1, as GL_NONE is valid */
	for (i = 1; i < ARRAY_SIZE(table_17_4); i++) {
		GLenum err = 0;

		glNamedFramebufferReadBuffer(framebuffer, table_17_4[i]);
		err = glGetError();
		if (err != GL_INVALID_OPERATION) {
			printf("Expected GL_INVALID_OPERATION with"
			       " %s but received: %s\n",
			       piglit_get_gl_enum_name(table_17_4[i]),
			       piglit_get_gl_error_name(err));
			subtest_pass = false;
		}
	}

	glNamedFramebufferReadBuffer(framebuffer,
				     GL_COLOR_ATTACHMENT0 + max_attachments);
	subtest_pass = subtest_pass &&
	  piglit_check_gl_error(GL_INVALID_OPERATION);
	PIGLIT_SUBTEST_CONDITION(subtest_pass, pass, "An INVALID_OPERATION error"
				 " is generated if a framebuffer object is "
				 "affected, and src is one of the constants from "
				 "table 17.4 (other than NONE, or COLOR_ATTACHMENTm"
				 " where m is greater than or equal to the value"
				 " of MAX_COLOR_ATTACHMENTS)");

	/* clean up */
	glDeleteFramebuffers(1, &framebuffer);

	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
}

enum piglit_result
piglit_display(void)
{
	/* UNREACHED */
	return PIGLIT_FAIL;
}
