import { profile } from "./profile";
import { compileShader, linkProgram } from "./shader";
import { mat4 } from "gl-matrix";
let identity = mat4.create();
export class PointsWithTypeDraw {
    constructor(_gl) {
        this._gl = _gl;
    }
    dispose() {
        if (this._vbo)
            this._gl.deleteBuffer(this._vbo);
        this._vbo = undefined;
        if (this._shader)
            this._gl.deleteProgram(this._shader.prog);
        this._shader = undefined;
    }
    _generate(gl, points) {
        if (!this._vbo)
            this._vbo = gl.createBuffer();
        if (!this._vbo)
            throw new Error("Unable to create buffer object");
        gl.bindBuffer(gl.ARRAY_BUFFER, this._vbo);
        gl.bufferData(gl.ARRAY_BUFFER, points, gl.DYNAMIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        return this._vbo;
    }
    _generateTypes(gl, points) {
        if (!this._typebo)
            this._typebo = gl.createBuffer();
        if (!this._typebo)
            throw new Error("Unable to create buffer object");
        gl.bindBuffer(gl.ARRAY_BUFFER, this._typebo);
        gl.bufferData(gl.ARRAY_BUFFER, points, gl.DYNAMIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        return this._typebo;
    }
    drawPoints(screenWidth, screenHeight, dataWidth, dataHeight, points, mirror, type, pointSize) {
        if (points.length === 0)
            return; // points = new Float32Array([0, 1, 0, 1]);
        let gl = this._gl;
        const reenableDepthTest = gl.isEnabled(gl.DEPTH_TEST);
        const reenableScissorTest = gl.isEnabled(gl.SCISSOR_TEST);
        const reenableCullFace = gl.isEnabled(gl.CULL_FACE);
        const reenableStencilTest = gl.isEnabled(gl.STENCIL_TEST);
        const reenableBlend = gl.isEnabled(gl.BLEND);
        const previousBoundTexture = gl.getParameter(gl.TEXTURE_BINDING_2D);
        const previousBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
        const previousProgram = gl.getParameter(gl.CURRENT_PROGRAM);
        const previousActiveTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
        gl.disable(gl.DEPTH_TEST);
        gl.disable(gl.SCISSOR_TEST);
        gl.disable(gl.CULL_FACE);
        gl.disable(gl.STENCIL_TEST);
        gl.disable(gl.BLEND);
        let shader = this._getCameraShader(gl);
        let vbo = this._generate(gl, points);
        let typebo = this._generateTypes(gl, type);
        gl.activeTexture(gl.TEXTURE0);
        gl.useProgram(shader.prog);
        gl.uniform1f(shader.unif_pointSize, pointSize);
        gl.uniformMatrix4fv(shader.unif_skinTexTransform, false, getPointsDataMatrix(dataWidth, dataHeight, screenWidth, screenHeight, mirror));
        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
        gl.vertexAttribPointer(shader.attr_position, 2, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(shader.attr_position);
        gl.bindBuffer(gl.ARRAY_BUFFER, typebo);
        gl.vertexAttribPointer(shader.attr_type, 1, gl.UNSIGNED_BYTE, false, 0, 0);
        gl.enableVertexAttribArray(shader.attr_type);
        gl.drawArrays(gl.POINTS, 0, points.length / 2);
        gl.disableVertexAttribArray(shader.attr_type);
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
        gl.disableVertexAttribArray(shader.attr_position);
        gl.bindFramebuffer(gl.FRAMEBUFFER, previousBoundFramebuffer);
        gl.useProgram(previousProgram);
        gl.bindTexture(gl.TEXTURE_2D, previousBoundTexture);
        gl.activeTexture(previousActiveTexture);
        if (reenableBlend)
            gl.enable(gl.BLEND);
        if (reenableCullFace)
            gl.enable(gl.CULL_FACE);
        if (reenableDepthTest)
            gl.enable(gl.DEPTH_TEST);
        if (reenableScissorTest)
            gl.enable(gl.SCISSOR_TEST);
        if (reenableStencilTest)
            gl.enable(gl.STENCIL_TEST);
    }
    _getCameraShader(gl) {
        if (this._shader)
            return this._shader;
        let prog = gl.createProgram();
        if (!prog)
            throw new Error("Unable to create program");
        let vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderSrc);
        let fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSrc);
        gl.attachShader(prog, vertexShader);
        gl.attachShader(prog, fragmentShader);
        linkProgram(gl, prog);
        let unif_skinTexTransform = gl.getUniformLocation(prog, "skinTexTransform");
        if (!unif_skinTexTransform)
            throw new Error("Unable to get uniform location skinTexTransform");
        let unif_pointSize = gl.getUniformLocation(prog, "pointSize");
        if (!unif_pointSize)
            throw new Error("Unable to get uniform location pointSize");
        this._shader = {
            prog,
            unif_skinTexTransform,
            unif_pointSize,
            attr_position: gl.getAttribLocation(prog, "pos"),
            attr_type: gl.getAttribLocation(prog, "aType"),
        };
        return this._shader;
    }
}
let vertexShaderSrc = `
attribute vec2 pos;
attribute float aType;
uniform mat4 skinTexTransform;
uniform float pointSize;

varying vec4 vColor;

void main()
{
    gl_Position = skinTexTransform * vec4(pos, 0.0, 1.0);
    vColor = vec4(1.0, 0.0, 0.0, 1.0);
    if (aType == 1.0) {
        vColor = vec4(1.0, 0.7, 0.0, 1.0);
    } else if (aType >= 4.0) {
        vColor = vec4(0.0, 0.0, 1.0, 1.0);
    } else if (aType > 1.0) {
        vColor = vec4(0.0, 1.0, 0.0, 1.0);
    }
    gl_PointSize = pointSize;
}`;
let fragmentShaderSrc = `
precision highp float;

varying vec4 vColor;

void main()
{
    gl_FragColor = vColor;
}`;
function cameraRotationForScreenOrientation() {
    if (window.screen.orientation && !profile.forceWindowOrientation) {
        switch (window.screen.orientation.type) {
            case "portrait-primary":
                return 270;
            case "landscape-secondary":
                return 180;
            case "portrait-secondary":
                return 90;
            default:
                return 0;
        }
    }
    else if (window.orientation !== undefined) {
        switch (window.orientation) {
            case 0: return 270;
            case 90: return 0;
            case 180: return 90;
            case -90: return 180;
        }
    }
    return 0;
}
export function getPointsDataMatrix(frameWidth, frameHeight, screenWidth, screenHeight, mirror) {
    let ret = mat4.create();
    let trans = mat4.create();
    // Translate to centre UV coords
    mat4.fromTranslation(trans, [-0.5 * frameWidth, -0.5 * frameHeight, 0]);
    mat4.multiply(ret, trans, ret);
    mat4.fromScaling(trans, [2 / frameWidth, 2 / -frameHeight, 1]);
    mat4.multiply(ret, trans, ret);
    if (mirror) {
        mat4.fromScaling(trans, [-1, 1, 1]);
        mat4.multiply(ret, trans, ret);
    }
    let screenAspect = screenWidth > screenHeight ? (screenWidth / screenHeight) : (screenHeight / screenWidth);
    let frameAspect = frameWidth / frameHeight;
    mat4.fromScaling(trans, [1, screenAspect / frameAspect, 1]);
    mat4.multiply(ret, trans, ret);
    // Apply rotation back into ZCV's landscape space
    mat4.fromRotation(trans, cameraRotationForScreenOrientation() * Math.PI / 180.0, [0, 0, 1]);
    mat4.multiply(ret, trans, ret);
    return ret;
}
