import { fillRectangle } from "../base/2d";
import { m4 } from "../base/m4";
import { Disposable } from "../base/Scene";
import { createProgram, createShader } from "../base/utils";
import { Area, Color } from "./common";
import { FillerData } from "./FillerData"



export class Mask implements Disposable {

  private vertexShaderCode = /*glsl*/ `
    attribute vec2 a_position;
    attribute vec2 a_texCoord;
    attribute vec4 a_colorId;
    uniform mat4 u_matrix;
    varying vec2 v_texCoord;
    varying vec4 v_colorId;
    void main() {
      gl_Position = u_matrix * vec4(a_position, 0, 1);
      v_texCoord = a_texCoord;
      v_colorId = a_colorId;
    }

  `;

  private fragmentShaderCode = /*glsl*/ `
    precision mediump float;
    uniform sampler2D u_map;
    varying vec2 v_texCoord;
    varying vec4 v_colorId;
    void main() {
      vec4 mapColor = texture2D(u_map, v_texCoord);
      float dist = distance(mapColor, v_colorId);
      if(dist < 0.001) {
        gl_FragColor = vec4(1, 1, 0, 1);
      }else{
        gl_FragColor = vec4(0, 0, 0, 0);
      }
    }
  
  `



  program: WebGLProgram

  positionBuffer: WebGLBuffer
  texCoordBuffer: WebGLBuffer
  colorIdBuffer: WebGLBuffer

  aPositionLoc: number
  aTexcoordLoc: number
  aColorIdLoc: number
  uMatrixLoc: WebGLUniformLocation



  matrix: m4.Matrix4


  texture: WebGLTexture
  fb: WebGLFramebuffer

  private pendingAreas: Array<Area> = []

  get width(): number { return this.fillerData.width }
  get height(): number { return this.fillerData.height }

  dispose() {
    this.gl.deleteTexture(this.texture)
    this.gl.deleteFramebuffer(this.fb)
    this.gl.deleteBuffer(this.positionBuffer)
    this.gl.deleteBuffer(this.texCoordBuffer)
    this.gl.deleteBuffer(this.colorIdBuffer)
    this.gl.deleteProgram(this.program)
  }

  constructor(
    private gl: WebGL2RenderingContext,
    private fillerData: FillerData,

  ) {

    this.program = createProgram(gl,
      createShader(gl, gl.VERTEX_SHADER, this.vertexShaderCode)!,
      createShader(gl, gl.FRAGMENT_SHADER, this.fragmentShaderCode)!)!

    this.aPositionLoc = gl.getAttribLocation(this.program, "a_position")
    this.aTexcoordLoc = gl.getAttribLocation(this.program, "a_texCoord")
    this.aColorIdLoc = gl.getAttribLocation(this.program, "a_colorId")
    this.uMatrixLoc = gl.getUniformLocation(this.program, "u_matrix")!


    this.positionBuffer = gl.createBuffer()!
    this.texCoordBuffer = gl.createBuffer()!
    this.colorIdBuffer = gl.createBuffer()!

    this.matrix = m4.projectionNoflipY(fillerData.width, fillerData.height)


    this.texture = gl.createTexture()!
    gl.bindTexture(gl.TEXTURE_2D, this.texture)
    //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.fillerData.width, this.fillerData.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, this.fillerData.width, this.fillerData.height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, null)

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_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);


    this.fb = gl.createFramebuffer()!
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fb)
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0)
    gl.bindFramebuffer(gl.FRAMEBUFFER, null)

  }



  addArea(area: Area) {
    this.pendingAreas.push(area)
  }

  flush(clear: boolean = false) {
    if (this.pendingAreas.length <= 0) return



    const vertexArray = new Float32Array(12 * this.pendingAreas.length)
    const texCoordArray = new Float32Array(12 * this.pendingAreas.length)
    const colorIdArray = new Float32Array(24 * this.pendingAreas.length)

    var vertexOffset = 0
    var colorOffset = 0

    const width = this.fillerData.width
    const height = this.fillerData.height

    for (var i = 0; i < this.pendingAreas.length; i++) {
      var area = this.pendingAreas[i]
      fillRectangle(vertexArray, vertexOffset, area.rect.x, area.rect.y, area.rect.width, area.rect.height)
      fillRectangle(texCoordArray, vertexOffset, area.rect.x / width, area.rect.y / height, area.rect.width / width, area.rect.height / height)
      var color = new Color(area.id)
      color.fillFloatArray(colorIdArray, colorOffset, 6)
      vertexOffset += 12
      colorOffset += 24
    }


    const gl = this.gl

    gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, vertexArray, gl.STATIC_DRAW);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, texCoordArray, gl.STATIC_DRAW);

    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorIdBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, colorIdArray, gl.STATIC_DRAW);


    this.draw(this.pendingAreas.length * 6, clear)

    //console.log('colorIdArray', colorIdArray)
    //console.log('vertexArray', vertexArray)

    this.pendingAreas = []
  }


  private draw(n: number, clear: boolean = false) {
    const gl = this.gl;

    gl.bindFramebuffer(gl.FRAMEBUFFER, this.fb)

    console.log('mask.fb=', this.fb)

    gl.useProgram(this.program);

    gl.viewport(0, 0, this.fillerData.width, this.fillerData.height)

    if (clear) {
      gl.clearColor(0, 0, 0, 0);
      gl.clear(gl.COLOR_BUFFER_BIT);
    }


    gl.enableVertexAttribArray(this.aPositionLoc);
    gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
    gl.vertexAttribPointer(this.aPositionLoc, 2, gl.FLOAT, false, 0, 0);

    gl.enableVertexAttribArray(this.aTexcoordLoc);
    gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
    gl.vertexAttribPointer(this.aTexcoordLoc, 2, gl.FLOAT, false, 0, 0);

    gl.enableVertexAttribArray(this.aColorIdLoc);
    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorIdBuffer);
    gl.vertexAttribPointer(this.aColorIdLoc, 4, gl.FLOAT, false, 0, 0);




    gl.uniformMatrix4fv(this.uMatrixLoc, false, this.matrix)

    gl.bindTexture(gl.TEXTURE_2D, this.fillerData.mapTexure)

    gl.drawArrays(gl.TRIANGLES, 0, n);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null)

  }

}







