/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.1 Module
 * -------------------------------------------------
 *
 * Copyright 2016 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 Precise Tests
 *//*--------------------------------------------------------------------*/

#include "es31fNegativePreciseTests.hpp"

#include "gluShaderProgram.hpp"
#include "glwEnums.hpp"

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

enum TestPrecise
{
    TEST_PRECISE_AS_VARIABLE_NAME = 0,
    TEST_PRECISE_AS_FUNCTION_NAME,
    TEST_PRECISE_AS_ARGUMENT_NAME,
    TEST_PRECISE_AS_MACRO_NAME,
    TEST_PRECISE_MACRO_AND_VARIABLE,
    TEST_PRECISE_MACRO_AND_FUNCTION,
    TEST_PRECISE_MACRO_AND_ARGUMENT,

    TEST_PRECISE_LAST
};

static const glu::ShaderType s_shaderTypes[] = {glu::SHADERTYPE_VERTEX,
                                                glu::SHADERTYPE_FRAGMENT,
                                                glu::SHADERTYPE_GEOMETRY,
                                                glu::SHADERTYPE_COMPUTE,
                                                glu::SHADERTYPE_TESSELLATION_CONTROL,
                                                glu::SHADERTYPE_TESSELLATION_EVALUATION};

std::string generateShaderSource(NegativeTestContext &ctx, glu::ShaderType shaderType, TestPrecise test)
{
    const bool supportsES32        = contextSupports(ctx.getRenderContext().getType(), glu::ApiType::es(3, 2));
    const glu::GLSLVersion version = supportsES32 ? glu::GLSL_VERSION_320_ES : glu::GLSL_VERSION_310_ES;
    std::ostringstream source;

    source << glu::getGLSLVersionDeclaration(version) << "\n"
           << (supportsES32 ? "" : "#extension GL_EXT_gpu_shader5 : enable\n");

    switch (test)
    {
    case TEST_PRECISE_AS_MACRO_NAME:
        source << "#define precise 0\n";
        break;

    case TEST_PRECISE_MACRO_AND_VARIABLE:
    case TEST_PRECISE_MACRO_AND_FUNCTION:
    case TEST_PRECISE_MACRO_AND_ARGUMENT:
        source << "#define precise aName\n";
        break;
    default:
        break;
    }

    switch (shaderType)
    {
    case glu::SHADERTYPE_GEOMETRY:
        source << (supportsES32 ? "" : "#extension GL_EXT_geometry_shader : enable\n")
               << "layout(max_vertices = 5) out;\n";
        break;

    case glu::SHADERTYPE_TESSELLATION_CONTROL:
        source << (supportsES32 ? "" : "#extension GL_EXT_tessellation_shader : enable\n")
               << "layout(vertices = 3) out;\n";
        break;

    case glu::SHADERTYPE_TESSELLATION_EVALUATION:
        source << (supportsES32 ? "" : "#extension GL_EXT_tessellation_shader : enable\n")
               << "layout(triangles, equal_spacing, cw) in;\n";
        break;

    default:
        break;
    }

    switch (test)
    {
    case TEST_PRECISE_AS_FUNCTION_NAME:
    case TEST_PRECISE_MACRO_AND_FUNCTION:
        source << "\n"
               << "void precise()\n"
               << "{\n"
               << "}\n";
        break;

    case TEST_PRECISE_AS_ARGUMENT_NAME:
    case TEST_PRECISE_MACRO_AND_ARGUMENT:
        source << "\n"
               << "void example(int precise)\n"
               << "{\n"
               << "}\n";
        break;

    default:
        break;
    }

    source << "void main()\n"
           << "{\n";

    switch (test)
    {
    case TEST_PRECISE_AS_VARIABLE_NAME:
    case TEST_PRECISE_MACRO_AND_VARIABLE:
        source << "    int precise = 1;\n";
        break;
    case TEST_PRECISE_AS_MACRO_NAME:
        source << "    int number = precise;\n";
        break;
    default:
        break;
    }

    source << "}\n";

    return source.str();
}

void generateAndVerifyShader(NegativeTestContext &ctx, glu::ShaderType shaderType, TestPrecise test)
{
    glu::Shader shader(ctx.getRenderContext(), shaderType);
    std::string shaderSource = generateShaderSource(ctx, shaderType, test);
    const char *const source = shaderSource.c_str();
    const int length         = (int)shaderSource.size();

    shader.setSources(1, &source, &length);
    shader.compile();

    ctx.getLog() << shader;

    if (shader.getCompileStatus())
        ctx.fail("Shader was not expected to compile.");
}

static bool checkSupport(NegativeTestContext &ctx)
{
    return ctx.isExtensionSupported("GL_EXT_gpu_shader5") ||
           contextSupports(ctx.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
           contextSupports(ctx.getRenderContext().getType(), glu::ApiType::core(4, 5));
}

void precise_as_variable_name(NegativeTestContext &ctx)
{
    TCU_CHECK_AND_THROW(
        NotSupportedError, checkSupport(ctx),
        "This test requires support for the extension GL_EXT_gpu_shader5 or context version 3.2 or higher.");

    ctx.beginSection("Test that precise cannot be used as a variable name.");
    for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_shaderTypes); ++ndx)
    {
        if (ctx.isShaderSupported(s_shaderTypes[ndx]))
            generateAndVerifyShader(ctx, s_shaderTypes[ndx], TEST_PRECISE_AS_VARIABLE_NAME);
    }
    ctx.endSection();
}

void precise_as_function_name(NegativeTestContext &ctx)
{
    TCU_CHECK_AND_THROW(
        NotSupportedError, checkSupport(ctx),
        "This test requires support for the extension GL_EXT_gpu_shader5 or context version 3.2 or higher.");

    ctx.beginSection("Test that precise cannot be used as a function name.");
    for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_shaderTypes); ++ndx)
    {
        if (ctx.isShaderSupported(s_shaderTypes[ndx]))
            generateAndVerifyShader(ctx, s_shaderTypes[ndx], TEST_PRECISE_AS_FUNCTION_NAME);
    }
    ctx.endSection();
}

void precise_as_function_argument(NegativeTestContext &ctx)
{
    TCU_CHECK_AND_THROW(
        NotSupportedError, checkSupport(ctx),
        "This test requires support for the extension GL_EXT_gpu_shader5 or context version 3.2 or higher.");

    ctx.beginSection("Test that precise cannot be used as a argument name.");
    for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_shaderTypes); ++ndx)
    {
        if (ctx.isShaderSupported(s_shaderTypes[ndx]))
            generateAndVerifyShader(ctx, s_shaderTypes[ndx], TEST_PRECISE_AS_ARGUMENT_NAME);
    }
    ctx.endSection();
}

} // namespace

std::vector<FunctionContainer> getNegativePreciseTestFunctions(void)
{
    const FunctionContainer funcs[] = {
        {precise_as_variable_name, "precise_as_variable_name", "Test precise keyword as variable name."},
        {precise_as_function_name, "precise_as_function_name", "Test precise keyword as function name."},
        {precise_as_function_argument, "precise_as_function_argument", "Test precise keyword as argument name."},
    };

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

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