/*
Usage: JeelizCanvas2DHelper(spec) where spec is the returned object of the initialization function (callbackReady)
Return an object width these properties:

 - canvas: the CANVAS element
 - ctx: the canvas drawing context
 - update_canvasTexture: function to launch each time the canvas has been updated (somethink has been drawn on it)
 - draw: draw the video with the canvas above
 - getCoordinates: transform the detectedState relative 2D viewport coords into canvas 2D pixel coordinates
 - resize: to call if the HTML canvas size has changed
*/

const JeelizCanvas2DHelper = function(spec){

  // some globalz:
  let CV = null, CANVAS2D = null, CTX = null, GL = null, CANVASTEXTURE = null, CANVASTEXTURENEEDSUPDATE = null, SHADERCOPY = null, VIDEOTEXTURE = null;
  let VIDEOTEXTURETRANSFORMMAT2 = null, UUVTRANSFORM = null;

  const COORDINATES = {
    x:0, y:0, s:0
  };

  //BEGIN WEBGL HELPERS
  // compile a shader:
  function compile_shader(source, glType, typeString) {
    const glShader = GL.createShader(glType);
    GL.shaderSource(glShader, source);
    GL.compileShader(glShader);
    if (!GL.getShaderParameter(glShader, GL.COMPILE_STATUS)) {
      alert("ERROR IN " + typeString +  " SHADER: " + GL.getShaderInfoLog(glShader));
      return null;
    }
    return glShader;
  };

  // helper function to build the shader program:
  function build_shaderProgram(shaderVertexSource, shaderFragmentSource, id) {
    // compile both shader separately:
    const glShaderVertex = compile_shader(shaderVertexSource, GL.VERTEX_SHADER, "VERTEX " + id);
    const glShaderFragment = compile_shader(shaderFragmentSource, GL.FRAGMENT_SHADER, "FRAGMENT " + id);

    const glShaderProgram = GL.createProgram();
    GL.attachShader(glShaderProgram, glShaderVertex);
    GL.attachShader(glShaderProgram, glShaderFragment);

    // start the linking stage:
    GL.linkProgram(glShaderProgram);
    return glShaderProgram;
  }
  //END WEBGL HELPERS


  // affect some globalz:
  GL = spec.GL;
  CV = spec.canvasElement;
  VIDEOTEXTURE = spec.videoTexture;
  VIDEOTEXTURETRANSFORMMAT2 = spec.videoTransformMat2;

  // create and size the 2D canvas and its drawing context:
  CANVAS2D = document.createElement('canvas');
  CANVAS2D.width = CV.width;
  CANVAS2D.height = CV.height;
  CTX = CANVAS2D.getContext('2d');
    
  // create the WebGL texture with the canvas:
  CANVASTEXTURE = GL.createTexture();
  GL.bindTexture(GL.TEXTURE_2D, CANVASTEXTURE);
  GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, CANVAS2D);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);

  // build the copy shader program:
  const copyVertexShaderSource = "attribute vec2 position;\n\
    uniform mat2 UVTransformMat2;\n\
    varying vec2 vUV;\n\
    void main(void){\n\
      gl_Position = vec4(position, 0., 1.);\n\
      vUV = vec2(0.5,0.5) + UVTransformMat2 * position;\n\
    }";

  const copyFragmentShaderSource = "precision lowp float;\n\
    uniform sampler2D samplerImage;\n\
    varying vec2 vUV;\n\
    \n\
    void main(void){\n\
      gl_FragColor = texture2D(samplerImage, vUV);\n\
    }";
  SHADERCOPY = build_shaderProgram(copyVertexShaderSource, copyFragmentShaderSource, 'VIDEO');
  const uSampler = GL.getUniformLocation(SHADERCOPY, 'samplerImage');
  UUVTRANSFORM = GL.getUniformLocation(SHADERCOPY, 'UVTransformMat2');
  GL.useProgram(SHADERCOPY);
  GL.uniform1i(uSampler, 0);
  
  return {
    canvas: CANVAS2D,
    ctx: CTX,

    update_canvasTexture: function(){
      CANVASTEXTURENEEDSUPDATE = true;
    },

    draw: function(){ // draw the video and the canvas above
      GL.viewport(0, 0, CV.width, CV.height);
      GL.useProgram(SHADERCOPY);

      // enable blending:
      GL.enable(GL.BLEND);
      GL.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);

      // draw the video first:
      GL.bindTexture(GL.TEXTURE_2D, VIDEOTEXTURE);
      GL.uniformMatrix2fv(UUVTRANSFORM, false, VIDEOTEXTURETRANSFORMMAT2);
      GL.drawElements(GL.TRIANGLES, 3, GL.UNSIGNED_SHORT, 0);

      // then draw the canvas:
      GL.bindTexture(GL.TEXTURE_2D, CANVASTEXTURE);
      GL.uniformMatrix2fv(UUVTRANSFORM, false, [0.5, 0, 0, 0.5]);
      if (CANVASTEXTURENEEDSUPDATE) {
        GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, CANVAS2D);
      }
      GL.drawElements(GL.TRIANGLES, 3, GL.UNSIGNED_SHORT, 0);

      GL.disable(GL.BLEND);      
    },

    getCoordinates: function(detectedState){
      COORDINATES.x = Math.round((0.5+0.5*detectedState.x-0.5*detectedState.s)*CV.width);
      COORDINATES.y = Math.round((0.5+0.5*detectedState.y-0.5*detectedState.s)*CV.height);
      COORDINATES.w = Math.round(detectedState.s*CV.width);
      COORDINATES.h = COORDINATES.w;
      return COORDINATES;   
    },

    resize: function(){
      CANVAS2D.width = CV.width;
      CANVAS2D.height = CV.height;
    }
  }; //end Canvas2DDisplay return value
} //end JeelizCanvas2DHelper()
// const JeelizCanvas2DHelper = JeelizCanvas2DHelper();
 if(typeof(module) !== 'undefined') {
 	module.exports = JeelizCanvas2DHelper;
 }