/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.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 Float State Query tests.
 *//*--------------------------------------------------------------------*/

#include "es3fFloatStateQueryTests.hpp"
#include "glsStateQueryUtil.hpp"
#include "es3fApiCase.hpp"
#include "gluRenderContext.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuFormatUtil.hpp"
#include "deRandom.hpp"
#include "deMath.h"
#include "glwEnums.hpp"

#include <limits>

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

namespace deqp
{
namespace gles3
{
namespace Functional
{
namespace FloatStateQueryVerifiers
{
namespace
{

const int FLOAT_EXPANSION_E		= 0x03FF; // 10 bits error allowed, requires 22 accurate bits
const int FLOAT_EXPANSION_E_64	= 0x07FF;

GLint64 expandGLFloatToInteger (GLfloat f)
{
	const GLuint64 referenceValue = (GLint64)(f * 2147483647.0);
	return referenceValue;
}

GLint clampToGLint (GLint64 val)
{
	return (GLint)de::clamp<GLint64>(val, std::numeric_limits<GLint>::min(), std::numeric_limits<GLint>::max());
}

} // anonymous

// StateVerifier

class StateVerifier : protected glu::CallLogWrapper
{
public:
						StateVerifier					(const glw::Functions& gl, tcu::TestLog& log, const char* testNamePostfix);
	virtual				~StateVerifier					(); // make GCC happy

	const char*			getTestNamePostfix				(void)																																													const;

	virtual void		verifyFloat						(tcu::TestContext& testCtx, GLenum name, GLfloat reference)																																		= DE_NULL;

	// "Expanded" == Float to int conversion converts from [-1.0 to 1.0] -> [MIN_INT MAX_INT]
	virtual void		verifyFloatExpanded				(tcu::TestContext& testCtx, GLenum name, GLfloat reference)																																		= DE_NULL;
	virtual void		verifyFloat2Expanded			(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1)																												= DE_NULL;
	virtual void		verifyFloat4Color				(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3)																		= DE_NULL;

	// verify that the given range is completely whitin the GL state range
	virtual void		verifyFloatRange				(tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max)																																= DE_NULL;
	virtual void		verifyFloatGreaterOrEqual		(tcu::TestContext& testCtx, GLenum name, GLfloat reference)																																		= DE_NULL;

private:
	const char*	const	m_testNamePostfix;
};

StateVerifier::StateVerifier (const glw::Functions& gl, tcu::TestLog& log, const char* testNamePostfix)
	: glu::CallLogWrapper	(gl, log)
	, m_testNamePostfix		(testNamePostfix)
{
	enableLogging(true);
}

StateVerifier::~StateVerifier ()
{
}

const char* StateVerifier::getTestNamePostfix (void) const
{
	return m_testNamePostfix;
}

// GetBooleanVerifier

class GetBooleanVerifier : public StateVerifier
{
public:
			GetBooleanVerifier		(const glw::Functions& gl, tcu::TestLog& log);
	void	verifyFloat						(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloatExpanded				(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloat2Expanded			(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1);
	void	verifyFloat4Color				(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3);
	void	verifyFloatRange				(tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max);
	void	verifyFloatGreaterOrEqual		(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
};

GetBooleanVerifier::GetBooleanVerifier (const glw::Functions& gl, tcu::TestLog& log)
	: StateVerifier(gl, log, "_getboolean")
{
}

void GetBooleanVerifier::verifyFloat (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLboolean> state;
	glGetBooleanv(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	const GLboolean expectedGLState = reference ? GL_TRUE : GL_FALSE;

	if (state != expectedGLState)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected " << (expectedGLState==GL_TRUE ? "GL_TRUE" : "GL_FALSE") << "; got " << (state == GL_TRUE ? "GL_TRUE" : (state == GL_FALSE ? "GL_FALSE" : "non-boolean")) << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
	}
}

void GetBooleanVerifier::verifyFloatExpanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	DE_ASSERT(de::inRange(reference, -1.0f, 1.0f));
	verifyFloat(testCtx, name, reference);
}

void GetBooleanVerifier::verifyFloat2Expanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1)
{
	DE_ASSERT(de::inRange(reference0, -1.0f, 1.0f));
	DE_ASSERT(de::inRange(reference1, -1.0f, 1.0f));

	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLboolean[2]> boolVector2;
	glGetBooleanv(name, boolVector2);

	if (!boolVector2.verifyValidity(testCtx))
		return;

	const GLboolean referenceAsGLBoolean[] =
	{
		reference0 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
		reference1 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
	};

	if (boolVector2[0] != referenceAsGLBoolean[0] ||
		boolVector2[1] != referenceAsGLBoolean[1])
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected "
			<< (boolVector2[0] ? "GL_TRUE" : "GL_FALSE") << " "
			<< (boolVector2[1] ? "GL_TRUE" : "GL_FALSE") << " "
			<< TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
	}
}

void GetBooleanVerifier::verifyFloat4Color (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLboolean[4]> boolVector4;
	glGetBooleanv(name, boolVector4);

	if (!boolVector4.verifyValidity(testCtx))
		return;

	const GLboolean referenceAsGLBoolean[] =
	{
		reference0 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
		reference1 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
		reference2 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
		reference3 ? GLboolean(GL_TRUE) : GLboolean(GL_FALSE),
	};

	if (boolVector4[0] != referenceAsGLBoolean[0] ||
		boolVector4[1] != referenceAsGLBoolean[1] ||
		boolVector4[2] != referenceAsGLBoolean[2] ||
		boolVector4[3] != referenceAsGLBoolean[3])
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected "
			<< (referenceAsGLBoolean[0] ? "GL_TRUE" : "GL_FALSE") << " "
			<< (referenceAsGLBoolean[1] ? "GL_TRUE" : "GL_FALSE") << " "
			<< (referenceAsGLBoolean[2] ? "GL_TRUE" : "GL_FALSE") << " "
			<< (referenceAsGLBoolean[3] ? "GL_TRUE" : "GL_FALSE") << TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
	}
}

void GetBooleanVerifier::verifyFloatRange (tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLboolean[2]> range;
	glGetBooleanv(name, range);

	if (!range.verifyValidity(testCtx))
		return;

	if (range[0] == GL_FALSE)
	{
		if (max < 0 || min < 0)
		{
			testCtx.getLog() << TestLog::Message << "// ERROR: range [" << min << ", " << max << "] is not in range [" << (range[0] == GL_TRUE ? "GL_TRUE" : (range[0] == GL_FALSE ? "GL_FALSE" : "non-boolean")) << ", " << (range[1] == GL_TRUE ? "GL_TRUE" : (range[1] == GL_FALSE ? "GL_FALSE" : "non-boolean")) << "]"  << TestLog::EndMessage;
			if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean range");
			return;
		}
	}
	if (range[1] == GL_FALSE)
	{
		if (max > 0 || min > 0)
		{
			testCtx.getLog() << TestLog::Message << "// ERROR: range [" << min << ", " << max << "] is not in range [" << (range[0] == GL_TRUE ? "GL_TRUE" : (range[0] == GL_FALSE ? "GL_FALSE" : "non-boolean")) << ", " << (range[1] == GL_TRUE ? "GL_TRUE" : (range[1] == GL_FALSE ? "GL_FALSE" : "non-boolean")) << "]"  << TestLog::EndMessage;
			if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean range");
			return;
		}
	}
}

void GetBooleanVerifier::verifyFloatGreaterOrEqual (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLboolean> state;
	glGetBooleanv(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	if (state == GL_TRUE) // state is non-zero, could be greater than reference (correct)
		return;

	if (state == GL_FALSE) // state is zero
	{
		if (reference > 0) // and reference is greater than zero?
		{
			testCtx.getLog() << TestLog::Message << "// ERROR: expected GL_TRUE" << TestLog::EndMessage;
			if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
				testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
		}
	}
	else
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected GL_TRUE or GL_FALSE" << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid boolean value");
	}
}

//GetIntegerVerifier

class GetIntegerVerifier : public StateVerifier
{
public:
			GetIntegerVerifier		(const glw::Functions& gl, tcu::TestLog& log);
	void	verifyFloat						(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloatExpanded				(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloat2Expanded			(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1);
	void	verifyFloat4Color				(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3);
	void	verifyFloatRange				(tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max);
	void	verifyFloatGreaterOrEqual		(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
};

GetIntegerVerifier::GetIntegerVerifier (const glw::Functions& gl, tcu::TestLog& log)
	: StateVerifier(gl, log, "_getinteger")
{
}

void GetIntegerVerifier::verifyFloat (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	const GLint expectedGLStateMax = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<GLint>(reference);
	const GLint expectedGLStateMin = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<GLint>(reference);

	StateQueryMemoryWriteGuard<GLint> state;
	glGetIntegerv(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	if (state < expectedGLStateMin || state > expectedGLStateMax)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected rounding to the nearest integer, valid range [" << expectedGLStateMin << "," << expectedGLStateMax << "]; got " << state << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetIntegerVerifier::verifyFloatExpanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	DE_ASSERT(de::inRange(reference, -1.0f, 1.0f));

	using tcu::TestLog;
	using tcu::toHex;

	StateQueryMemoryWriteGuard<GLint> state;
	glGetIntegerv(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	const GLint expectedGLStateMax = clampToGLint(expandGLFloatToInteger(reference) + FLOAT_EXPANSION_E);
	const GLint expectedGLStateMin = clampToGLint(expandGLFloatToInteger(reference) - FLOAT_EXPANSION_E);

	if (state < expectedGLStateMin || state > expectedGLStateMax)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected in range [" << toHex(expectedGLStateMin) << "," << toHex(expectedGLStateMax) << "]; got " << toHex((GLint)state) << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetIntegerVerifier::verifyFloat2Expanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1)
{
	DE_ASSERT(de::inRange(reference0, -1.0f, 1.0f));
	DE_ASSERT(de::inRange(reference1, -1.0f, 1.0f));

	using tcu::TestLog;
	using tcu::toHex;

	StateQueryMemoryWriteGuard<GLint[2]> floatVector2;
	glGetIntegerv(name, floatVector2);

	if (!floatVector2.verifyValidity(testCtx))
		return;

	const GLint referenceAsGLintMin[] =
	{
		clampToGLint(expandGLFloatToInteger(reference0) - FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference1) - FLOAT_EXPANSION_E)
	};
	const GLint referenceAsGLintMax[] =
	{
		clampToGLint(expandGLFloatToInteger(reference0) + FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference1) + FLOAT_EXPANSION_E)
	};

	if (floatVector2[0] < referenceAsGLintMin[0] || floatVector2[0] > referenceAsGLintMax[0] ||
		floatVector2[1] < referenceAsGLintMin[1] || floatVector2[1] > referenceAsGLintMax[1])
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: expected in ranges "
			<< "[" << toHex(referenceAsGLintMin[0]) << " " << toHex(referenceAsGLintMax[0]) << "], "
			<< "[" << toHex(referenceAsGLintMin[1]) << " " << toHex(referenceAsGLintMax[1]) << "]"
			<< "; got "
			<< toHex(floatVector2[0]) << ", "
			<< toHex(floatVector2[1]) << " "<< TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetIntegerVerifier::verifyFloat4Color (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3)
{
	using tcu::TestLog;
	using tcu::toHex;

	StateQueryMemoryWriteGuard<GLint[4]> floatVector4;
	glGetIntegerv(name, floatVector4);

	if (!floatVector4.verifyValidity(testCtx))
		return;

	const GLint referenceAsGLintMin[] =
	{
		clampToGLint(expandGLFloatToInteger(reference0) - FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference1) - FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference2) - FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference3) - FLOAT_EXPANSION_E)
	};
	const GLint referenceAsGLintMax[] =
	{
		clampToGLint(expandGLFloatToInteger(reference0) + FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference1) + FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference2) + FLOAT_EXPANSION_E),
		clampToGLint(expandGLFloatToInteger(reference3) + FLOAT_EXPANSION_E)
	};

	if (floatVector4[0] < referenceAsGLintMin[0] || floatVector4[0] > referenceAsGLintMax[0] ||
		floatVector4[1] < referenceAsGLintMin[1] || floatVector4[1] > referenceAsGLintMax[1] ||
		floatVector4[2] < referenceAsGLintMin[2] || floatVector4[2] > referenceAsGLintMax[2] ||
		floatVector4[3] < referenceAsGLintMin[3] || floatVector4[3] > referenceAsGLintMax[3])
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: expected in ranges "
			<< "[" << toHex(referenceAsGLintMin[0]) << " " << toHex(referenceAsGLintMax[0]) << "], "
			<< "[" << toHex(referenceAsGLintMin[1]) << " " << toHex(referenceAsGLintMax[1]) << "], "
			<< "[" << toHex(referenceAsGLintMin[2]) << " " << toHex(referenceAsGLintMax[2]) << "], "
			<< "[" << toHex(referenceAsGLintMin[3]) << " " << toHex(referenceAsGLintMax[3]) << "]"
			<< "; got "
			<< toHex(floatVector4[0]) << ", "
			<< toHex(floatVector4[1]) << ", "
			<< toHex(floatVector4[2]) << ", "
			<< toHex(floatVector4[3]) << " "<< TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetIntegerVerifier::verifyFloatRange (tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint[2]> range;
	glGetIntegerv(name, range);

	if (!range.verifyValidity(testCtx))
		return;

	const GLint testRangeAsGLint[] =
	{
		StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<GLint>(min),
		StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<GLint>(max)
	};

	// check if test range outside of gl state range
	if (testRangeAsGLint[0] < range[0] ||
		testRangeAsGLint[1] > range[1])
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: range ["
			<< testRangeAsGLint[0] << ", "
			<< testRangeAsGLint[1] << "]"
			<< " is not in range ["
			<< range[0] << ", "
			<< range[1] << "]" << TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer range");
	}
}

void GetIntegerVerifier::verifyFloatGreaterOrEqual (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint> state;
	glGetIntegerv(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	const GLint referenceAsInt = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<GLint>(reference);

	if (state < referenceAsInt)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected expected greater or equal to " << referenceAsInt << "; got " << state << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

//GetInteger64Verifier

class GetInteger64Verifier : public StateVerifier
{
public:
			GetInteger64Verifier		(const glw::Functions& gl, tcu::TestLog& log);
	void	verifyFloat						(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloatExpanded				(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloat2Expanded			(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1);
	void	verifyFloat4Color				(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3);
	void	verifyFloatRange				(tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max);
	void	verifyFloatGreaterOrEqual		(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
};

GetInteger64Verifier::GetInteger64Verifier (const glw::Functions& gl, tcu::TestLog& log)
	: StateVerifier(gl, log, "_getinteger64")
{
}

void GetInteger64Verifier::verifyFloat (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	const GLint64 expectedGLStateMax = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<GLint64>(reference);
	const GLint64 expectedGLStateMin = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<GLint64>(reference);

	StateQueryMemoryWriteGuard<GLint64> state;
	glGetInteger64v(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	if (state < expectedGLStateMin || state > expectedGLStateMax)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected rounding to the nearest integer, valid range [" << expectedGLStateMin << "," << expectedGLStateMax << "]; got " << state << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetInteger64Verifier::verifyFloatExpanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	DE_ASSERT(de::inRange(reference, -1.0f, 1.0f));

	using tcu::TestLog;
	using tcu::toHex;

	StateQueryMemoryWriteGuard<GLint64> state;
	glGetInteger64v(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	const GLint64 expectedGLStateMax = expandGLFloatToInteger(reference) + FLOAT_EXPANSION_E_64;
	const GLint64 expectedGLStateMin = expandGLFloatToInteger(reference) - FLOAT_EXPANSION_E_64;

	if (state < expectedGLStateMin || state > expectedGLStateMax)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected in range [" << toHex(expectedGLStateMin) << "," << toHex(expectedGLStateMax) << "]; got " << toHex((GLint64)state) << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetInteger64Verifier::verifyFloat2Expanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1)
{
	DE_ASSERT(de::inRange(reference0, -1.0f, 1.0f));
	DE_ASSERT(de::inRange(reference1, -1.0f, 1.0f));

	using tcu::TestLog;
	using tcu::toHex;

	StateQueryMemoryWriteGuard<GLint64[2]> floatVector2;
	glGetInteger64v(name, floatVector2);

	if (!floatVector2.verifyValidity(testCtx))
		return;

	const GLint64 referenceAsGLintMin[] =
	{
		expandGLFloatToInteger(reference0) - FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference1) - FLOAT_EXPANSION_E_64
	};
	const GLint64 referenceAsGLintMax[] =
	{
		expandGLFloatToInteger(reference0) + FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference1) + FLOAT_EXPANSION_E_64
	};

	if (floatVector2[0] < referenceAsGLintMin[0] || floatVector2[0] > referenceAsGLintMax[0] ||
		floatVector2[1] < referenceAsGLintMin[1] || floatVector2[1] > referenceAsGLintMax[1])
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: expected in ranges "
			<< "[" << toHex(referenceAsGLintMin[0]) << " " << toHex(referenceAsGLintMax[0]) << "], "
			<< "[" << toHex(referenceAsGLintMin[1]) << " " << toHex(referenceAsGLintMax[1]) << "]"
			<< "; got "
			<< toHex(floatVector2[0]) << ", "
			<< toHex(floatVector2[1]) << " "<< TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetInteger64Verifier::verifyFloat4Color (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3)
{
	using tcu::TestLog;
	using tcu::toHex;

	StateQueryMemoryWriteGuard<GLint64[4]> floatVector4;
	glGetInteger64v(name, floatVector4);

	if (!floatVector4.verifyValidity(testCtx))
		return;

	const GLint64 referenceAsGLintMin[] =
	{
		expandGLFloatToInteger(reference0) - FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference1) - FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference2) - FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference3) - FLOAT_EXPANSION_E_64
	};
	const GLint64 referenceAsGLintMax[] =
	{
		expandGLFloatToInteger(reference0) + FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference1) + FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference2) + FLOAT_EXPANSION_E_64,
		expandGLFloatToInteger(reference3) + FLOAT_EXPANSION_E_64
	};

	if (floatVector4[0] < referenceAsGLintMin[0] || floatVector4[0] > referenceAsGLintMax[0] ||
		floatVector4[1] < referenceAsGLintMin[1] || floatVector4[1] > referenceAsGLintMax[1] ||
		floatVector4[2] < referenceAsGLintMin[2] || floatVector4[2] > referenceAsGLintMax[2] ||
		floatVector4[3] < referenceAsGLintMin[3] || floatVector4[3] > referenceAsGLintMax[3])
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: expected in ranges "
			<< "[" << toHex(referenceAsGLintMin[0]) << " " << toHex(referenceAsGLintMax[0]) << "], "
			<< "[" << toHex(referenceAsGLintMin[1]) << " " << toHex(referenceAsGLintMax[1]) << "], "
			<< "[" << toHex(referenceAsGLintMin[2]) << " " << toHex(referenceAsGLintMax[2]) << "], "
			<< "[" << toHex(referenceAsGLintMin[3]) << " " << toHex(referenceAsGLintMax[3]) << "]"
			<< "; got "
			<< toHex(floatVector4[0]) << ", "
			<< toHex(floatVector4[1]) << ", "
			<< toHex(floatVector4[2]) << ", "
			<< toHex(floatVector4[3]) << " "<< TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

void GetInteger64Verifier::verifyFloatRange (tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint64[2]> range;
	glGetInteger64v(name, range);

	if (!range.verifyValidity(testCtx))
		return;

	const GLint64 testRangeAsGLint[] =
	{
		StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<GLint64>(min),
		StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<GLint64>(max)
	};

	// check if test range outside of gl state range
	if (testRangeAsGLint[0] < range[0] ||
		testRangeAsGLint[1] > range[1])
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: range ["
			<< testRangeAsGLint[0] << ", "
			<< testRangeAsGLint[1] << "]"
			<< " is not in range ["
			<< range[0] << ", "
			<< range[1] << "]" << TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer range");
	}
}

void GetInteger64Verifier::verifyFloatGreaterOrEqual (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLint64> state;
	glGetInteger64v(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	const GLint64 referenceAsInt = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<GLint64>(reference);

	if (state < referenceAsInt)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected expected greater or equal to " << referenceAsInt << "; got " << state << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid integer value");
	}
}

//GetFloatVerifier

class GetFloatVerifier : public StateVerifier
{
public:
			GetFloatVerifier			(const glw::Functions& gl, tcu::TestLog& log);
	void	verifyFloat						(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloatExpanded				(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
	void	verifyFloat2Expanded			(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1);
	void	verifyFloat4Color				(tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3);
	void	verifyFloatRange				(tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max);
	void	verifyFloatGreaterOrEqual		(tcu::TestContext& testCtx, GLenum name, GLfloat reference);
};

GetFloatVerifier::GetFloatVerifier (const glw::Functions& gl, tcu::TestLog& log)
	: StateVerifier(gl, log, "_getfloat")
{
}

void GetFloatVerifier::verifyFloat (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat> state;
	glGetFloatv(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

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

void GetFloatVerifier::verifyFloatExpanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	DE_ASSERT(de::inRange(reference, -1.0f, 1.0f));
	verifyFloat(testCtx, name, reference);
}

void GetFloatVerifier::verifyFloat2Expanded (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1)
{
	DE_ASSERT(de::inRange(reference0, -1.0f, 1.0f));
	DE_ASSERT(de::inRange(reference1, -1.0f, 1.0f));

	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[2]> floatVector2;
	glGetFloatv(name, floatVector2);

	if (!floatVector2.verifyValidity(testCtx))
		return;

	if (floatVector2[0] != reference0 ||
		floatVector2[1] != reference1)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected " << reference0 << ", " << reference1 << "; got " << floatVector2[0] << " " << floatVector2[1] << TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid float value");
	}
}

void GetFloatVerifier::verifyFloat4Color (tcu::TestContext& testCtx, GLenum name, GLfloat reference0, GLfloat reference1, GLfloat reference2, GLfloat reference3)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[4]> floatVector4;
	glGetFloatv(name, floatVector4);

	if (!floatVector4.verifyValidity(testCtx))
		return;

	if (floatVector4[0] != reference0 ||
		floatVector4[1] != reference1 ||
		floatVector4[2] != reference2 ||
		floatVector4[3] != reference3)
	{
		testCtx.getLog() << TestLog::Message
			<< "// ERROR: expected "<< reference0 << ", " << reference1 << ", " << reference2 << ", " << reference3
			<< "; got " << floatVector4[0] << ", " << floatVector4[1] << ", " << floatVector4[2] << ", " << floatVector4[3]
			<< TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid float value");
	}
}

void GetFloatVerifier::verifyFloatRange (tcu::TestContext& testCtx, GLenum name, GLfloat min, GLfloat max)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat[2]> floatVector2;
	glGetFloatv(name, floatVector2);

	if (!floatVector2.verifyValidity(testCtx))
		return;

	if (floatVector2[0] > min ||
		floatVector2[1] < max)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected in range [" << min << ", " << max << "]; got [" << floatVector2[0] << " " << floatVector2[1]  << "]" << TestLog::EndMessage;

		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid float range");
	}
}

void GetFloatVerifier::verifyFloatGreaterOrEqual (tcu::TestContext& testCtx, GLenum name, GLfloat reference)
{
	using tcu::TestLog;

	StateQueryMemoryWriteGuard<GLfloat> state;
	glGetFloatv(name, &state);

	if (!state.verifyValidity(testCtx))
		return;

	if (state < reference)
	{
		testCtx.getLog() << TestLog::Message << "// ERROR: expected greater or equal to " << reference << "; got " << state << TestLog::EndMessage;
		if (testCtx.getTestResult() == QP_TEST_RESULT_PASS)
			testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid float value");
	}
}


} // FloatStateQueryVerifiers

namespace
{

using namespace FloatStateQueryVerifiers;

class DepthRangeCase : public ApiCase
{
public:
	DepthRangeCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		m_verifier->verifyFloat2Expanded(m_testCtx, GL_DEPTH_RANGE, 0.0f, 1.0f);
		expectError(GL_NO_ERROR);

		const struct FixedTest
		{
			float n, f;
		} fixedTests[] =
		{
			{ 0.5f, 1.0f },
			{ 0.0f, 0.5f },
			{ 0.0f, 0.0f },
			{ 1.0f, 1.0f }
		};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(fixedTests); ++ndx)
		{
			glDepthRangef(fixedTests[ndx].n, fixedTests[ndx].f);

			m_verifier->verifyFloat2Expanded(m_testCtx, GL_DEPTH_RANGE, fixedTests[ndx].n, fixedTests[ndx].f);
			expectError(GL_NO_ERROR);
		}

		const int numIterations = 120;
		for (int i = 0; i < numIterations; ++i)
		{
			GLfloat n	= rnd.getFloat(0, 1);
			GLfloat f	= rnd.getFloat(0, 1);

			glDepthRangef(n, f);
			m_verifier->verifyFloat2Expanded(m_testCtx, GL_DEPTH_RANGE, n, f);
			expectError(GL_NO_ERROR);
		}
	}
private:
	StateVerifier*	m_verifier;
};

class LineWidthCase : public ApiCase
{
public:
	LineWidthCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		m_verifier->verifyFloat(m_testCtx, GL_LINE_WIDTH, 1.0f);
		expectError(GL_NO_ERROR);

		GLfloat range[2] = {1};
		glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, range);
		expectError(GL_NO_ERROR);

		const int numIterations = 120;
		for (int i = 0; i < numIterations; ++i)
		{
			const GLfloat reference = rnd.getFloat(range[0], range[1]);

			glLineWidth(reference);
			m_verifier->verifyFloat(m_testCtx, GL_LINE_WIDTH, reference);
			expectError(GL_NO_ERROR);
		}
	}
private:
	StateVerifier*	m_verifier;
};

class PolygonOffsetFactorCase : public ApiCase
{
public:
	PolygonOffsetFactorCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		m_verifier->verifyFloat(m_testCtx, GL_POLYGON_OFFSET_FACTOR, 0.0f);
		expectError(GL_NO_ERROR);

		const float fixedTests[] =
		{
			0.0f, 0.5f, -0.5f, 1.5f
		};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(fixedTests); ++ndx)
		{
			glPolygonOffset(fixedTests[ndx], 0);
			m_verifier->verifyFloat(m_testCtx, GL_POLYGON_OFFSET_FACTOR, fixedTests[ndx]);
			expectError(GL_NO_ERROR);
		}

		const int numIterations = 120;
		for (int i = 0; i < numIterations; ++i)
		{
			const GLfloat reference = rnd.getFloat(-64000, 64000);

			glPolygonOffset(reference, 0);
			m_verifier->verifyFloat(m_testCtx, GL_POLYGON_OFFSET_FACTOR, reference);
			expectError(GL_NO_ERROR);
		}
	}
private:
	StateVerifier*	m_verifier;
};

class PolygonOffsetUnitsCase : public ApiCase
{
public:
	PolygonOffsetUnitsCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		m_verifier->verifyFloat(m_testCtx, GL_POLYGON_OFFSET_UNITS, 0.0f);
		expectError(GL_NO_ERROR);

		const float fixedTests[] =
		{
			0.0f, 0.5f, -0.5f, 1.5f
		};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(fixedTests); ++ndx)
		{
			glPolygonOffset(0, fixedTests[ndx]);
			m_verifier->verifyFloat(m_testCtx, GL_POLYGON_OFFSET_UNITS, fixedTests[ndx]);
			expectError(GL_NO_ERROR);
		}

		const int numIterations = 120;
		for (int i = 0; i < numIterations; ++i)
		{
			const GLfloat reference = rnd.getFloat(-64000, 64000);

			glPolygonOffset(0, reference);
			m_verifier->verifyFloat(m_testCtx, GL_POLYGON_OFFSET_UNITS, reference);
			expectError(GL_NO_ERROR);
		}
	}
private:
	StateVerifier*	m_verifier;
};

class SampleCoverageCase : public ApiCase
{
public:
	SampleCoverageCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		m_verifier->verifyFloat(m_testCtx, GL_SAMPLE_COVERAGE_VALUE, 1.0f);
		expectError(GL_NO_ERROR);

		{
			const float fixedTests[] =
			{
				0.0f, 0.5f, 0.45f, 0.55f
			};
			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(fixedTests); ++ndx)
			{
				glSampleCoverage(fixedTests[ndx], GL_FALSE);
				m_verifier->verifyFloat(m_testCtx, GL_SAMPLE_COVERAGE_VALUE, fixedTests[ndx]);
				expectError(GL_NO_ERROR);
			}
		}

		{
			const float clampTests[] =
			{
				-1.0f, -1.5f, 1.45f, 3.55f
			};
			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(clampTests); ++ndx)
			{
				glSampleCoverage(clampTests[ndx], GL_FALSE);
				m_verifier->verifyFloat(m_testCtx, GL_SAMPLE_COVERAGE_VALUE, de::clamp(clampTests[ndx], 0.0f, 1.0f));
				expectError(GL_NO_ERROR);
			}
		}

		{
			const int numIterations = 120;
			for (int i = 0; i < numIterations; ++i)
			{
				GLfloat		reference	= rnd.getFloat(0, 1);
				GLboolean	invert		= rnd.getBool() ? GL_TRUE : GL_FALSE;

				glSampleCoverage(reference, invert);
				m_verifier->verifyFloat(m_testCtx, GL_SAMPLE_COVERAGE_VALUE, reference);
				expectError(GL_NO_ERROR);
			}
		}
	}
private:
	StateVerifier*	m_verifier;
};

class BlendColorCase : public ApiCase
{
public:
	BlendColorCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		m_verifier->verifyFloat4Color(m_testCtx, GL_BLEND_COLOR, 0, 0, 0, 0);
		expectError(GL_NO_ERROR);

		const struct FixedTest
		{
			float r, g, b, a;
		} fixedTests[] =
		{
			{ 0.5f, 1.0f, 0.5f, 1.0f },
			{ 0.0f, 0.5f, 0.0f, 0.5f },
			{ 0.0f, 0.0f, 0.0f, 0.0f },
			{ 1.0f, 1.0f, 1.0f, 1.0f },
		};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(fixedTests); ++ndx)
		{
			glBlendColor(fixedTests[ndx].r, fixedTests[ndx].g, fixedTests[ndx].b, fixedTests[ndx].a);
			m_verifier->verifyFloat4Color(m_testCtx, GL_BLEND_COLOR, fixedTests[ndx].r, fixedTests[ndx].g, fixedTests[ndx].b, fixedTests[ndx].a);
			expectError(GL_NO_ERROR);
		}

		const int numIterations = 120;
		for (int i = 0; i < numIterations; ++i)
		{
			const GLfloat r = rnd.getFloat(0, 1);
			const GLfloat g = rnd.getFloat(0, 1);
			const GLfloat b = rnd.getFloat(0, 1);
			const GLfloat a = rnd.getFloat(0, 1);

			glBlendColor(r, g, b, a);
			m_verifier->verifyFloat4Color(m_testCtx, GL_BLEND_COLOR, r, g, b, a);
			expectError(GL_NO_ERROR);
		}
	}
private:
	StateVerifier*	m_verifier;
};

class ColorClearCase : public ApiCase
{
public:
	ColorClearCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		// \note Initial color clear value check is temorarily removed. (until the framework does not alter it)
		//m_verifier->verifyFloat4Color(m_testCtx, GL_COLOR_CLEAR_VALUE, 0, 0, 0, 0);
		//expectError(GL_NO_ERROR);

		const struct FixedTest
		{
			float r, g, b, a;
		} fixedTests[] =
		{
			{ 0.5f, 1.0f, 0.5f, 1.0f },
			{ 0.0f, 0.5f, 0.0f, 0.5f },
			{ 0.0f, 0.0f, 0.0f, 0.0f },
			{ 1.0f, 1.0f, 1.0f, 1.0f },
		};
		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(fixedTests); ++ndx)
		{
			glClearColor(fixedTests[ndx].r, fixedTests[ndx].g, fixedTests[ndx].b, fixedTests[ndx].a);
			m_verifier->verifyFloat4Color(m_testCtx, GL_COLOR_CLEAR_VALUE, fixedTests[ndx].r, fixedTests[ndx].g, fixedTests[ndx].b, fixedTests[ndx].a);
			expectError(GL_NO_ERROR);
		}

		const int numIterations = 120;
		for (int i = 0; i < numIterations; ++i)
		{
			const GLfloat r = rnd.getFloat(0, 1);
			const GLfloat g = rnd.getFloat(0, 1);
			const GLfloat b = rnd.getFloat(0, 1);
			const GLfloat a = rnd.getFloat(0, 1);

			glClearColor(r, g, b, a);
			m_verifier->verifyFloat4Color(m_testCtx, GL_COLOR_CLEAR_VALUE, r, g, b, a);
			expectError(GL_NO_ERROR);
		}
	}
private:
	StateVerifier*	m_verifier;
};

class DepthClearCase : public ApiCase
{
public:
	DepthClearCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		de::Random rnd(0xabcdef);

		m_verifier->verifyFloatExpanded(m_testCtx, GL_DEPTH_CLEAR_VALUE, 1);
		expectError(GL_NO_ERROR);

		const int numIterations = 120;
		for (int i = 0; i < numIterations; ++i)
		{
			const GLfloat ref = rnd.getFloat(0, 1);

			glClearDepthf(ref);
			m_verifier->verifyFloatExpanded(m_testCtx, GL_DEPTH_CLEAR_VALUE, ref);
			expectError(GL_NO_ERROR);
		}
	}
private:
	StateVerifier*	m_verifier;
};

class MaxTextureLODBiasCase : public ApiCase
{
public:
	MaxTextureLODBiasCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		m_verifier->verifyFloatGreaterOrEqual(m_testCtx, GL_MAX_TEXTURE_LOD_BIAS, 2.0f);
		expectError(GL_NO_ERROR);
	}
private:
	StateVerifier*	m_verifier;
};

class AliasedPointSizeRangeCase : public ApiCase
{
public:
	AliasedPointSizeRangeCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		m_verifier->verifyFloatRange(m_testCtx, GL_ALIASED_POINT_SIZE_RANGE, 1, 1);
		expectError(GL_NO_ERROR);
	}
private:
	StateVerifier*	m_verifier;
};

class AliasedLineWidthRangeCase : public ApiCase
{
public:
	AliasedLineWidthRangeCase (Context& context, StateVerifier* verifier, const char* name, const char* description)
		: ApiCase		(context, name, description)
		, m_verifier	(verifier)
	{
	}

	void test (void)
	{
		m_verifier->verifyFloatRange(m_testCtx, GL_ALIASED_LINE_WIDTH_RANGE, 1, 1);
		expectError(GL_NO_ERROR);
	}
private:
	StateVerifier*	m_verifier;
};

#define FOR_EACH_VERIFIER(VERIFIERS, CODE_BLOCK)												\
	for (int _verifierNdx = 0; _verifierNdx < DE_LENGTH_OF_ARRAY(VERIFIERS); _verifierNdx++)	\
	{																							\
		StateVerifier* verifier = VERIFIERS[_verifierNdx];										\
		CODE_BLOCK;																				\
	}

} // anonymous

FloatStateQueryTests::FloatStateQueryTests (Context& context)
	: TestCaseGroup			(context, "floats", "Float Values")
	, m_verifierBoolean		(DE_NULL)
	, m_verifierInteger		(DE_NULL)
	, m_verifierInteger64	(DE_NULL)
	, m_verifierFloat		(DE_NULL)
{
}

FloatStateQueryTests::~FloatStateQueryTests (void)
{
	deinit();
}

void FloatStateQueryTests::init (void)
{
	DE_ASSERT(m_verifierBoolean == DE_NULL);
	DE_ASSERT(m_verifierInteger == DE_NULL);
	DE_ASSERT(m_verifierInteger64 == DE_NULL);
	DE_ASSERT(m_verifierFloat == DE_NULL);

	m_verifierBoolean		= new GetBooleanVerifier	(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());
	m_verifierInteger		= new GetIntegerVerifier	(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());
	m_verifierInteger64		= new GetInteger64Verifier	(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());
	m_verifierFloat			= new GetFloatVerifier		(m_context.getRenderContext().getFunctions(), m_context.getTestContext().getLog());

	StateVerifier* verifiers[] = {m_verifierBoolean, m_verifierInteger, m_verifierInteger64, m_verifierFloat};

	FOR_EACH_VERIFIER(verifiers, addChild(new DepthRangeCase				(m_context, verifier,	(std::string("depth_range")					+ verifier->getTestNamePostfix()).c_str(),	"DEPTH_RANGE")));
	FOR_EACH_VERIFIER(verifiers, addChild(new LineWidthCase					(m_context, verifier,	(std::string("line_width")					+ verifier->getTestNamePostfix()).c_str(),	"LINE_WIDTH")));
	FOR_EACH_VERIFIER(verifiers, addChild(new PolygonOffsetFactorCase		(m_context, verifier,	(std::string("polygon_offset_factor")		+ verifier->getTestNamePostfix()).c_str(),	"POLYGON_OFFSET_FACTOR")));
	FOR_EACH_VERIFIER(verifiers, addChild(new PolygonOffsetUnitsCase		(m_context, verifier,	(std::string("polygon_offset_units")		+ verifier->getTestNamePostfix()).c_str(),	"POLYGON_OFFSET_UNITS")));
	FOR_EACH_VERIFIER(verifiers, addChild(new SampleCoverageCase			(m_context, verifier,	(std::string("sample_coverage_value")		+ verifier->getTestNamePostfix()).c_str(),	"SAMPLE_COVERAGE_VALUE")));
	FOR_EACH_VERIFIER(verifiers, addChild(new BlendColorCase				(m_context, verifier,	(std::string("blend_color")					+ verifier->getTestNamePostfix()).c_str(),	"BLEND_COLOR")));
	FOR_EACH_VERIFIER(verifiers, addChild(new ColorClearCase				(m_context, verifier,	(std::string("color_clear_value")			+ verifier->getTestNamePostfix()).c_str(),	"COLOR_CLEAR_VALUE")));
	FOR_EACH_VERIFIER(verifiers, addChild(new DepthClearCase				(m_context, verifier,	(std::string("depth_clear_value")			+ verifier->getTestNamePostfix()).c_str(),	"DEPTH_CLEAR_VALUE")));
	FOR_EACH_VERIFIER(verifiers, addChild(new MaxTextureLODBiasCase			(m_context, verifier,	(std::string("max_texture_lod_bias")		+ verifier->getTestNamePostfix()).c_str(),	"MAX_TEXTURE_LOD_BIAS")));
	FOR_EACH_VERIFIER(verifiers, addChild(new AliasedPointSizeRangeCase		(m_context, verifier,	(std::string("aliased_point_size_range")	+ verifier->getTestNamePostfix()).c_str(),	"ALIASED_POINT_SIZE_RANGE")));
	FOR_EACH_VERIFIER(verifiers, addChild(new AliasedLineWidthRangeCase		(m_context, verifier,	(std::string("aliased_line_width_range")	+ verifier->getTestNamePostfix()).c_str(),	"ALIASED_LINE_WIDTH_RANGE")));
}

void FloatStateQueryTests::deinit (void)
{
	if (m_verifierBoolean)
	{
		delete m_verifierBoolean;
		m_verifierBoolean = DE_NULL;
	}
	if (m_verifierInteger)
	{
		delete m_verifierInteger;
		m_verifierInteger = DE_NULL;
	}
	if (m_verifierInteger64)
	{
		delete m_verifierInteger64;
		m_verifierInteger64 = DE_NULL;
	}
	if (m_verifierFloat)
	{
		delete m_verifierFloat;
		m_verifierFloat = DE_NULL;
	}

	this->TestCaseGroup::deinit();
}

} // Functional
} // gles3
} // deqp