/*
 * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 */

import type { default as GlyphAtlas } from '../GlyphAtlas';
import shaders from '../shaders';
import { Program } from './Program';

export class TextProgram extends Program {
    private maxVertices: number = 500000;
    atlas: GlyphAtlas;
    #uniformLoc: Record<string, WebGLUniformLocation | null> = {};

    constructor(gl: WebGL2RenderingContext, atlas: GlyphAtlas) {
        super(gl);
        this.atlas = atlas;
    }

    initialize(): void {
        const { vertexShader, fragmentShader } = this.getShaderCode();
        this.program = this.createProgram(vertexShader, fragmentShader);
        const gl = this.gl;
        this.#uniformLoc.uScale = gl.getUniformLocation(this.program, 'uScale');
        this.#uniformLoc.uTranslate = gl.getUniformLocation(this.program, 'uTranslate');
        this.#uniformLoc.uResolution = gl.getUniformLocation(this.program, 'uResolution');
        this.#uniformLoc.uAtlasSampler = gl.getUniformLocation(this.program, 'uAtlasSampler');
        this.instanceBuffer = this.createBuffer(this.maxVertices * 12 * 4);
        this.vao = gl.createVertexArray()!;
        gl.bindVertexArray(this.vao);
        gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceBuffer);
        const stride = 12 * 4;
        gl.enableVertexAttribArray(0);
        gl.vertexAttribPointer(0, 2, gl.FLOAT, false, stride, 0);
        gl.vertexAttribDivisor(0, 1);
        gl.enableVertexAttribArray(1);
        gl.vertexAttribPointer(1, 2, gl.FLOAT, false, stride, 8);
        gl.vertexAttribDivisor(1, 1);
        gl.enableVertexAttribArray(2);
        gl.vertexAttribPointer(2, 4, gl.FLOAT, false, stride, 16);
        gl.vertexAttribDivisor(2, 1);
        gl.enableVertexAttribArray(3);
        gl.vertexAttribPointer(3, 4, gl.FLOAT, false, stride, 32);
        gl.vertexAttribDivisor(3, 1);
        gl.bindVertexArray(null);
    }

    draw(params: { instanceCount: number, uniformData: Float32Array }): void {
        const gl = this.gl;
        const { instanceCount, uniformData } = params;
        gl.useProgram(this.program);
        gl.uniform2f(this.#uniformLoc.uScale, uniformData[0], uniformData[1]);
        gl.uniform2f(this.#uniformLoc.uTranslate, uniformData[8], uniformData[9]);
        gl.uniform2f(this.#uniformLoc.uResolution, uniformData[12], uniformData[13]);
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.atlas.texture);
        gl.uniform1i(this.#uniformLoc.uAtlasSampler, 0);
        gl.bindVertexArray(this.vao);
        gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount);
        gl.bindVertexArray(null);
    }

    private getShaderCode(): { vertexShader: string; fragmentShader: string } {
        return shaders.text;
    }
}