import createRegl from "regl";
import { DashCache } from "./dash_cache";
import accumulate_vertex_shader from "./accumulate.vert";
import accumulate_fragment_shader from "./accumulate.frag";
import image_vertex_shader from "./image.vert";
import image_fragment_shader from "./image.frag";
import line_vertex_shader from "./regl_line.vert";
import line_fragment_shader from "./regl_line.frag";
import marker_vertex_shader from "./marker.vert";
import marker_fragment_shader from "./marker.frag";
// All access to regl is performed via the get_regl() function that returns a
// ReglWrapper object.  This ensures that regl is correctly initialised before
// it is used, and is only initialised once.
let regl_wrapper = null;
export function get_regl(gl) {
    if (regl_wrapper == null) {
        regl_wrapper = new ReglWrapper(gl);
    }
    return regl_wrapper;
}
export class ReglWrapper {
    static __name__ = "ReglWrapper";
    _regl;
    _regl_available;
    _dash_cache;
    // Drawing functions.
    _accumulate;
    _image;
    _solid_line;
    _dashed_line;
    _marker_no_hatch_map = new Map();
    _marker_hatch_map = new Map();
    // Static Buffers/Elements
    _line_geometry;
    _line_triangles;
    _rect_geometry;
    _rect_triangles;
    // WebGL state variables.
    _scissor;
    _viewport;
    // WebGL framebuffer used to accumulate glyph rendering before single blit to Canvas.
    _framebuffer;
    _framebuffer_texture;
    constructor(gl) {
        try {
            this._regl = createRegl({
                gl,
                extensions: [
                    "ANGLE_instanced_arrays",
                    "EXT_blend_minmax",
                ],
            });
            this._regl_available = true;
            // Initialise static Buffers/Elements.
            this._line_geometry = this._regl.buffer({
                usage: "static",
                type: "float",
                data: [[-2, 0], [-1, -1], [1, -1], [1, 1], [-1, 1]],
            });
            this._line_triangles = this._regl.elements({
                usage: "static",
                primitive: "triangle fan",
                data: [0, 1, 2, 3, 4],
            });
            this._rect_geometry = this._regl.buffer({
                usage: "static",
                type: "float",
                data: [[-1, -1], [1, -1], [1, 1], [-1, 1]],
            });
            this._rect_triangles = this._regl.elements({
                usage: "static",
                primitive: "triangle fan",
                data: [0, 1, 2, 3],
            });
        }
        catch (err) {
            this._regl_available = false;
        }
    }
    // Create and return ReGL Buffer.
    buffer(options) {
        return this._regl.buffer(options);
    }
    clear(width, height) {
        this._viewport = { x: 0, y: 0, width, height };
        this._regl.clear({ color: [0, 0, 0, 0] });
    }
    clear_framebuffer(framebuffer) {
        this._regl.clear({ color: [0, 0, 0, 0], framebuffer });
    }
    get framebuffer_and_texture() {
        const { _regl } = this;
        const { _gl } = _regl;
        const size = {
            height: _gl.drawingBufferHeight,
            width: _gl.drawingBufferWidth,
        };
        if (this._framebuffer_texture == null) {
            this._framebuffer_texture = _regl.texture(size);
        }
        else {
            // Resize texture, no-op if no change.
            this._framebuffer_texture(size);
        }
        if (this._framebuffer == null) {
            this._framebuffer = _regl.framebuffer({
                // Auto-sizes to size of texture.
                color: this._framebuffer_texture,
                depth: false,
                stencil: false,
            });
        }
        return [this._framebuffer, this._framebuffer_texture];
    }
    get has_webgl() {
        return this._regl_available;
    }
    get scissor() {
        return this._scissor;
    }
    set_scissor(x, y, width, height) {
        this._scissor = { x, y, width, height };
    }
    texture(options) {
        return this._regl.texture(options);
    }
    get viewport() {
        return this._viewport;
    }
    accumulate() {
        if (this._accumulate == null) {
            this._accumulate = regl_accumulate(this._regl, this._rect_geometry, this._rect_triangles);
        }
        return this._accumulate;
    }
    dashed_line() {
        if (this._dashed_line == null) {
            this._dashed_line = regl_dashed_line(this._regl, this._line_geometry, this._line_triangles);
        }
        return this._dashed_line;
    }
    get_dash(line_dash) {
        if (this._dash_cache == null) {
            this._dash_cache = new DashCache(this._regl);
        }
        return this._dash_cache.get(line_dash);
    }
    image() {
        if (this._image == null) {
            this._image = regl_image(this._regl, this._rect_geometry, this._rect_triangles);
        }
        return this._image;
    }
    marker_no_hatch(marker_type) {
        let func = this._marker_no_hatch_map.get(marker_type);
        if (func == null) {
            func = regl_marker(this._regl, marker_type);
            this._marker_no_hatch_map.set(marker_type, func);
        }
        return func;
    }
    marker_hatch(marker_type) {
        let func = this._marker_hatch_map.get(marker_type);
        if (func == null) {
            func = regl_marker_hatch(this._regl, marker_type);
            this._marker_hatch_map.set(marker_type, func);
        }
        return func;
    }
    solid_line() {
        if (this._solid_line == null) {
            this._solid_line = regl_solid_line(this._regl, this._line_geometry, this._line_triangles);
        }
        return this._solid_line;
    }
}
function regl_accumulate(regl, geometry, triangles) {
    const config = {
        vert: accumulate_vertex_shader,
        frag: accumulate_fragment_shader,
        attributes: {
            a_position: {
                buffer: geometry,
                divisor: 0,
            },
        },
        uniforms: {
            u_framebuffer_tex: regl.prop("framebuffer_tex"),
        },
        elements: triangles,
        blend: {
            enable: true,
            func: {
                srcRGB: "one",
                srcAlpha: "one",
                dstRGB: "one minus src alpha",
                dstAlpha: "one minus src alpha",
            },
        },
        depth: { enable: false },
        scissor: {
            enable: true,
            box: regl.prop("scissor"),
        },
        viewport: regl.prop("viewport"),
    };
    return regl(config);
}
// Regl rendering functions are here as some will be reused, e.g. lines may also
// be used around polygons or for bezier curves.
function regl_image(regl, geometry, triangles) {
    const config = {
        vert: image_vertex_shader,
        frag: image_fragment_shader,
        attributes: {
            a_position: {
                buffer: geometry,
                divisor: 0,
            },
            a_bounds(_, props) {
                return props.bounds.to_attribute_config();
            },
        },
        uniforms: {
            u_canvas_size: regl.prop("canvas_size"),
            u_tex: regl.prop("tex"),
            u_global_alpha: regl.prop("global_alpha"),
        },
        elements: triangles,
        blend: {
            enable: true,
            func: {
                srcRGB: "one",
                srcAlpha: "one",
                dstRGB: "one minus src alpha",
                dstAlpha: "one minus src alpha",
            },
        },
        depth: { enable: false },
        scissor: {
            enable: true,
            box: regl.prop("scissor"),
        },
        viewport: regl.prop("viewport"),
    };
    return regl(config);
}
// Mesh for line rendering (solid and dashed).
//
//   1       4-----3
//          /      |
//         /       |
// y 0    0        |
//         \       |
//          \      |
//  -1       1-----2
//
//       -2  -1    1
//              x
function regl_solid_line(regl, line_geometry, line_triangles) {
    const config = {
        vert: line_vertex_shader,
        frag: line_fragment_shader,
        attributes: {
            a_position: {
                buffer: line_geometry,
                divisor: 0,
            },
            a_point_prev(_, props) {
                return props.points.to_attribute_config(props.point_offset);
            },
            a_point_start(_, props) {
                return props.points.to_attribute_config(props.point_offset + 2);
            },
            a_point_end(_, props) {
                return props.points.to_attribute_config(props.point_offset + 4);
            },
            a_point_next(_, props) {
                return props.points.to_attribute_config(props.point_offset + 6);
            },
            a_show_prev(_, props) {
                return props.show.to_attribute_config(props.point_offset / 2 - props.line_offset);
            },
            a_show_curr(_, props) {
                return props.show.to_attribute_config(props.point_offset / 2 - props.line_offset + 1);
            },
            a_show_next(_, props) {
                return props.show.to_attribute_config(props.point_offset / 2 - props.line_offset + 2);
            },
            a_linewidth(_, props) {
                return props.linewidth.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_line_color(_, props) {
                return props.line_color.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_line_cap(_, props) {
                return props.line_cap.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_line_join(_, props) {
                return props.line_join.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
        },
        uniforms: {
            u_canvas_size: regl.prop("canvas_size"),
            u_antialias: regl.prop("antialias"),
            u_miter_limit: regl.prop("miter_limit"),
        },
        elements: line_triangles,
        instances: regl.prop("nsegments"),
        blend: {
            enable: true,
            equation: "max",
            func: {
                srcRGB: 1,
                srcAlpha: 1,
                dstRGB: 1,
                dstAlpha: 1,
            },
        },
        depth: { enable: false },
        framebuffer: regl.prop("framebuffer"),
        scissor: {
            enable: true,
            box: regl.prop("scissor"),
        },
        viewport: regl.prop("viewport"),
    };
    return regl(config);
}
function regl_dashed_line(regl, line_geometry, line_triangles) {
    const config = {
        vert: `\
#define DASHED
${line_vertex_shader}
`,
        frag: `\
#define DASHED
${line_fragment_shader}
`,
        attributes: {
            a_position: {
                buffer: line_geometry,
                divisor: 0,
            },
            a_point_prev(_, props) {
                return props.points.to_attribute_config(props.point_offset);
            },
            a_point_start(_, props) {
                return props.points.to_attribute_config(props.point_offset + 2);
            },
            a_point_end(_, props) {
                return props.points.to_attribute_config(props.point_offset + 4);
            },
            a_point_next(_, props) {
                return props.points.to_attribute_config(props.point_offset + 6);
            },
            a_show_prev(_, props) {
                return props.show.to_attribute_config(props.point_offset / 2 - props.line_offset);
            },
            a_show_curr(_, props) {
                return props.show.to_attribute_config(props.point_offset / 2 - props.line_offset + 1);
            },
            a_show_next(_, props) {
                return props.show.to_attribute_config(props.point_offset / 2 - props.line_offset + 2);
            },
            a_linewidth(_, props) {
                return props.linewidth.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_line_color(_, props) {
                return props.line_color.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_line_cap(_, props) {
                return props.line_cap.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_line_join(_, props) {
                return props.line_join.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_length_so_far(_, props) {
                return props.length_so_far.to_attribute_config(props.point_offset / 2 - 3 * props.line_offset);
            },
            a_dash_tex_info(_, props) {
                return props.dash_tex_info.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_dash_scale(_, props) {
                return props.dash_scale.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
            a_dash_offset(_, props) {
                return props.dash_offset.to_attribute_config_nested(props.line_offset, props.nsegments + 3);
            },
        },
        uniforms: {
            u_canvas_size: regl.prop("canvas_size"),
            u_antialias: regl.prop("antialias"),
            u_miter_limit: regl.prop("miter_limit"),
            u_dash_tex: regl.prop("dash_tex"),
        },
        elements: line_triangles,
        instances: regl.prop("nsegments"),
        blend: {
            enable: true,
            equation: "max",
            func: {
                srcRGB: 1,
                srcAlpha: 1,
                dstRGB: 1,
                dstAlpha: 1,
            },
        },
        depth: { enable: false },
        framebuffer: regl.prop("framebuffer"),
        scissor: {
            enable: true,
            box: regl.prop("scissor"),
        },
        viewport: regl.prop("viewport"),
    };
    return regl(config);
}
function regl_marker(regl, marker_type, vert_defs = [], frag_defs = [], attributes) {
    const vert_prefix = vert_defs.map((def) => `#define ${def}`).join("\n");
    const frag_prefix = frag_defs.map((def) => `#define ${def}`).join("\n");
    const config = {
        vert: `\
${vert_prefix}
#define MULTI_MARKER
#define USE_${marker_type.toUpperCase()}
${marker_vertex_shader}
`,
        frag: `\
${frag_prefix}
#define USE_${marker_type.toUpperCase()}
${marker_fragment_shader}
`,
        attributes: {
            a_position: {
                buffer: regl.buffer([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]]),
                divisor: 0,
            },
            a_center(_, props) {
                return props.center.to_attribute_config(0, props.nmarkers);
            },
            a_width(_, props) {
                return props.width.to_attribute_config(0, props.nmarkers);
            },
            a_height(_, props) {
                return props.height.to_attribute_config(0, props.nmarkers);
            },
            a_angle(_, props) {
                return props.angle.to_attribute_config(0, props.nmarkers);
            },
            a_aux(_, props) {
                return props.aux.to_attribute_config(0, props.nmarkers);
            },
            a_linewidth(_, props) {
                return props.linewidth.to_attribute_config(0, props.nmarkers);
            },
            a_line_color(_, props) {
                return props.line_color.to_attribute_config(0, props.nmarkers);
            },
            a_fill_color(_, props) {
                return props.fill_color.to_attribute_config(0, props.nmarkers);
            },
            a_line_cap(_, props) {
                return props.line_cap.to_attribute_config(0, props.nmarkers);
            },
            a_line_join(_, props) {
                return props.line_join.to_attribute_config(0, props.nmarkers);
            },
            a_show(_, props) {
                return props.show.to_attribute_config(0, props.nmarkers);
            },
            ...attributes,
        },
        uniforms: {
            u_canvas_size: regl.prop("canvas_size"),
            u_antialias: regl.prop("antialias"),
            u_size_hint: regl.prop("size_hint"),
            u_border_radius: regl.prop("border_radius"),
        },
        count: 4,
        primitive: "triangle fan",
        instances: regl.prop("nmarkers"),
        blend: {
            enable: true,
            func: {
                srcRGB: "one",
                srcAlpha: "one",
                dstRGB: "one minus src alpha",
                dstAlpha: "one minus src alpha",
            },
        },
        depth: { enable: false },
        scissor: {
            enable: true,
            box: regl.prop("scissor"),
        },
        viewport: regl.prop("viewport"),
    };
    return regl(config);
}
function regl_marker_hatch(regl, marker_type) {
    const hatch_attributes = {
        a_hatch_pattern(_, props) {
            return props.hatch_pattern.to_attribute_config(0, props.nmarkers);
        },
        a_hatch_scale(_, props) {
            return props.hatch_scale.to_attribute_config(0, props.nmarkers);
        },
        a_hatch_weight(_, props) {
            return props.hatch_weight.to_attribute_config(0, props.nmarkers);
        },
        a_hatch_color(_, props) {
            return props.hatch_color.to_attribute_config(0, props.nmarkers);
        },
    };
    return regl_marker(regl, marker_type, ["HATCH"], ["HATCH"], hatch_attributes);
}
//# sourceMappingURL=regl_wrap.js.map