/*-------------------------------------------------------------------------
 * 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 Shader state query tests
 *//*--------------------------------------------------------------------*/

#include "es31fShaderStateQueryTests.hpp"
#include "tcuTestLog.hpp"
#include "tcuStringTemplate.hpp"
#include "gluShaderProgram.hpp"
#include "gluRenderContext.hpp"
#include "gluContextInfo.hpp"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"

namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace
{

class SamplerTypeCase : public TestCase
{
public:
					SamplerTypeCase	(Context& ctx, const char* name, const char* desc);

private:
	IterateResult	iterate			(void);
};

SamplerTypeCase::SamplerTypeCase (Context& ctx, const char* name, const char* desc)
	: TestCase(ctx, name, desc)
{
}

SamplerTypeCase::IterateResult SamplerTypeCase::iterate (void)
{
	static const struct SamplerType
	{
		glw::GLenum	glType;
		const char*	typeStr;
		const char*	samplePosStr;
		bool		isArray;
	} samplerTypes[] =
	{
		{ GL_SAMPLER_2D_MULTISAMPLE,						"sampler2DMS",			"ivec2(gl_FragCoord.xy)",	false	},
		{ GL_INT_SAMPLER_2D_MULTISAMPLE,					"isampler2DMS",			"ivec2(gl_FragCoord.xy)",	false	},
		{ GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE,			"usampler2DMS",			"ivec2(gl_FragCoord.xy)",	false	},
		{ GL_SAMPLER_2D_MULTISAMPLE_ARRAY,					"sampler2DMSArray",		"ivec3(gl_FragCoord.xyz)",	true	},
		{ GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,				"isampler2DMSArray",	"ivec3(gl_FragCoord.xyz)",	true	},
		{ GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,		"usampler2DMSArray",	"ivec3(gl_FragCoord.xyz)",	true	},
	};

	static const char* const	vertexSource			=	"#version 310 es\n"
															"in highp vec4 a_position;\n"
															"void main(void)\n"
															"{\n"
															"	gl_Position = a_position;\n"
															"}\n";
	static const char* const	fragmentSourceTemplate	=	"#version 310 es\n"
															"${EXTENSIONSTATEMENT}"
															"uniform highp ${SAMPLERTYPE} u_sampler;\n"
															"layout(location = 0) out highp vec4 dEQP_FragColor;\n"
															"void main(void)\n"
															"{\n"
															"	dEQP_FragColor = vec4(texelFetch(u_sampler, ${POSITION}, 0));\n"
															"}\n";
	const bool					textureArraySupported	=	m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array");
	bool						error					=	false;

	for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(samplerTypes); ++typeNdx)
	{
		const tcu::ScopedLogSection section(m_testCtx.getLog(), std::string(samplerTypes[typeNdx].typeStr), std::string() + "Sampler type " + samplerTypes[typeNdx].typeStr);

		if (samplerTypes[typeNdx].isArray && !textureArraySupported)
		{
			m_testCtx.getLog() << tcu::TestLog::Message << "GL_OES_texture_storage_multisample_2d_array not supported, skipping type " << samplerTypes[typeNdx].typeStr << tcu::TestLog::EndMessage;
		}
		else
		{
			std::map<std::string, std::string>	shaderArgs;
			shaderArgs["SAMPLERTYPE"]			= samplerTypes[typeNdx].typeStr;
			shaderArgs["POSITION"]				= samplerTypes[typeNdx].samplePosStr;
			shaderArgs["EXTENSIONSTATEMENT"]	= (samplerTypes[typeNdx].isArray) ? ("#extension GL_OES_texture_storage_multisample_2d_array : require\n") : ("");

			{
				const std::string			fragmentSource	= tcu::StringTemplate(fragmentSourceTemplate).specialize(shaderArgs);
				const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
				glu::ShaderProgram			program			(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexSource) << glu::FragmentSource(fragmentSource));

				m_testCtx.getLog() << tcu::TestLog::Message << "Building program with uniform sampler of type " << samplerTypes[typeNdx].typeStr << tcu::TestLog::EndMessage;

				if (!program.isOk())
				{
					m_testCtx.getLog() << program;
					throw tcu::TestError("could not build shader");
				}

				// only one uniform -- uniform at index 0
				{
					int uniforms = 0;
					gl.getProgramiv(program.getProgram(), GL_ACTIVE_UNIFORMS, &uniforms);

					if (uniforms != 1)
						throw tcu::TestError("Unexpected GL_ACTIVE_UNIFORMS, expected 1");
				}

				m_testCtx.getLog() << tcu::TestLog::Message << "Verifying uniform type." << tcu::TestLog::EndMessage;

				// check type
				{
					const glw::GLuint	uniformIndex	= 0;
					glw::GLint			type			= 0;

					gl.getActiveUniformsiv(program.getProgram(), 1, &uniformIndex, GL_UNIFORM_TYPE, &type);

					if (type != (glw::GLint)samplerTypes[typeNdx].glType)
					{
						m_testCtx.getLog() << tcu::TestLog::Message << "Invalid type, expected " << samplerTypes[typeNdx].glType << ", got " << type << tcu::TestLog::EndMessage;
						error = true;
					}
				}

				GLU_EXPECT_NO_ERROR(gl.getError(), "");
			}
		}
	}

	if (!error)
		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	else
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid uniform type");

	return STOP;
}

} // anonymous

ShaderStateQueryTests::ShaderStateQueryTests (Context& context)
	: TestCaseGroup(context, "shader", "Shader state query tests")
{
}

ShaderStateQueryTests::~ShaderStateQueryTests (void)
{
}

void ShaderStateQueryTests::init (void)
{
	// sampler type query
	addChild(new SamplerTypeCase(m_context, "sampler_type", "Sampler type cases"));
}

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