"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeImageProgram = void 0;
/*
 * This class copies sigma/rendering/webgl/programs/node.image, but with a some
 * difference: it can change color of picture for inactive state
 * Program rendering nodes using GL_POINTS, but that draws an image on top of
 * the classic colored disc.
 */
var node_image_vert_glsl_1 = require("./shaders/node.image.vert.glsl");
var node_image_frag_glsl_1 = require("./shaders/node.image.frag.glsl");
var node_1 = require("sigma/rendering/webgl/programs/common/node");
var utils_1 = require("sigma/utils");
var mdm_sdk_1 = require("@reltio/mdm-sdk");
var no_photo_inline_svg_1 = require("../icons/no_photo.inline.svg");
var image_1 = require("./image");
var POINTS = 1;
var ATTRIBUTES = 9;
var defaultImage = { id: 'defaultImage', path: (0, mdm_sdk_1.svg2Url)(no_photo_inline_svg_1.DEFAULT_PROFILE_IMAGE) };
/*
 * To share the texture between the program instances of the graph and the
 * hovered nodes (to prevent some flickering, mostly), this program must be
 * "built" for each sigma instance:
 */
var NodeImageProgram = /** @class */ (function (_super) {
    __extends(NodeImageProgram, _super);
    function NodeImageProgram(gl, renderer) {
        var _this = _super.call(this, gl, node_image_vert_glsl_1.vertexShaderSource, node_image_frag_glsl_1.fragmentShaderSource, POINTS, ATTRIBUTES) || this;
        _this.imageLoader = new image_1.WebGLImageLoader();
        _this.imageLoader.rebindTextureFns.push(function () {
            if (_this && _this.rebindTexture)
                _this.rebindTexture();
            if (renderer && renderer.refresh)
                renderer.refresh();
        });
        _this.imageLoader.textureImage = new ImageData(1, 1);
        // Attribute Location
        _this.textureLocation = gl.getAttribLocation(_this.program, 'a_texture');
        _this.inactive = gl.getAttribLocation(_this.program, 'a_inactive');
        // Uniform Location
        var atlasLocation = gl.getUniformLocation(_this.program, 'u_atlas');
        if (atlasLocation === null)
            throw new Error('NodeProgramImage: error while getting atlasLocation');
        _this.atlasLocation = atlasLocation;
        // Initialize WebGL texture:
        _this.texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, _this.texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
        _this.imageLoader.loadImage(defaultImage.id, defaultImage.path);
        _this.bind();
        return _this;
    }
    NodeImageProgram.prototype.bind = function () {
        _super.prototype.bind.call(this);
        var gl = this.gl;
        gl.enableVertexAttribArray(this.textureLocation);
        gl.enableVertexAttribArray(this.inactive);
        gl.vertexAttribPointer(this.inactive, 1, gl.FLOAT, false, this.attributes * Float32Array.BYTES_PER_ELEMENT, 16);
        gl.vertexAttribPointer(this.textureLocation, 4, gl.FLOAT, false, this.attributes * Float32Array.BYTES_PER_ELEMENT, 20);
    };
    NodeImageProgram.prototype.process = function (data, hidden, offset) {
        var array = this.array;
        var i = offset * POINTS * ATTRIBUTES;
        var imageSource = data.image;
        var imageState = imageSource && this.imageLoader.images[imageSource];
        if (typeof imageSource === 'string' && !imageState) {
            this.imageLoader.loadImage(imageSource, imageSource);
        }
        if (hidden) {
            array[i++] = 0;
            array[i++] = 0;
            array[i++] = 0;
            array[i++] = 0;
            array[i++] = 0;
            // Texture:
            array[i++] = 0;
            array[i++] = 0;
            array[i++] = 0;
            array[i++] = 0;
            return;
        }
        array[i++] = data.x; // position of center
        array[i++] = data.y;
        array[i++] = data.size; // size
        array[i++] = (0, utils_1.floatColor)(data.color); // color
        array[i++] = data.inactive ? 1 : 0; // inactive property
        // Reference texture:
        if (!imageState || imageState.status !== 'ready') {
            imageState = this.imageLoader.images[defaultImage.id];
        }
        if (imageState && imageState.status === 'ready') {
            var _a = this.imageLoader.textureImage, width = _a.width, height = _a.height;
            array[i++] = imageState.x / width;
            array[i++] = imageState.y / height;
            array[i++] = imageState.width / width;
            array[i++] = imageState.height / height;
        }
        else {
            array[i++] = 0;
            array[i++] = 0;
            array[i++] = 0;
            array[i++] = 0;
        }
    };
    NodeImageProgram.prototype.render = function (params) {
        if (this.hasNothingToRender())
            return;
        this.latestRenderParams = params;
        var gl = this.gl;
        var program = this.program;
        gl.useProgram(program);
        gl.uniform1f(this.ratioLocation, 1 / Math.sqrt(params.ratio));
        gl.uniform1f(this.scaleLocation, params.scalingRatio);
        gl.uniformMatrix3fv(this.matrixLocation, false, params.matrix);
        gl.uniform1i(this.atlasLocation, 0);
        gl.drawArrays(gl.POINTS, 0, this.array.length / ATTRIBUTES);
    };
    NodeImageProgram.prototype.rebindTexture = function () {
        var gl = this.gl;
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.imageLoader.textureImage);
        gl.generateMipmap(gl.TEXTURE_2D);
        if (this.latestRenderParams) {
            this.bind();
            this.bufferData();
            this.render(this.latestRenderParams);
        }
    };
    return NodeImageProgram;
}(node_1.AbstractNodeProgram));
exports.NodeImageProgram = NodeImageProgram;
