/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 2.0 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 Fbo state query tests.
 *//*--------------------------------------------------------------------*/

#include "es2fFboStateQueryTests.hpp"
#include "glsStateQueryUtil.hpp"
#include "es2fApiCase.hpp"
#include "gluRenderContext.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuRenderTarget.hpp"
#include "deMath.h"

using namespace glw; // GLint and other GL types
using deqp::gls::StateQueryUtil::StateQueryMemoryWriteGuard;


namespace deqp
{
namespace gles2
{
namespace Functional
{
namespace
{

void checkIntEquals (tcu::TestContext& testCtx, GLint got, GLint expected)
{
	using tcu::TestLog;

	if (got != expected)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: Expected " << expected << "; got " << got << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got invalid value");
	}
}

void checkAttachmentParam(tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLenum target, GLenum attachment, GLenum pname, GLenum reference)
{
	StateQueryMemoryWriteGuard<GLint> state;
	gl.glGetFramebufferAttachmentParameteriv(target, attachment, pname, &state);

	if (state.verifyValidity(testCtx))
		checkIntEquals(testCtx, state, reference);
}

void checkColorAttachmentParam(tcu::TestContext& testCtx, glu::CallLogWrapper& gl, GLenum target, GLenum pname, GLenum reference)
{
	checkAttachmentParam(testCtx, gl, target, GL_COLOR_ATTACHMENT0, pname, reference);
}

class AttachmentObjectCase : public ApiCase
{
public:
	AttachmentObjectCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// texture
		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_2D, textureID);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, DE_NULL);
			expectError(GL_NO_ERROR);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, GL_TEXTURE);
			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, textureID);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		// rb
		{
			GLuint renderbufferID = 0;
			glGenRenderbuffers(1, &renderbufferID);
			glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
			glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 128, 128);
			expectError(GL_NO_ERROR);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbufferID);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, GL_RENDERBUFFER);
			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, renderbufferID);

			glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
			glDeleteRenderbuffers(1, &renderbufferID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentTextureLevelCase : public ApiCase
{
public:
	AttachmentTextureLevelCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		// GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL can only be 0
		{
			GLuint textureID = 0;

			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_2D, textureID);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, DE_NULL);
			expectError(GL_NO_ERROR);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
			expectError(GL_NO_ERROR);

			checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, 0);

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

class AttachmentTextureCubeMapFaceCase : public ApiCase
{
public:
	AttachmentTextureCubeMapFaceCase (Context& context, const char* name, const char* description)
		: ApiCase(context, name, description)
	{
	}

	void test (void)
	{
		GLuint framebufferID = 0;
		glGenFramebuffers(1, &framebufferID);
		glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
		expectError(GL_NO_ERROR);

		{
			GLuint textureID = 0;
			glGenTextures(1, &textureID);
			glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
			expectError(GL_NO_ERROR);

			const GLenum faces[] =
			{
				GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
				GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
				GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
			};

			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(faces); ++ndx)
				glTexImage2D(faces[ndx], 0, GL_RGB, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, DE_NULL);
			expectError(GL_NO_ERROR);

			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(faces); ++ndx)
			{
				glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, faces[ndx], textureID, 0);
				checkColorAttachmentParam(m_testCtx, *this, GL_FRAMEBUFFER, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, faces[ndx]);
			}

			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
			glDeleteTextures(1, &textureID);
		}

		glDeleteFramebuffers(1, &framebufferID);
		expectError(GL_NO_ERROR);
	}
};

} // anonymous


FboStateQueryTests::FboStateQueryTests (Context& context)
	: TestCaseGroup(context, "fbo", "Fbo State Query tests")
{
}

void FboStateQueryTests::init (void)
{
	addChild(new AttachmentObjectCase				(m_context, "framebuffer_attachment_object",				"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE and FRAMEBUFFER_ATTACHMENT_OBJECT_NAME"));
	addChild(new AttachmentTextureLevelCase			(m_context, "framebuffer_attachment_texture_level",			"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL"));
	addChild(new AttachmentTextureCubeMapFaceCase	(m_context, "framebuffer_attachment_texture_cube_map_face",	"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE"));
}

} // Functional
} // gles2
} // deqp