/* *
 *
 *  Copyright (c) 2019-2019 Highsoft AS
 *
 *  Boost module: stripped-down renderer for higher performance
 *
 *  License: highcharts.com/license
 *
 * */

'use strict';

/**
 * Vertex Buffer abstraction.
 * A vertex buffer is a set of vertices which are passed to the GPU
 * in a single call.
 *
 * @private
 * @function GLVertexBuffer
 *
 * @param {WebGLContext} gl
 *        the context in which to create the buffer
 *
 * @param {GLShader} shader
 *        the shader to use
 *
 * @return {*}
 */
function GLVertexBuffer(gl, shader, dataComponents /* , type */) {
    var buffer = false,
        vertAttribute = false,
        components = dataComponents || 2,
        preAllocated = false,
        iterator = 0,
        // farray = false,
        data;

    // type = type || 'float';

    function destroy() {
        if (buffer) {
            gl.deleteBuffer(buffer);
            buffer = false;
            vertAttribute = false;
        }

        iterator = 0;
        components = dataComponents || 2;
        data = [];
    }

    /**
     * Build the buffer
     * @private
     * @param dataIn {Array<float>} - a 0 padded array of indices
     * @param attrib {String} - the name of the Attribute to bind the buffer to
     * @param dataComponents {Integer} - the number of components per. indice
     */
    function build(dataIn, attrib, dataComponents) {
        var farray;

        data = dataIn || [];

        if ((!data || data.length === 0) && !preAllocated) {
            // console.error('trying to render empty vbuffer');
            destroy();
            return false;
        }

        components = dataComponents || components;

        if (buffer) {
            gl.deleteBuffer(buffer);
        }

        if (!preAllocated) {
            farray = new Float32Array(data);
        }

        buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(
            gl.ARRAY_BUFFER,
            preAllocated || farray,
            gl.STATIC_DRAW
        );

        // gl.bindAttribLocation(shader.program(), 0, 'aVertexPosition');
        vertAttribute = gl.getAttribLocation(shader.program(), attrib);
        gl.enableVertexAttribArray(vertAttribute);

        // Trigger cleanup
        farray = false;

        return true;
    }

    /**
     * Bind the buffer
     * @private
     */
    function bind() {
        if (!buffer) {
            return false;
        }

        // gl.bindAttribLocation(shader.program(), 0, 'aVertexPosition');
        // gl.enableVertexAttribArray(vertAttribute);
        // gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.vertexAttribPointer(
            vertAttribute, components, gl.FLOAT, false, 0, 0
        );
        // gl.enableVertexAttribArray(vertAttribute);
    }

    /**
     * Render the buffer
     * @private
     * @param from {Integer} - the start indice
     * @param to {Integer} - the end indice
     * @param drawMode {String} - the draw mode
     */
    function render(from, to, drawMode) {
        var length = preAllocated ? preAllocated.length : data.length;

        if (!buffer) {
            return false;
        }

        if (!length) {
            return false;
        }

        if (!from || from > length || from < 0) {
            from = 0;
        }

        if (!to || to > length) {
            to = length;
        }

        drawMode = drawMode || 'points';

        gl.drawArrays(
            gl[drawMode.toUpperCase()],
            from / components,
            (to - from) / components
        );

        return true;
    }

    function push(x, y, a, b) {
        if (preAllocated) { // && iterator <= preAllocated.length - 4) {
            preAllocated[++iterator] = x;
            preAllocated[++iterator] = y;
            preAllocated[++iterator] = a;
            preAllocated[++iterator] = b;
        }
    }

    /**
     * Note about pre-allocated buffers:
     *     - This is slower for charts with many series
     * @private
     */
    function allocate(size) {
        size *= 4;
        iterator = -1;

        preAllocated = new Float32Array(size);
    }

    // /////////////////////////////////////////////////////////////////////////
    return {
        destroy: destroy,
        bind: bind,
        data: data,
        build: build,
        render: render,
        allocate: allocate,
        push: push
    };
}

export default GLVertexBuffer;
