import { profile } from "./profile";
import { compileShader, linkProgram } from "./shader";
import { mat4, vec3 } from "gl-matrix";
let identity = mat4.create();
export class CameraDraw {
    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, i) {
        if (this._vbo)
            return this._vbo;
        if (!this._vbo)
            this._vbo = gl.createBuffer();
        if (!this._vbo)
            throw new Error("Unable to create buffer object");
        let vboData = new Float32Array([
            -1, -1, 0, 0, 0,
            -1, 1, 0, 0, 1,
            1, -1, 0, 1, 0,
            1, -1, 0, 1, 0,
            -1, 1, 0, 0, 1,
            1, 1, 0, 1, 1
        ]);
        gl.bindBuffer(gl.ARRAY_BUFFER, this._vbo);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vboData), gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        return this._vbo;
    }
    drawCameraFrame(screenWidth, screenHeight, i, mirror) {
        if (!i.texture)
            return;
        let gl = this._gl;
        gl.disable(gl.DEPTH_TEST);
        gl.disable(gl.SCISSOR_TEST);
        gl.disable(gl.CULL_FACE);
        gl.disable(gl.BLEND);
        let shader = this._getCameraShader(gl);
        let vbo = this._generate(gl, i);
        gl.activeTexture(gl.TEXTURE0);
        gl.useProgram(shader.prog);
        gl.uniformMatrix4fv(shader.unif_skinTexTransform, false, cameraFrameTextureMatrix(i.dataWidth, i.dataHeight, screenWidth, screenHeight, i.uvTransform || identity, mirror));
        gl.uniform1i(shader.unif_skinSampler, 0);
        gl.bindTexture(gl.TEXTURE_2D, i.texture);
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
        gl.vertexAttribPointer(shader.attr_position, 3, gl.FLOAT, false, 5 * 4, 0);
        gl.enableVertexAttribArray(shader.attr_position);
        gl.vertexAttribPointer(shader.attr_texCoord, 2, gl.FLOAT, false, 5 * 4, 3 * 4);
        gl.enableVertexAttribArray(shader.attr_texCoord);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
        gl.disableVertexAttribArray(shader.attr_position);
        gl.disableVertexAttribArray(shader.attr_texCoord);
        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        gl.useProgram(null);
    }
    _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_skinSampler = gl.getUniformLocation(prog, "skinSampler");
        if (!unif_skinSampler)
            throw new Error("Unable to get uniform location skinSampler");
        this._shader = {
            prog,
            unif_skinTexTransform,
            unif_skinSampler,
            attr_position: gl.getAttribLocation(prog, "position"),
            attr_texCoord: gl.getAttribLocation(prog, "texCoord")
        };
        return this._shader;
    }
}
let vertexShaderSrc = `
#ifndef GL_ES
#define highp
#define mediump
#define lowp
#endif

attribute vec4 position;
attribute vec4 texCoord;
varying vec4 skinTexVarying;
uniform mat4 skinTexTransform;

void main()
{
    gl_Position = position;
    skinTexVarying = skinTexTransform * texCoord;
}`;
let fragmentShaderSrc = `
#define highp mediump
#ifdef GL_ES
    // define default precision for float, vec, mat.
    precision highp float;
#else
#define highp
#define mediump
#define lowp
#endif

varying vec4 skinTexVarying;
uniform lowp sampler2D skinSampler;

void main()
{
    gl_FragColor = texture2DProj(skinSampler, skinTexVarying);
}`;
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 cameraFrameTextureMatrix(frameWidth, frameHeight, screenWidth, screenHeight, uvMatrix, mirror) {
    let ret = mat4.create();
    let trans = mat4.create();
    // Translate to centre UV coords
    mat4.fromTranslation(trans, [-0.5, -0.5, 0]);
    mat4.multiply(ret, trans, ret);
    if (mirror) {
        mat4.fromScaling(trans, [-1, 1, 1]);
        mat4.multiply(ret, trans, ret);
    }
    // Apply rotation back into ZCV's landscape space
    mat4.fromRotation(trans, -1 * cameraRotationForScreenOrientation() * Math.PI / 180.0, [0, 0, 1]);
    mat4.multiply(ret, trans, ret);
    // Get our screenWidth and screenHeight into that same space
    let vec = vec3.create();
    vec[0] = screenWidth;
    vec[1] = screenHeight;
    vec[2] = 0;
    vec3.transformMat4(vec, vec, trans);
    let absScreenX = Math.abs(vec[0]);
    let absScreenY = Math.abs(vec[1]);
    // Apply a flip since the texture is upside-down
    mat4.fromScaling(trans, [1, -1, 1]);
    mat4.multiply(ret, trans, ret);
    // Apply cropping
    let screenAspect = absScreenX / absScreenY;
    let frameAspect = frameWidth / frameHeight;
    if (screenAspect > frameAspect) {
        mat4.fromScaling(trans, [1, frameAspect / screenAspect, 1]);
    }
    else {
        mat4.fromScaling(trans, [screenAspect / frameAspect, 1, 1]);
    }
    mat4.multiply(ret, trans, ret);
    // Translate back to UV coords
    mat4.fromTranslation(trans, [0.5, 0.5, 0]);
    mat4.multiply(ret, trans, ret);
    // Apply the camera frame's UV matrix
    mat4.multiply(ret, uvMatrix, ret);
    return ret;
}
