import { ShaderCache } from "../../Source/Cesium.js";
import createContext from "../createContext.js";

describe(
  "Renderer/ShaderCache",
  function () {
    var context;

    beforeAll(function () {
      context = createContext();
    });

    afterAll(function () {
      context.destroyForSpecs();
    });

    it("adds and removes", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);

      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });

      expect(sp._cachedShader.count).toEqual(1);
      expect(cache.numberOfShaders).toEqual(1);

      cache.releaseShaderProgram(sp);
      expect(sp.isDestroyed()).toEqual(false);
      expect(cache.numberOfShaders).toEqual(1);

      cache.destroyReleasedShaderPrograms();
      expect(sp.isDestroyed()).toEqual(true);
      expect(cache.numberOfShaders).toEqual(0);

      cache.destroy();
    });

    it("adds and removes 2", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);
      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });
      expect(sp._cachedShader.count).toEqual(1);

      sp.destroy();
      expect(sp.isDestroyed()).toEqual(false);

      cache.destroyReleasedShaderPrograms();
      expect(sp.isDestroyed()).toEqual(true);

      cache.destroy();
    });

    it("has a cache hit", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);
      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });
      var sp2 = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });

      expect(sp).toBe(sp2);
      expect(sp._cachedShader.count).toEqual(2);
      expect(cache.numberOfShaders).toEqual(1);

      sp.destroy();
      sp2.destroy();
      cache.destroyReleasedShaderPrograms();

      expect(sp.isDestroyed()).toEqual(true);
      expect(cache.numberOfShaders).toEqual(0);

      cache.destroy();
    });

    it("replaces a shader program", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";
      var fs2 = "void main() { gl_FragColor = vec4(0.5); }";
      var attributeLocations = {
        position: 0,
      };

      var cache = new ShaderCache(context);
      var sp = cache.replaceShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: attributeLocations,
      });

      var sp2 = cache.replaceShaderProgram({
        shaderProgram: sp,
        vertexShaderSource: vs,
        fragmentShaderSource: fs2,
        attributeLocations: attributeLocations,
      });

      expect(sp._cachedShader.count).toEqual(0);
      expect(sp2._cachedShader.count).toEqual(1);

      cache.destroy();
    });

    it("avoids thrashing", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);
      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });
      sp.destroy();
      var sp2 = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      }); // still cache hit

      cache.destroyReleasedShaderPrograms(); // does not destroy
      expect(sp.isDestroyed()).toEqual(false);
      expect(sp2.isDestroyed()).toEqual(false);

      sp2.destroy();
      cache.destroyReleasedShaderPrograms(); // destroys

      expect(sp.isDestroyed()).toEqual(true);
      expect(sp2.isDestroyed()).toEqual(true);

      cache.destroy();
    });

    it("create derived shader program", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);
      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });

      var keyword = "derived";
      var spDerived = cache.getDerivedShaderProgram(sp, keyword);
      expect(spDerived).not.toBeDefined();

      var fsDerived = "void main() { gl_FragColor = vec4(vec3(1.0), 0.5); }";
      spDerived = cache.createDerivedShaderProgram(sp, keyword, {
        vertexShaderSource: vs,
        fragmentShaderSource: fsDerived,
        attributeLocations: {
          position: 0,
        },
      });
      expect(spDerived).toBeDefined();

      cache.destroy();
    });

    it("replaces derived shader program", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);
      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });
      var derivedKeywords = sp._cachedShader.derivedKeywords;

      var keyword = "derived";
      var fsDerived = "void main() { gl_FragColor = vec4(vec3(1.0), 0.5); }";
      var spDerived = cache.replaceDerivedShaderProgram(sp, keyword, {
        vertexShaderSource: vs,
        fragmentShaderSource: fsDerived,
        attributeLocations: {
          position: 0,
        },
      });

      expect(spDerived).toBeDefined();
      expect(derivedKeywords.length).toBe(1);

      var fsDerived2 = "void main() { gl_FragColor = vec4(vec3(0.5), 0.5); }";
      var spDerived2 = cache.replaceDerivedShaderProgram(sp, keyword, {
        vertexShaderSource: vs,
        fragmentShaderSource: fsDerived2,
        attributeLocations: {
          position: 0,
        },
      });

      expect(spDerived.isDestroyed()).toBe(true);
      expect(spDerived2.isDestroyed()).toBe(false);
      expect(derivedKeywords.length).toBe(1);

      cache.destroy();
    });

    it("destroying a shader program destroys its derived shaders", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);
      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });

      var keyword = "derived";
      var fsDerived = "void main() { gl_FragColor = vec4(vec3(1.0), 0.5); }";
      var spDerived = cache.createDerivedShaderProgram(sp, keyword, {
        vertexShaderSource: vs,
        fragmentShaderSource: fsDerived,
        attributeLocations: {
          position: 0,
        },
      });
      expect(spDerived).toBeDefined();

      sp.destroy();
      cache.destroyReleasedShaderPrograms();

      expect(sp.isDestroyed()).toEqual(true);
      expect(spDerived.isDestroyed()).toEqual(true);

      cache.destroy();
    });

    it("is destroyed", function () {
      var vs =
        "attribute vec4 position; void main() { gl_Position = position; }";
      var fs = "void main() { gl_FragColor = vec4(1.0); }";

      var cache = new ShaderCache(context);
      var sp = cache.getShaderProgram({
        vertexShaderSource: vs,
        fragmentShaderSource: fs,
        attributeLocations: {
          position: 0,
        },
      });

      cache.destroy();

      expect(sp.isDestroyed()).toEqual(true);
      expect(cache.isDestroyed()).toEqual(true);
    });

    it("is not destroyed", function () {
      var cache = new ShaderCache(context);
      expect(cache.isDestroyed()).toEqual(false);
    });
  },
  "WebGL"
);