/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.1 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Negative Fragment Pipe API tests.
 *//*--------------------------------------------------------------------*/

#include "es31fNegativeFragmentApiTests.hpp"

#include "gluCallLogWrapper.hpp"

#include "glwDefs.hpp"
#include "glwEnums.hpp"

namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace NegativeTestShared
{

using tcu::TestLog;
using glu::CallLogWrapper;
using namespace glw;

using tcu::TestLog;

void scissor (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_VALUE is generated if either width or height is negative.");
	ctx.glScissor(0, 0, -1, 0);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.glScissor(0, 0, 0, -1);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.glScissor(0, 0, -1, -1);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();
}

void depth_func (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if func is not an accepted value.");
	ctx.glDepthFunc(-1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void viewport (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_VALUE is generated if either width or height is negative.");
	ctx.glViewport(0, 0, -1, 1);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.glViewport(0, 0, 1, -1);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.glViewport(0, 0, -1, -1);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();
}

// Stencil functions
void stencil_func (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if func is not one of the eight accepted values.");
	ctx.glStencilFunc(-1, 0, 1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void stencil_func_separate (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if face is not GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK.");
	ctx.glStencilFuncSeparate(-1, GL_NEVER, 0, 1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_ENUM is generated if func is not one of the eight accepted values.");
	ctx.glStencilFuncSeparate(GL_FRONT, -1, 0, 1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void stencil_op (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if sfail, dpfail, or dppass is any value other than the defined symbolic constant values.");
	ctx.glStencilOp(-1, GL_ZERO, GL_REPLACE);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glStencilOp(GL_KEEP, -1, GL_REPLACE);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glStencilOp(GL_KEEP, GL_ZERO, -1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void stencil_op_separate (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if face is any value other than GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK.");
	ctx.glStencilOpSeparate(-1, GL_KEEP, GL_ZERO, GL_REPLACE);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_ENUM is generated if sfail, dpfail, or dppass is any value other than the eight defined symbolic constant values.");
	ctx.glStencilOpSeparate(GL_FRONT, -1, GL_ZERO, GL_REPLACE);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glStencilOpSeparate(GL_FRONT, GL_KEEP, -1, GL_REPLACE);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_ZERO, -1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void stencil_mask_separate (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if face is not GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK.");
	ctx.glStencilMaskSeparate(-1, 0);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

// Blend functions
void blend_equation (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if mode is not GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MAX or GL_MIN.");
	ctx.glBlendEquation(-1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void blend_equation_separate (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if modeRGB is not GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MAX or GL_MIN.");
	ctx.glBlendEquationSeparate(-1, GL_FUNC_ADD);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
	ctx.beginSection("GL_INVALID_ENUM is generated if modeAlpha is not GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MAX or GL_MIN.");
	ctx.glBlendEquationSeparate(GL_FUNC_ADD, -1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void blend_func (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if either sfactor or dfactor is not an accepted value.");
	ctx.glBlendFunc(-1, GL_ONE);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glBlendFunc(GL_ONE, -1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void blend_func_separate (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if srcRGB, dstRGB, srcAlpha, or dstAlpha is not an accepted value.");
	ctx.glBlendFuncSeparate(-1, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glBlendFuncSeparate(GL_ZERO, -1, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glBlendFuncSeparate(GL_ZERO, GL_ONE, -1, GL_ONE_MINUS_SRC_COLOR);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_SRC_COLOR, -1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

// Rasterization API functions
void cull_face (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if mode is not an accepted value.");
	ctx.glCullFace(-1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void front_face (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if mode is not an accepted value.");
	ctx.glFrontFace(-1);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();
}

void line_width (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_VALUE is generated if width is less than or equal to 0.");
	ctx.glLineWidth(0);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.glLineWidth(-1);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();
}

// Asynchronous queries
void gen_queries (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_VALUE is generated if n is negative.");
	GLuint ids = 0;
	ctx.glGenQueries	(-1, &ids);
	ctx.expectError		(GL_INVALID_VALUE);
	ctx.endSection();
}

void begin_query (NegativeTestContext& ctx)
{
	GLuint ids[3];
	ctx.glGenQueries	(3, ids);

	ctx.beginSection("GL_INVALID_ENUM is generated if target is not one of the accepted tokens.");
	ctx.glBeginQuery	(-1, ids[0]);
	ctx.expectError		(GL_INVALID_ENUM);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glBeginQuery is executed while a query object of the same target is already active.");
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED, ids[0]);
	ctx.expectError		(GL_NO_ERROR);
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED, ids[1]);
	ctx.expectError		(GL_INVALID_OPERATION);
	// \note GL_ANY_SAMPLES_PASSED and GL_ANY_SAMPLES_PASSED_CONSERVATIVE alias to the same target for the purposes of this error.
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, ids[1]);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.glBeginQuery	(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, ids[1]);
	ctx.expectError		(GL_NO_ERROR);
	ctx.glBeginQuery	(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, ids[2]);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.glEndQuery		(GL_ANY_SAMPLES_PASSED);
	ctx.glEndQuery		(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
	ctx.expectError		(GL_NO_ERROR);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_OPERATION is generated if id is 0.");
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED, 0);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_OPERATION is generated if id not a name returned from a previous call to ctx.glGenQueries, or if such a name has since been deleted with ctx.glDeleteQueries.");
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED, -1);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.glDeleteQueries	(1, &ids[2]);
	ctx.expectError		(GL_NO_ERROR);
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED, ids[2]);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_OPERATION is generated if id is the name of an already active query object.");
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED, ids[0]);
	ctx.expectError		(GL_NO_ERROR);
	ctx.glBeginQuery	(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, ids[0]);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_OPERATION is generated if id refers to an existing query object whose type does not does not match target.");
	ctx.glEndQuery		(GL_ANY_SAMPLES_PASSED);
	ctx.expectError		(GL_NO_ERROR);
	ctx.glBeginQuery	(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, ids[0]);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.endSection();

	ctx.glDeleteQueries	(2, &ids[0]);
	ctx.expectError		(GL_NO_ERROR);
}

void end_query (NegativeTestContext& ctx)
{
	GLuint id = 0;
	ctx.glGenQueries	(1, &id);

	ctx.beginSection("GL_INVALID_ENUM is generated if target is not one of the accepted tokens.");
	ctx.glEndQuery		(-1);
	ctx.expectError		(GL_INVALID_ENUM);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_OPERATION is generated if ctx.glEndQuery is executed when a query object of the same target is not active.");
	ctx.glEndQuery		(GL_ANY_SAMPLES_PASSED);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.glBeginQuery	(GL_ANY_SAMPLES_PASSED, id);
	ctx.expectError		(GL_NO_ERROR);
	ctx.glEndQuery		(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
	ctx.expectError		(GL_INVALID_OPERATION);
	ctx.glEndQuery		(GL_ANY_SAMPLES_PASSED);
	ctx.expectError		(GL_NO_ERROR);
	ctx.endSection();

	ctx.glDeleteQueries	(1, &id);
	ctx.expectError		(GL_NO_ERROR);
}

void delete_queries (NegativeTestContext& ctx)
{
	GLuint id = 0;
	ctx.glGenQueries	(1, &id);

	ctx.beginSection("GL_INVALID_VALUE is generated if n is negative.");
	ctx.glDeleteQueries	(-1, &id);
	ctx.expectError		(GL_INVALID_VALUE);
	ctx.endSection();

	ctx.glDeleteQueries	(1, &id);
}

// Sync objects
void fence_sync (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_ENUM is generated if condition is not GL_SYNC_GPU_COMMANDS_COMPLETE.");
	ctx.glFenceSync(-1, 0);
	ctx.expectError(GL_INVALID_ENUM);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_VALUE is generated if flags is not zero.");
	ctx.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0x0010);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();
}

void wait_sync (NegativeTestContext& ctx)
{
	GLsync sync = ctx.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);

	ctx.beginSection("GL_INVALID_VALUE is generated if sync is not the name of a sync object.");
	ctx.glWaitSync(0, 0, GL_TIMEOUT_IGNORED);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_VALUE is generated if flags is not zero.");
	ctx.glWaitSync(sync, 0x0010, GL_TIMEOUT_IGNORED);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_VALUE is generated if timeout is not GL_TIMEOUT_IGNORED.");
	ctx.glWaitSync(sync, 0, 0);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();

	ctx.glDeleteSync(sync);
}

void client_wait_sync (NegativeTestContext& ctx)
{
	GLsync sync = ctx.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);

	ctx.beginSection("GL_INVALID_VALUE is generated if sync is not the name of an existing sync object.");
	ctx.glClientWaitSync (0, 0, 10000);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();

	ctx.beginSection("GL_INVALID_VALUE is generated if flags contains any unsupported flag.");
	ctx.glClientWaitSync(sync, 0x00000004, 10000);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.endSection();

	ctx.glDeleteSync(sync);
}

void delete_sync (NegativeTestContext& ctx)
{
	ctx.beginSection("GL_INVALID_VALUE is generated if sync is neither zero or the name of a sync object.");
	ctx.glDeleteSync((GLsync)1);
	ctx.expectError(GL_INVALID_VALUE);
	ctx.glDeleteSync(0);
	ctx.expectError(GL_NO_ERROR);
	ctx.endSection();
}

std::vector<FunctionContainer> getNegativeFragmentApiTestFunctions ()
{
	FunctionContainer funcs[] =
	{
		{scissor,					"scissor",					"Invalid glScissor() usage"				 },
		{depth_func,				"depth_func",				"Invalid glDepthFunc() usage"			 },
		{viewport,					"viewport",					"Invalid glViewport() usage"			 },
		{stencil_func,				"stencil_func",				"Invalid glStencilFunc() usage"			 },
		{stencil_func_separate,		"stencil_func_separate",	"Invalid glStencilFuncSeparate() usage"	 },
		{stencil_op,				"stencil_op",				"Invalid glStencilOp() usage"			 },
		{stencil_op_separate,		"stencil_op_separate",		"Invalid glStencilOpSeparate() usage"	 },
		{stencil_mask_separate,		"stencil_mask_separate",	"Invalid glStencilMaskSeparate() usage"	 },
		{blend_equation,			"blend_equation",			"Invalid glBlendEquation() usage"		 },
		{blend_equation_separate,	"blend_equation_separate",	"Invalid glBlendEquationSeparate() usage"},
		{blend_func,				"blend_func",				"Invalid glBlendFunc() usage"			 },
		{blend_func_separate,		"blend_func_separate",		"Invalid glBlendFuncSeparate() usage"	 },
		{cull_face,					"cull_face",				"Invalid glCullFace() usage"			 },
		{front_face,				"front_face",				"Invalid glFrontFace() usage"			 },
		{line_width,				"line_width",				"Invalid glLineWidth() usage"			 },
		{gen_queries,				"gen_queries",				"Invalid glGenQueries() usage"			 },
		{begin_query,				"begin_query",				"Invalid glBeginQuery() usage"			 },
		{end_query,					"end_query",				"Invalid glEndQuery() usage"			 },
		{delete_queries,			"delete_queries",			"Invalid glDeleteQueries() usage"		 },
		{fence_sync,				"fence_sync",				"Invalid glFenceSync() usage"			 },
		{wait_sync,					"wait_sync",				"Invalid glWaitSync() usage"			 },
		{client_wait_sync,			"client_wait_sync",			"Invalid glClientWaitSync() usage"		 },
		{delete_sync,				"delete_sync",				"Invalid glDeleteSync() usage"			 },
	};

	return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs));
}

} // NegativeTestShared
} // Functional
} // gles31
} // deqp