import { cap_lookup, hatch_pattern_to_index, join_lookup } from "./webgl_utils";
import { assert } from "../../../core/util/assert";
import { color2rgba } from "../../../core/util/color";
// Arrays are sent to GPU using ReGL Buffer objects.  CPU-side arrays used to
// update the Buffers are also kept for reuse to avoid unnecessary reallocation.
class WrappedBuffer {
    static __name__ = "WrappedBuffer";
    regl_wrapper;
    buffer;
    array;
    is_scalar;
    // Number of buffer elements per rendered primitive, e.g. for RGBA buffers this is 4
    // as a single color is 4 x uint8 = 32-bit in total.
    elements_per_primitive;
    constructor(regl_wrapper, elements_per_primitive = 1) {
        this.regl_wrapper = regl_wrapper;
        this.is_scalar = true;
        this.elements_per_primitive = elements_per_primitive;
    }
    // Return array if already know it exists and is the correct length.
    get_array() {
        assert(this.array != null, "WrappedBuffer not yet initialised");
        return this.array;
    }
    // Return array of correct size, creating it if necessary.
    // Must call update() when finished setting the array values.
    get_sized_array(length) {
        if (this.array == null || this.array.length != length) {
            this.array = this.new_array(length);
        }
        return this.array;
    }
    is_normalized() {
        return false;
    }
    get length() {
        return this.array != null ? this.array.length : 0;
    }
    set_from_array(numbers) {
        const len = numbers.length;
        const array = this.get_sized_array(len);
        for (let i = 0; i < len; i++) {
            array[i] = numbers[i];
        }
        this.update();
    }
    set_from_prop(prop) {
        const len = prop.is_Scalar() ? 1 : prop.length;
        const array = this.get_sized_array(len);
        for (let i = 0; i < len; i++) {
            array[i] = prop.get(i);
        }
        this.update(prop.is_Scalar());
    }
    set_from_scalar(scalar) {
        this.get_sized_array(1).fill(scalar);
        this.update(true);
    }
    // Return a ReGL AttributeConfig that corresponds to one value for each glyph
    // or the same value for a number of glyphs.  A buffer passed to ReGL for
    // instanced rendering can be used for multiple rendering calls and the
    // important attributes for this are the offset (in bytes) into the buffer
    // and the divisor, which is the number of instances rendered before the
    // offset is advanced to the next buffer element.
    // to_attribute_config() is used for the common case of a single render call
    // per buffer with visual properties that are either scalar or vector.
    // Visual properties of scatter markers are an good example, and scalar_divisor
    // would be the number of markers rendered.
    to_attribute_config(offset = 0, scalar_divisor = 1) {
        return {
            buffer: this.buffer,
            divisor: this.is_scalar ? scalar_divisor : 1,
            normalized: this.is_normalized(),
            offset: offset * this.bytes_per_element(),
        };
    }
    // to_attribute_config_nested() is used for the more complicated case in
    // which the vectorisation is nested, such as rendering multi_lines where
    // each visual property has a single buffer that is used multiple times, once
    // for each of the constituent lines.  Vector properties are therefore
    // constant for each constituent line (composed of multiple rendered
    // instances) but change between lines.
    to_attribute_config_nested(offset_vector = 0, divisor = 1) {
        return {
            buffer: this.buffer,
            divisor: divisor * this.elements_per_primitive,
            normalized: this.is_normalized(),
            offset: this.is_scalar ? 0 : offset_vector * this.bytes_per_element() * this.elements_per_primitive,
        };
    }
    // Update ReGL buffer with data contained in array in preparation for passing
    // it to the GPU.  This function must be called after get_sized_array().
    update(is_scalar = false) {
        // Update buffer with data contained in array.
        if (this.buffer == null) {
            // Create new buffer.
            this.buffer = this.regl_wrapper.buffer({
                usage: "dynamic",
                data: this.array,
            });
        }
        else {
            // Reuse existing buffer.
            this.buffer({ data: this.array });
        }
        this.is_scalar = is_scalar;
    }
}
export class Float32Buffer extends WrappedBuffer {
    static __name__ = "Float32Buffer";
    bytes_per_element() {
        return Float32Array.BYTES_PER_ELEMENT;
    }
    new_array(len) {
        return new Float32Array(len);
    }
}
export class Uint8Buffer extends WrappedBuffer {
    static __name__ = "Uint8Buffer";
    bytes_per_element() {
        return Uint8Array.BYTES_PER_ELEMENT;
    }
    new_array(len) {
        return new Uint8Array(len);
    }
    set_from_color(color_prop, alpha_prop) {
        const is_scalar = color_prop.is_Scalar() && alpha_prop.is_Scalar();
        const ncolors = is_scalar ? 1 : color_prop.length;
        const array = this.get_sized_array(4 * ncolors);
        for (let i = 0; i < ncolors; i++) {
            const [r, g, b, a] = color2rgba(color_prop.get(i), alpha_prop.get(i));
            array[4 * i] = r;
            array[4 * i + 1] = g;
            array[4 * i + 2] = b;
            array[4 * i + 3] = a;
        }
        this.update(is_scalar);
    }
    set_from_hatch_pattern(hatch_pattern_prop) {
        const len = hatch_pattern_prop.is_Scalar() ? 1 : hatch_pattern_prop.length;
        const array = this.get_sized_array(len);
        for (let i = 0; i < len; i++) {
            array[i] = hatch_pattern_to_index(hatch_pattern_prop.get(i));
        }
        this.update(hatch_pattern_prop.is_Scalar());
    }
    set_from_line_cap(line_cap_prop) {
        const len = line_cap_prop.is_Scalar() ? 1 : line_cap_prop.length;
        const array = this.get_sized_array(len);
        for (let i = 0; i < len; i++) {
            array[i] = cap_lookup[line_cap_prop.get(i)];
        }
        this.update(line_cap_prop.is_Scalar());
    }
    set_from_line_join(line_join_prop) {
        const len = line_join_prop.is_Scalar() ? 1 : line_join_prop.length;
        const array = this.get_sized_array(len);
        for (let i = 0; i < len; i++) {
            array[i] = join_lookup[line_join_prop.get(i)];
        }
        this.update(line_join_prop.is_Scalar());
    }
}
// Normalized refers to optional WebGL behaviour of automatically converting
// Uint8 values that are passed to shaders into floats in the range 0 to 1.
export class NormalizedUint8Buffer extends Uint8Buffer {
    static __name__ = "NormalizedUint8Buffer";
    is_normalized() {
        return true;
    }
}
//# sourceMappingURL=buffer.js.map