import { modernizeShader } from "../../Source/Cesium.js";

describe("Renderer/modernizeShader", function () {
  it("adds version string", function () {
    var simple =
      "#define OUTPUT_DECLARATION \n" + "void main() \n" + "{ \n" + "} \n";
    var output = modernizeShader(simple, true);
    var expected = "#version 300 es\n#define WEBGL_2";
    expect(output.startsWith(expected)).toBe(true);
  });

  it("replace existing version string", function () {
    var simple =
      "#version 200 es" +
      "#define OUTPUT_DECLARATION \n" +
      "void main() \n" +
      "{ \n" +
      "} \n";
    var output = modernizeShader(simple, true);
    var expected = "#version 300 es\n#define WEBGL_2";
    expect(output.startsWith(expected)).toBe(true);
  });

  it("removes extensions", function () {
    var extensions =
      "#define OUTPUT_DECLARATION \n" +
      "#extension GL_EXT_draw_buffers : enable \n" +
      "void main() \n" +
      "{ \n" +
      "} \n";
    var output = modernizeShader(extensions, true);
    var notExpected = "#extension GL_EXT_draw_buffers : enable \n";
    expect(output).not.toContain(notExpected);
  });

  it("replace extension macro check with WEBGL_2", function () {
    var extensions =
      "#define OUTPUT_DECLARATION \n" +
      "#extension GL_EXT_draw_buffers : enable \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef GL_EXT_draw_buffers\n" +
      "    #endif //GL_EXT_draw_buffers\n" +
      "    #if defined(GL_EXT_draw_buffers)\n" +
      "    #endif //GL_EXT_draw_buffers\n" +
      "} \n";
    var output = modernizeShader(extensions, true);
    var notExpected = "#extension GL_EXT_draw_buffers : enable \n";
    var expected =
      "    #ifdef WEBGL_2\n" +
      "    #endif //WEBGL_2\n" +
      "    #if defined(WEBGL_2)\n" +
      "    #endif //WEBGL_2\n";
    expect(output).not.toContain(notExpected);
    expect(output).toContain(expected);
  });

  it("throws exception if no output declaration", function () {
    var noOutputDeclaration = "void main() \n" + "{ \n" + "} \n";
    var runFunc = function () {
      modernizeShader(noOutputDeclaration, true);
    };
    expect(runFunc).toThrowDeveloperError();
  });

  it("creates layout qualifier for gl_FragColor", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "void main() \n" +
      "{ \n" +
      "    gl_FragColor = vec4(0.0); \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected = "layout(location = 0) out vec4 czm_fragColor;";
    expect(output).toContain(expected);
  });

  it("creates layout qualifier for gl_FragData", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "void main() \n" +
      "{ \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected = "layout(location = 0) out vec4 czm_out0;";
    expect(output).toContain(expected);
  });

  it("creates layout qualifier for MRT", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#extension GL_EXT_draw_buffers : enable \n" +
      "void main() \n" +
      "{ \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    gl_FragData[1] = vec4(1.0); \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected0 = "layout(location = 0) out vec4 czm_out0;";
    var expected1 = "layout(location = 1) out vec4 czm_out1;";
    expect(output).toContain(expected0);
    expect(output).toContain(expected1);
  });

  it("does not create layout qualifier for reserved word lookalike variables", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "uniform sampler2D example; \n" +
      "void main() \n" +
      "{ \n" +
      "    vec4 gl_FragData_ = vec4(0.0); \n" +
      "    vec4 a_gl_FragData = vec4(0.0); \n" +
      "    vec2 thisIsNotAGoodNameForAtexture2D = vec2(0.0); \n" +
      "    vec4 gl_FragColor = texture2D(example, thisIsNotAGoodNameForAtexture2D); \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expectedBadName = "vec2 thisIsNotAGoodNameForAtexture2D";
    var expectedVariable = "vec4 a_gl_FragData = vec4(0.0);";
    var expectedTextureCall =
      "texture(example, thisIsNotAGoodNameForAtexture2D)";
    var notExpectedLayout = "layout(location = 0) out czm_out";
    expect(output).toContain(expectedBadName);
    expect(output).toContain(expectedVariable);
    expect(output).toContain(expectedTextureCall);
    expect(output).not.toContain(notExpectedLayout);
  });

  it("creates layout qualifier with swizzle", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#extension GL_EXT_draw_buffers : enable \n" +
      "void main() \n" +
      "{ \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    gl_FragData[1].xyz = vec3(1.0); \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected0 = "layout(location = 0) out vec4 czm_out0;";
    var expected1 = "layout(location = 1) out vec4 czm_out1;";
    expect(output).toContain(expected0);
    expect(output).toContain(expected1);
  });

  it("removes old functions/variables from fragment shader", function () {
    var old_fragment =
      "#define OUTPUT_DECLARATION \n" +
      "#extension GL_EXT_draw_buffers : enable \n" +
      "uniform sampler2D example; \n" +
      "uniform sampler2D exampleCube; \n" +
      "uniform sampler2D example3D; \n" +
      "varying vec2 v_textureCoordinates; \n" +
      "void main() \n" +
      "{ \n" +
      "    gl_FragData[0] = texture2D(example, v_textureCoordinates); \n" +
      "    gl_FragData[1] = textureCube(exampleCube, v_textureCoordinates); \n" +
      "    gl_FragData[2] = texture3D(example3D, v_textureCoordinates); \n" +
      "    gl_FragDepthEXT = 0.0; \n" +
      "} \n";

    var output = modernizeShader(old_fragment, true);

    var expectedDepth = "gl_FragDepth = 0.0;";
    var expectedTexture2D = "texture(example, v_textureCoordinates);";
    var expectedTextureCube = "texture(exampleCube, v_textureCoordinates);";
    var expectedTexture3D = "texture(example3D, v_textureCoordinates);";
    var expectedIn = "in vec2 v_textureCoordinates;";

    var notExpectedDepth = "gl_FragDepthEXT = 0.0;";
    var notExpectedTexture2D = "texture2D(example, v_textureCoordinates);";
    var notExpectedTextureCube =
      "textureCube(exampleCube, v_textureCoordinates);";
    var notExpectedTexture3D = "texture3D(example3D, v_textureCoordinates);";
    var notExpectedVarying = "varying vec2 v_textureCoordinates;";

    expect(output).toContain(expectedDepth);
    expect(output).toContain(expectedTexture2D);
    expect(output).toContain(expectedTextureCube);
    expect(output).toContain(expectedTexture3D);
    expect(output).toContain(expectedIn);

    expect(output).not.toContain(notExpectedDepth);
    expect(output).not.toContain(notExpectedTexture2D);
    expect(output).not.toContain(notExpectedTextureCube);
    expect(output).not.toContain(notExpectedTexture3D);
    expect(output).not.toContain(notExpectedVarying);
  });

  it("removes old functions/variables from vertex shader", function () {
    var old_vertex =
      "#define OUTPUT_DECLARATION \n" +
      "attribute vec4 position; \n" +
      "varying vec4 varyingVariable; \n" +
      "void main() \n" +
      "{ \n" +
      "    gl_Position = position; \n" +
      "    varyingVariable = position.wzyx; \n" +
      "} \n";

    var output = modernizeShader(old_vertex, false);

    var expectedOut = "out vec4 varyingVariable;";
    var expectedIn = "in vec4 position;";

    var notExpectedAttribute = "attribute vec4 varyingVariable;";
    var notExpectedVarying = "varying vec2 varyingVariable;";

    expect(output).toContain(expectedOut);
    expect(output).toContain(expectedIn);

    expect(output).not.toContain(notExpectedAttribute);
    expect(output).not.toContain(notExpectedVarying);
  });

  it("creates single layout qualifier under single branch, single condition", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#define EXAMPLE_BRANCH \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    #endif //EXAMPLE_BRANCH \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif";
    expect(output).toContain(expected);
  });

  it("creates multiple layout qualifiers under single branch, single condition", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#define EXAMPLE_BRANCH \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    gl_FragData[1] = vec4(1.0); \n" +
      "    #endif //EXAMPLE_BRANCH \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected0 =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif";
    var expected1 =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 1) out vec4 czm_out1;\n#endif";
    expect(output).toContain(expected0);
    expect(output).toContain(expected1);
  });

  it("creates multiple layout qualifiers under multiple branches, single condition (cancels)", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#define EXAMPLE_BRANCH \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    gl_FragData[1] = vec4(1.0); \n" +
      "    #endif //EXAMPLE_BRANCH \n" +
      "    #ifndef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    gl_FragData[1] = vec4(1.0); \n" +
      "    #endif //!EXAMPLE_BRANCH \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var notExpected =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif";
    expect(output).not.toContain(notExpected);
  });

  it("creates single layout qualifier under multiple branches, multiple conditions (cancels)", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#define EXAMPLE_BRANCH \n" +
      "#define EXAMPLE_BRANCH1 \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    #endif //EXAMPLE_BRANCH \n" +
      "    #ifdef EXAMPLE_BRANCH1 \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    #endif //EXAMPLE_BRANCH1 \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var notExpected =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif";
    expect(output).not.toContain(notExpected);
  });

  it("creates multiple layout qualifiers under multiple branches, multiple conditions (cascades)", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#define EXAMPLE_BRANCH \n" +
      "#define EXAMPLE_BRANCH1 \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    #ifdef EXAMPLE_BRANCH1 \n" +
      "    gl_FragData[1] = vec4(1.0); \n" +
      "    #endif //EXAMPLE_BRANCH1 \n" +
      "    #endif //EXAMPLE_BRANCH \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected0 =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif";
    var expected1 = /#ifdef (EXAMPLE_BRANCH|EXAMPLE_BRANCH1)\s*\n\s*#ifdef (EXAMPLE_BRANCH1|EXAMPLE_BRANCH)\s*\n\s*layout\(location = 1\) out vec4 czm_out1;/g;
    var containsExpected0 = expected1.test(output);
    expect(output).toContain(expected0);
    expect(containsExpected0).toBe(true);
  });

  it("creates single layout qualifier under multiple branches, single condition (else)", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#define EXAMPLE_BRANCH \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    #else \n" +
      "    gl_FragData[0] = vec4(1.0); \n" +
      "    #endif //EXAMPLE_BRANCH \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var notExpected =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif";
    expect(output).not.toContain(notExpected);
  });

  it("creates branched layout qualifiers for gl_FragColor and gl_FragData", function () {
    var noQualifiers =
      "#define OUTPUT_DECLARATION \n" +
      "#define EXAMPLE_BRANCH \n" +
      "void main() \n" +
      "{ \n" +
      "    #ifdef EXAMPLE_BRANCH \n" +
      "    gl_FragData[0] = vec4(0.0); \n" +
      "    #else \n" +
      "    gl_FragColor = vec4(1.0); \n" +
      "    #endif //EXAMPLE_BRANCH \n" +
      "} \n";
    var output = modernizeShader(noQualifiers, true);
    var expected0 =
      "#ifdef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_out0;\n#endif";
    var expected1 =
      "#ifndef EXAMPLE_BRANCH \nlayout(location = 0) out vec4 czm_fragColor;\n#endif";
    expect(output).toContain(expected0);
    expect(output).toContain(expected1);
  });
});
