#include <string>

#include <GL/glew.h>

#include <glshader.hpp>
#include <gltest.hpp>


GLTEST(2, 0, 640, 480, glshader)
{
    gltest_root("assets/tests");
    Shader::root("assets/shaders");

    // Create.
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/create_noextension"}),
        "Failed to infer type of shader 'tests/create_noextension' of shader program 'tests/create_noextension'; "
        "no file extension."
    )
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/create.unknownextension"}),
        "Failed to infer type of shader 'tests/create.unknownextension' of shader program 'tests/create.unknownextension'; "
        "unknown file extension 'unknownextension'."
    )
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/create_nonexistent.vert"}),
        "Failed to source shader 'tests/create_nonexistent.vert' of shader program 'tests/create_nonexistent.vert'; "
        "could not open file 'assets/shaders/tests/create_nonexistent.vert':\n"
        "No such file or directory"
    )
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/create_bad_compile.vert"}),
        "Failed to compile shader 'tests/create_bad_compile.vert' of shader program 'tests/create_bad_compile.vert':\n"
        "\"assets/shaders/tests/create_bad_compile.vert\":4(5): error: main() must return void\n"
    )
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/create_bad_link.vert"}),
        "Failed to link shader program 'tests/create_bad_link.vert':\n"
        "error: vertex shader does not write to `gl_Position'. \n"
    )

    // Validate.
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/validate_bad_samplers.vert"}).validate(),
        "Failed to validate shader program 'tests/validate_bad_samplers.vert':\n"
        "active samplers with a different type refer to the same texture image unit"
    )

    // Use.
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/use.vert"}).validate(),
        "Failed to validate shader program 'tests/use.vert'; "
        "shader program not current."
    )

    // Version.
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/version_none.vert"}),
        "Failed to source shader 'tests/version_none.vert' of shader program 'tests/version_none.vert'; "
        "found no #version."
    )
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/version_malformed.vert"}),
        "Failed to source shader 'tests/version_malformed.vert' of shader program 'tests/version_malformed.vert'; "
        "malformed #version:\n"
        "assets/shaders/tests/version_malformed.vert:1: #version 1.10"
    )
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/version_repeated.vert"}),
        "Failed to source shader 'tests/version_repeated.vert' of shader program 'tests/version_repeated.vert'; "
        "found repeated #version:\n"
        "assets/shaders/tests/version_repeated.vert:2: #version 110"
    )

    // Defines.
    constexpr auto red = 1.0F;
    Shader::defines({
        {"red", std::to_string(red)},
    });

    // Include.
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/include_malformed_extension.vert"}),
        "Failed to source shader 'tests/include_malformed_extension.vert' of shader program 'tests/include_malformed_extension.vert'; "
        "malformed #extension:\n"
        "assets/shaders/tests/include_malformed_extension.vert:4: #extension extension"
    )
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/include_malformed.vert"}),
        "Failed to source shader 'tests/include_malformed.vert' of shader program 'tests/include_malformed.vert'; "
        "malformed #include:\n"
        "assets/shaders/tests/include_malformed.vert:7: #include \"bad_include_malformed.h\" malformed.h\""
    )
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/include_mismatched_quotes.vert"}),
        "Failed to source shader 'tests/include_mismatched_quotes.vert' of shader program 'tests/include_mismatched_quotes.vert'; "
        "mismatched #include quotes '\"' and '>':\n"
        "assets/shaders/tests/include_mismatched_quotes.vert:7: #include \"bad_include_quotes.h>"
    )
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/include_no_extension.vert"}),
        "Failed to source shader 'tests/include_no_extension.vert' of shader program 'tests/include_no_extension.vert'; "
        "#include found but #extension GL_ARB_shading_language_include not enabled:\n"
        "assets/shaders/tests/include_no_extension.vert:4: #include \"bad_include_noextension.h\""
    )
    GLTEST_EXPECT_EXCEPTION(true,
        Shader({"tests/include_nonexistent.vert"}),
        "Failed to source shader 'tests/include_nonexistent.vert' of shader program 'tests/include_nonexistent.vert'; "
        "could not open file 'assets/shaders/tests/bad_include_nonexistent.h':\n"
        "assets/shaders/tests/include_nonexistent.vert:7: #include \"bad_include_nonexistent.h\":\n"
        "No such file or directory"
    )
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/include_nested_version.vert"}),
        "Failed to source shader 'tests/include_nested_version.vert' of shader program 'tests/include_nested_version.vert'; "
        "found #version in #include:\n"
        "assets/shaders/tests/include_nested_version.h:1: #version 110\n"
        "assets/shaders/tests/include_nested_version.vert:7: #include \"include_nested_version.h\""
    )
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/include_nested_extension.vert"}),
        "Failed to source shader 'tests/include_nested_extension.vert' of shader program 'tests/include_nested_extension.vert'; "
        "found #extension GL_ARB_shading_language_include in #include:\n"
        "assets/shaders/tests/include_nested_extension.h:1: #extension GL_ARB_shading_language_include : require\n"
        "assets/shaders/tests/include_nested_extension.vert:7: #include \"include_nested_extension.h\""
    )

    // Vertex inputs.
    constexpr auto vert_position  = 0;
    constexpr auto vert_tex_coord = 1;
    Shader::verts({
        {"vert_position",  vert_position},
        {"vert_tex_coord", vert_tex_coord},
    });
    GLTEST_EXPECT_EXCEPTION(false,
        Shader({"tests/vert_unset.vert"}),
        "Failed to initialize vertex input 'vert_unset' of shader program 'tests/vert_unset.vert'."
    );

    auto all = Shader({
        "tests/all.vert",
        "tests/all.frag",
    });

    all
        .use()
        .validate();

    constexpr auto size = 0.5F;
    glBegin(GL_TRIANGLE_STRIP);
    glVertex3f(-size, -size, 0.0F);
    glVertex3f(-size, +size, 0.0F);
    glVertex3f(+size, -size, 0.0F);
    glVertex3f(+size, +size, 0.0F);
    glEnd();

    GLTEST_EXPECT_FRAME("frame.data")
}