import { LayerAB, Scene } from "../base/Scene";
import { createProgram, createShader } from "../base/utils";
import { AnimatableMask } from "./AnimatableMask";
import { rectangleBuffer } from "./common";
import { FillerData } from "./FillerData";

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

  `;

  private fragmentShaderCode = /*glsl*/ `
    precision mediump float;
    uniform sampler2D u_colored;
    uniform sampler2D u_mask;
    uniform vec2 u_pixelSize; 
    varying vec2 v_texCoord;


    const float w1 = 0.147761;
    const float w2 = 0.118318; 
    const float w3 = 0.0947416; 
    
    
    vec4 GaussianBlur(in sampler2D image, in vec2 texCoord, in vec2 pixelSize) {
      vec4 C00 = texture2D(image, texCoord + vec2(-pixelSize.x, -pixelSize.y)) * w3 ;
      vec4 C01 = texture2D(image, texCoord + vec2(0.0, -pixelSize.y)) * w2;
      vec4 C02 = texture2D(image, texCoord + vec2(pixelSize.x, -pixelSize.y)) * w3 ;
    
      vec4 C10 = texture2D(image, texCoord + vec2(-pixelSize.x, 0.0)) * w2;
      vec4 C11 = texture2D(image, texCoord + vec2(0.0, 0.0)) * w1;
      vec4 C12 = texture2D(image, texCoord + vec2(pixelSize.x, 0.0)) * w2;
    
      vec4 C20 = texture2D(image, texCoord + vec2(-pixelSize.x, pixelSize.y)) * w3;
      vec4 C21 = texture2D(image, texCoord + vec2(0.0, pixelSize.y)) * w2;
      vec4 C22 = texture2D(image, texCoord + vec2(pixelSize.x, pixelSize.y)) * w3;
    
      return 
        C00 + C01 + C02 +
        C10 + C11 + C12 +
        C20 + C21 + C22 ;
    }

        
    vec4 GaussianBlurR(in sampler2D image, in vec2 texCoord, in vec2 pixelSize) {
      vec4 C00 = texture2D(image, texCoord + vec2(-pixelSize.x, -pixelSize.y)) * w3 ;
      vec4 C01 = texture2D(image, texCoord + vec2(0.0, -pixelSize.y)) * w2;
      vec4 C02 = texture2D(image, texCoord + vec2(pixelSize.x, -pixelSize.y)) * w3 ;
    
      vec4 C10 = texture2D(image, texCoord + vec2(-pixelSize.x, 0.0)) * w2;
      vec4 C11 = texture2D(image, texCoord + vec2(0.0, 0.0)) * w1;
      vec4 C12 = texture2D(image, texCoord + vec2(pixelSize.x, 0.0)) * w2;
    
      vec4 C20 = texture2D(image, texCoord + vec2(-pixelSize.x, pixelSize.y)) * w3;
      vec4 C21 = texture2D(image, texCoord + vec2(0.0, pixelSize.y)) * w2;
      vec4 C22 = texture2D(image, texCoord + vec2(pixelSize.x, pixelSize.y)) * w3;

      vec4 g =  
        C00 + C01 + C02 +
        C10 + C11 + C12 +
        C20 + C21 + C22 ;

      return g;
    }


    
    void main() {
        vec4 mask = GaussianBlurR(u_mask, v_texCoord, u_pixelSize);
        vec4 colored = GaussianBlur(u_colored, v_texCoord, u_pixelSize);
        //vec4 colored = texture2D(u_colored, v_texCoord);
        //gl_FragColor = vec4(colored.rgb, mask.a);
        if(mask.r >= 0.4) {
            gl_FragColor = vec4(colored.rgb, 1);
            //gl_FragColor = colored;
        }else{
            gl_FragColor = vec4(0, 0, 0, 0);
        }
    }
    
  `;

  program: WebGLProgram;

  positionBuffer: WebGLBuffer;
  texCoordBuffer: WebGLBuffer;

  aPositionLoc: number;
  aTexcoordLoc: number;
  uMatrixLoc: WebGLUniformLocation;
  uMaskLoc: WebGLUniformLocation;
  uColoredLoc: WebGLUniformLocation;
  uPixelSizeLoc: WebGLUniformLocation;

  animatableMask: AnimatableMask;

  dispose() {
    let gl = this.scene.gl;
    this.animatableMask.dispose();
    gl.deleteProgram(this.program);
    gl.deleteBuffer(this.positionBuffer);
    gl.deleteBuffer(this.texCoordBuffer);
  }

  constructor(
    private scene: Scene,
    private fillerData: FillerData,
  ) {
    super();

    this.animatableMask = new AnimatableMask(scene, fillerData);

    const gl = scene.gl;

    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.uMatrixLoc = gl.getUniformLocation(this.program, "u_matrix")!;
    this.uMaskLoc = gl.getUniformLocation(this.program, "u_mask")!;
    this.uColoredLoc = gl.getUniformLocation(this.program, "u_colored")!;
    this.uPixelSizeLoc = gl.getUniformLocation(this.program, "u_pixelSize")!;

    this.positionBuffer = rectangleBuffer(
      gl,
      0,
      0,
      fillerData.width,
      fillerData.height,
    );
    this.texCoordBuffer = rectangleBuffer(gl, 0, 0, 1, 1);
  }

  // 恢复游戏，已经有部分完成的
  public initTask() {
    this.animatableMask.initTask();
    this.scene.invalidate();
  }

  override draw() {
    console.log("WorkLayer draw()");
    const gl = this.scene.gl;

    gl.useProgram(this.program);

    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.uniformMatrix4fv(this.uMatrixLoc, false, this.scene.drawMatrix);
    gl.uniform2f(
      this.uPixelSizeLoc,
      1 / this.fillerData.width,
      1 / this.fillerData.height,
    );

    gl.uniform1i(this.uMaskLoc, 0);
    gl.uniform1i(this.uColoredLoc, 1);

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, this.animatableMask.texture);
    gl.activeTexture(gl.TEXTURE1);
    gl.bindTexture(gl.TEXTURE_2D, this.fillerData.colored);

    gl.drawArrays(gl.TRIANGLES, 0, 6);
  }

  override preDraw(): void {
    this.animatableMask.flush();
  }

  override tap(cx: number, cy: number, sx: number, sy: number): void {
    let fd = this.fillerData;

    if (fd.currentGroup == null) {
      fd.callback?.onFillFailed();
      return;
    }

    const hash = fd.data.groupAreaHash(fd.currentGroup);

    let area = this.fillerData.getArea(cx, cy, 50, hash);
    if (area != null) {
      this.animatableMask.addArea(area, cx, cy);
      fd.setColored(area, fd.currentGroup.color, cx, cy);

      fd.callback?.onFillSuccess();

      // 全部填完，自动切换到下一个group
      if (fd.currentGroup.isAllColored) {
        if (!fd.switchToNextGroup()) {
          fd.callback?.onFinish();
        } else {
          fd.callback?.onSwitchGroup();
        }
      }
    }
  }

  override scale(scale: number): void {}

  async replay() {
    const gl = this.scene.gl;

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

    // Clear the framebuffer
    gl.clearColor(0, 0, 0, 0); // White
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Bind the default framebuffer again
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    const length = this.fillerData.taskList.length;
    const duration = Math.max(2000 / length, 50);

    for (let task of this.fillerData.taskList) {
      let area = this.fillerData.data.areaHash.get(task);
      if (area) {
        this.animatableMask.addArea(area, area.center.x, area.center.y, 200);
        this.scene.invalidate();
        await delay(duration);
      }
    }
  }
}

const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
