import Pbf from 'pbf';
import Point from '@mapbox/point-geometry';

class FeatureWrapper {
    constructor(feature, extent) {
        this.feature = feature;
        this.type = feature.type;
        this.properties = feature.tags ? feature.tags : {};
        this.extent = extent;
        // If the feature has a top-level `id` property, copy it over, but only
        // if it can be coerced to an integer, because this wrapper is used for
        // serializing geojson feature data into vector tile PBF data, and the
        // vector tile spec only supports integer values for feature ids --
        // allowing non-integer values here results in a non-compliant PBF
        // that causes an exception when it is parsed with vector-tile-js
        if ('id' in feature) {
            if (typeof feature.id === 'string') {
                this.id = parseInt(feature.id, 10);
            }
            else if (typeof feature.id === 'number' && !isNaN(feature.id)) {
                this.id = feature.id;
            }
        }
    }
    loadGeometry() {
        const geometry = [];
        const rawGeo = this.feature.type === 1 ? [this.feature.geometry] : this.feature.geometry;
        for (const ring of rawGeo) {
            const newRing = [];
            for (const point of ring) {
                newRing.push(new Point(point[0], point[1]));
            }
            geometry.push(newRing);
        }
        return geometry;
    }
}
const GEOJSON_TILE_LAYER_NAME = "_geojsonTileLayer";
class GeoJSONWrapper {
    constructor(features, options) {
        this.layers = { [GEOJSON_TILE_LAYER_NAME]: this };
        this.name = GEOJSON_TILE_LAYER_NAME;
        this.version = options ? options.version : 1;
        this.extent = options ? options.extent : 4096;
        this.length = features.length;
        this.features = features;
    }
    feature(i) {
        return new FeatureWrapper(this.features[i], this.extent);
    }
}

/**
 * Serialize a vector-tile-js-created tile to pbf
 *
 * @param tile - the tile to serialize
 * @param jsonPrefix - a string prefix to prepend to JSON-stringified non-primitive property values, used to distinguish them from regular string values when parsing the tile later. Default is "".
 * @return uncompressed, pbf-serialized tile data
 */
function fromVectorTileJs(tile, jsonPrefix = "") {
    const out = new Pbf();
    writeTile(tile, out, jsonPrefix);
    return out.finish();
}
/**
 * Serialized a geojson-vt-created tile to pbf.
 *
 * @param layers - An object mapping layer names to geojson-vt-created vector tile objects
 * @param options - An object specifying the vector-tile specification version and extent that were used to create `layers`.
 * @return uncompressed, pbf-serialized tile data
 */
function fromGeojsonVt(layers, options) {
    const l = {};
    // eslint-disable-next-line @typescript-eslint/no-for-in-array
    for (const k in layers) {
        l[k] = new GeoJSONWrapper(layers[k].features, options);
        l[k].name = k;
        l[k].version = options ? options.version : 1;
        l[k].extent = options ? options.extent : 4096;
    }
    return fromVectorTileJs({ layers: l });
}
function writeTile(tile, pbf, jsonPrefix = "") {
    for (const key in tile.layers) {
        pbf.writeMessage(3, (layer, pbf) => writeLayer(layer, pbf, jsonPrefix), tile.layers[key]);
    }
}
function writeLayer(layer, pbf, jsonPrefix = "") {
    pbf.writeVarintField(15, layer.version || 1);
    pbf.writeStringField(1, layer.name || '');
    pbf.writeVarintField(5, layer.extent || 4096);
    const context = {
        jsonPrefix,
        keys: [],
        values: [],
        keycache: {},
        valuecache: {}
    };
    for (let i = 0; i < layer.length; i++) {
        context.feature = layer.feature(i);
        pbf.writeMessage(2, writeFeature, context);
    }
    const keys = context.keys;
    for (const key of keys) {
        pbf.writeStringField(3, key);
    }
    const values = context.values;
    for (const value of values) {
        pbf.writeMessage(4, writeValue, value);
    }
}
function writeFeature(context, pbf) {
    if (!context.feature) {
        return;
    }
    const feature = context.feature;
    if (feature.id !== undefined) {
        pbf.writeVarintField(1, feature.id);
    }
    pbf.writeMessage(2, writeProperties, context);
    pbf.writeVarintField(3, feature.type);
    pbf.writeMessage(4, writeGeometry, feature);
}
function writeProperties(context, pbf) {
    for (const key in context.feature?.properties) {
        let value = context.feature.properties[key];
        let keyIndex = context.keycache[key];
        if (value == null)
            continue; // don't encode null/undefined value properties
        if (typeof keyIndex === 'undefined') {
            context.keys.push(key);
            keyIndex = context.keys.length - 1;
            context.keycache[key] = keyIndex;
        }
        pbf.writeVarint(keyIndex);
        if (typeof value !== 'string' && typeof value !== 'boolean' && typeof value !== 'number') {
            value = context.jsonPrefix + JSON.stringify(value);
        }
        const valueKey = typeof value + ':' + value;
        let valueIndex = context.valuecache[valueKey];
        if (typeof valueIndex === 'undefined') {
            context.values.push(value);
            valueIndex = context.values.length - 1;
            context.valuecache[valueKey] = valueIndex;
        }
        pbf.writeVarint(valueIndex);
    }
}
function command(cmd, length) {
    return (length << 3) + (cmd & 0x7);
}
function zigzag(num) {
    return (num << 1) ^ (num >> 31);
}
function writeGeometry(feature, pbf) {
    const geometry = feature.loadGeometry();
    const type = feature.type;
    let x = 0;
    let y = 0;
    for (const ring of geometry) {
        let count = 1;
        if (type === 1) {
            count = ring.length;
        }
        pbf.writeVarint(command(1, count)); // moveto
        // do not write polygon closing path as lineto
        const lineCount = type === 3 ? ring.length - 1 : ring.length;
        for (let i = 0; i < lineCount; i++) {
            if (i === 1 && type !== 1) {
                pbf.writeVarint(command(2, lineCount - 1)); // lineto
            }
            const dx = ring[i].x - x;
            const dy = ring[i].y - y;
            pbf.writeVarint(zigzag(dx));
            pbf.writeVarint(zigzag(dy));
            x += dx;
            y += dy;
        }
        if (feature.type === 3) {
            pbf.writeVarint(command(7, 1)); // closepath
        }
    }
}
function writeValue(value, pbf) {
    const type = typeof value;
    if (type === 'string') {
        pbf.writeStringField(1, value);
    }
    else if (type === 'boolean') {
        pbf.writeBooleanField(7, value);
    }
    else if (type === 'number') {
        if (value % 1 !== 0) {
            pbf.writeDoubleField(3, value);
        }
        else if (value < 0) {
            pbf.writeSVarintField(6, value);
        }
        else {
            pbf.writeVarintField(5, value);
        }
    }
}

export { GEOJSON_TILE_LAYER_NAME, GeoJSONWrapper, fromGeojsonVt, fromVectorTileJs };
//# sourceMappingURL=index.es.js.map
