'use strict';

const browser = require('../util/browser');
const pixelsToTileUnits = require('../source/pixels_to_tile_units');

/**
 * Draw a line. Under the hood this will read elements from
 * a tile, dash textures from a lineAtlas, and style properties from a layer.
 * @param {Object} painter
 * @param {Object} layer
 * @param {Object} posMatrix
 * @param {Tile} tile
 * @returns {undefined} draws with the painter
 * @private
 */
module.exports = function drawLine(painter, sourceCache, layer, coords) {
    if (painter.isOpaquePass) return;
    painter.setDepthSublayer(0);
    painter.depthMask(false);

    const gl = painter.gl;
    gl.enable(gl.STENCIL_TEST);

    // don't draw zero-width lines
    if (layer.paint['line-width'] <= 0) return;

    const programId =
        layer.paint['line-dasharray'] ? 'lineSDF' :
        layer.paint['line-pattern'] ? 'linePattern' : 'line';

    let prevTileZoom;
    let firstTile = true;

    for (const coord of coords) {
        const tile = sourceCache.getTile(coord);
        const bucket = tile.getBucket(layer);
        if (!bucket) continue;

        const layerData = bucket.buffers.layerData[layer.id];
        const prevProgram = painter.currentProgram;
        const program = painter.useProgram(programId, layerData.programConfiguration);
        const programChanged = firstTile || program !== prevProgram;
        const tileRatioChanged = prevTileZoom !== tile.coord.z;

        if (programChanged) {
            layerData.programConfiguration.setUniforms(painter.gl, program, layer, {zoom: painter.transform.zoom});
        }
        drawLineTile(program, painter, tile, bucket.buffers, layer, coord, layerData, programChanged, tileRatioChanged);
        prevTileZoom = tile.coord.z;
        firstTile = false;
    }
};

function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, programChanged, tileRatioChanged) {
    const gl = painter.gl;
    const dasharray = layer.paint['line-dasharray'];
    const image = layer.paint['line-pattern'];

    let posA, posB, imagePosA, imagePosB;

    if (programChanged || tileRatioChanged) {
        const tileRatio = 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom);

        if (dasharray) {
            posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round');
            posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round');

            const widthA = posA.width * dasharray.fromScale;
            const widthB = posB.width * dasharray.toScale;

            gl.uniform2f(program.u_patternscale_a, tileRatio / widthA, -posA.height / 2);
            gl.uniform2f(program.u_patternscale_b, tileRatio / widthB, -posB.height / 2);
            gl.uniform1f(program.u_sdfgamma, painter.lineAtlas.width / (Math.min(widthA, widthB) * 256 * browser.devicePixelRatio) / 2);

        } else if (image) {
            imagePosA = painter.spriteAtlas.getPosition(image.from, true);
            imagePosB = painter.spriteAtlas.getPosition(image.to, true);
            if (!imagePosA || !imagePosB) return;

            gl.uniform2f(program.u_pattern_size_a, imagePosA.size[0] * image.fromScale / tileRatio, imagePosB.size[1]);
            gl.uniform2f(program.u_pattern_size_b, imagePosB.size[0] * image.toScale / tileRatio, imagePosB.size[1]);
        }

        gl.uniform2f(program.u_gl_units_to_pixels, 1 / painter.transform.pixelsToGLUnits[0], 1 / painter.transform.pixelsToGLUnits[1]);
    }

    if (programChanged) {

        if (dasharray) {
            gl.uniform1i(program.u_image, 0);
            gl.activeTexture(gl.TEXTURE0);
            painter.lineAtlas.bind(gl);

            gl.uniform1f(program.u_tex_y_a, posA.y);
            gl.uniform1f(program.u_tex_y_b, posB.y);
            gl.uniform1f(program.u_mix, dasharray.t);

        } else if (image) {
            gl.uniform1i(program.u_image, 0);
            gl.activeTexture(gl.TEXTURE0);
            painter.spriteAtlas.bind(gl, true);

            gl.uniform2fv(program.u_pattern_tl_a, imagePosA.tl);
            gl.uniform2fv(program.u_pattern_br_a, imagePosA.br);
            gl.uniform2fv(program.u_pattern_tl_b, imagePosB.tl);
            gl.uniform2fv(program.u_pattern_br_b, imagePosB.br);
            gl.uniform1f(program.u_fade, image.t);
        }
        gl.uniform1f(program.u_width, layer.paint['line-width']);
    }

    painter.enableTileClippingMask(coord);

    const posMatrix = painter.translatePosMatrix(coord.posMatrix, tile, layer.paint['line-translate'], layer.paint['line-translate-anchor']);
    gl.uniformMatrix4fv(program.u_matrix, false, posMatrix);

    gl.uniform1f(program.u_ratio, 1 / pixelsToTileUnits(tile, 1, painter.transform.zoom));

    for (const segment of buffers.segments) {
        segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, layerData.paintVertexBuffer, segment.vertexOffset);
        gl.drawElements(gl.TRIANGLES, segment.primitiveLength * 3, gl.UNSIGNED_SHORT, segment.primitiveOffset * 3 * 2);
    }
}
