import { fillRectangle } from "../base/2d";
import { LayerAB, Scene } from "../base/Scene";
import { createProgram, createShader } from "../base/utils";
import { Center, createTexture, TexImage } from "./common";
import { FillerData } from "./FillerData";

export class NumberLayer 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_image;
    varying vec2 v_texCoord;

    void main() {
      vec4 color = texture2D(u_image, v_texCoord);
      gl_FragColor = color;
    }
  
  `;

  program: WebGLProgram;

  aPositionLoc: number;
  aTexcoordLoc: number;
  uMatrixLoc: WebGLUniformLocation;
  uScaleLoc: WebGLUniformLocation;
  uTexSizeLoc: WebGLUniformLocation;

  vertexBuffer: WebGLBuffer;
  texcoordBuffer: WebGLBuffer;

  vertexArray: Float32Array;
  texCoordArray: Float32Array;

  texture: WebGLTexture;

  digits: number;
  centers: Array<Center>;

  dispose() {
    let gl = this.scene.gl;
    gl.deleteProgram(this.program);
    gl.deleteBuffer(this.vertexBuffer);
    gl.deleteBuffer(this.texcoordBuffer);
    gl.deleteTexture(this.texture);
  }

  constructor(
    public readonly scene: Scene,
    private image: TexImage,
    private fillerData: FillerData,
    private bestFitScale: number,
  ) {
    super();

    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.uScaleLoc = gl.getUniformLocation(this.program, "u_scale")!;
    this.uTexSizeLoc = gl.getUniformLocation(this.program, "u_texSize")!;

    this.texture = createTexture(gl, image, gl.LINEAR);

    const centers: Array<Center> = [];
    const ratio = image.width / 10 / image.height;
    const textHeightRatio = Array(4);
    for (var i = 0; i < 4; i++) {
      textHeightRatio[i] = 2 / Math.sqrt(i * i * ratio * ratio + 1);
    }

    const magnify = 1.2;
    const minify = 0.8;
    var digits = 0;

    const config = fillerData.config;

    //const maxScale = fillerData.config.maxScale

    const areaGroups = fillerData.data.areaGroups;

    for (var i = 0; i < areaGroups.length; i++) {
      var group = areaGroups[i];
      for (var j = 0; j < group.areas.length; j++) {
        var area = group.areas[j];

        // 剪枝， 如果已经上色，直接跳过
        if (area.colored) continue;

        digits += area.center.label!.length;
        centers.push(area.center);
        var fontHeight =
          area.center.radius * textHeightRatio[area.center.label!.length];

        // 让文字大小线性放大或者缩小
        var k = (fontHeight * config.maxScale) / config.visibleFontSize;
        if (k <= 1) {
          k *= magnify;
        } else if (k > 1 && k < 1 / minify) {
          k = ((1 / minify - magnify) / (1 / minify - 1)) * (k - 1) + magnify;
        } else if (k >= 1 / minify) {
          k = minify * k + (1 / minify - 1);
        }
        fontHeight = (k * config.visibleFontSize) / config.maxScale;

        if (fontHeight * this.bestFitScale > config.visibleFontSize * 1.2) {
          fontHeight = (config.visibleFontSize * 1.2) / bestFitScale;
        }

        area.center.fontHeight = fontHeight;
      }
    }

    centers.sort((a, b) => b.fontHeight - a.fontHeight);

    this.digits = digits;
    this.centers = centers;

    this.vertexArray = new Float32Array(digits * 12);
    this.texCoordArray = new Float32Array(digits * 12);
    var offset = 0;

    for (var i = 0; i < centers.length; i++) {
      var center = centers[i];
      this.fillVertexArray(
        this.vertexArray,
        this.texCoordArray,
        offset,
        center,
        ratio,
      );
      offset += 12 * center.label.length;
      center.offset = offset / 2;
    }

    //fillRectangle(this.vertexArray, 0, 0, 0, width, height )
    //fillRectangle(this.texCoordArray, 0, 0, 0, 1, 1)

    this.vertexBuffer = gl.createBuffer()!;
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW);

    this.texcoordBuffer = gl.createBuffer()!;
    gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.texCoordArray, gl.STATIC_DRAW);
  }

  /** replay 阶段设为 false，隐藏 hint 覆盖层 */
  public enabled: boolean = true;

  override draw() {
    if (!this.enabled) return;

    var offset = 0;
    const maxScale = this.fillerData.config.maxScale - 0.2;
    const scale = this.scene.userMat[0];

    if (scale > maxScale) {
      offset = this.vertexArray.length / 2;
    } else {
      for (var i = this.centers.length - 1; i >= 0; i--) {
        if (
          this.centers[i].fontHeight * scale >
          this.fillerData.config.visibleFontSize
        ) {
          offset = this.centers[i].offset;
          break;
        }
      }
    }

    const gl = this.scene.gl;
    gl.useProgram(this.program);

    gl.enableVertexAttribArray(this.aPositionLoc);
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    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.uniform1f(this.uScaleLoc, this.scene.userMat[0]);
    //gl.uniform2f(this.uTexSizeLoc, 1. / this.texWidth, 1. / this.texHeight)

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, this.texture);

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

  fillVertexArray(
    vertexArray: Float32Array,
    texCoordArray: Float32Array,
    offset: number,
    center: Center,
    ratio: number,
  ) {
    const text = center.label;
    const size = text.length;
    const cellWidth = center.fontHeight * ratio;
    const cellHeight = center.fontHeight;
    const width = cellWidth * size;
    const left = center.x - width / 2;
    const top = center.y - cellHeight / 2;

    for (var i = 0; i < size; i++) {
      fillRectangle(
        vertexArray,
        offset + 12 * i,
        left + i * cellWidth,
        top,
        cellWidth,
        cellHeight,
      );
      const index = parseInt(text.substring(i, i + 1));
      fillRectangle(texCoordArray, offset + 12 * i, index * 0.1, 0, 0.1, 1);
    }
  }

  toString(): string {
    return `NumberLayer()`;
  }
}
