import * as maptalks from 'maptalks';
import simpleheat from 'simpleheat';

const options = {
    max: 1,
    gradient: {
        0.4: 'blue',
        0.6: 'cyan',
        0.7: 'lime',
        0.8: 'yellow',
        1.0: 'red',
    },
    radius: 25,
    blur: 15,
    heatValueScale: 1,
    minOpacity: 0.05,
};
class HeatLayer extends maptalks.Layer {
    constructor(id, heats, options) {
        if (!Array.isArray(heats)) {
            options = heats;
            // @ts-ignore
            heats = null;
        }
        super(id, options);
        this._heats = heats || [];
    }
    getData() {
        return this._heats;
    }
    setData(heats) {
        this._heats = heats || [];
        return this.redraw();
    }
    addPoint(heat) {
        if (!heat) {
            return this;
        }
        if (heat[0] && Array.isArray(heat[0])) {
            maptalks.Util.pushIn(this._heats, heat);
        }
        else {
            this._heats.push(heat);
        }
        return this.redraw();
    }
    onConfig(conf) {
        for (const p in conf) {
            // @ts-ignore
            if (options[p]) {
                return this.redraw();
            }
        }
        return this;
    }
    redraw() {
        const renderer = this._getRenderer();
        if (renderer) {
            renderer.clearHeatCache();
            renderer.setToRedraw();
        }
        return this;
    }
    isEmpty() {
        if (!this._heats.length) {
            return true;
        }
        return false;
    }
    clear() {
        this._heats = [];
        this.redraw();
        this.fire('clear');
        return this;
    }
    /**
     * Export the HeatLayer's JSON.
     * @return {Object} layer's JSON
     */
    toJSON(options) {
        if (!options) {
            options = {};
        }
        const json = {
            type: this.getJSONType(),
            id: this.getId(),
            options: this.config(),
        };
        const data = this.getData();
        if (options['clipExtent']) {
            let clipExtent = new maptalks.Extent(options['clipExtent']);
            const r = this._getHeatRadius();
            if (r) {
                clipExtent = clipExtent._expand(r);
            }
            const clipped = [];
            for (let i = 0, len = data.length; i < len; i++) {
                if (clipExtent.contains(new maptalks.Coordinate(data[i][0], data[i][1]))) {
                    clipped.push(data[i]);
                }
            }
            // @ts-ignore
            json['data'] = clipped;
        }
        else {
            // @ts-ignore
            json['data'] = data;
        }
        return json;
    }
    /**
     * Reproduce a HeatLayer from layer's JSON.
     * @param  {Object} json - layer's JSON
     * @return {maptalks.HeatLayer}
     * @static
     * @private
     * @function
     */
    static fromJSON(json) {
        if (!json || json['type'] !== 'HeatLayer') {
            return null;
        }
        return new HeatLayer(json['id'], json['data'], json['options']);
    }
    _getHeatRadius() {
        if (!this._getRenderer()) {
            return null;
        }
        return this._getRenderer()._heatRadius;
    }
}
HeatLayer.mergeOptions(options);
HeatLayer.registerJSONType('HeatLayer');
HeatLayer.registerRenderer('canvas', class extends maptalks.renderer.CanvasRenderer {
    draw() {
        const map = this.getMap(), layer = this.layer, extent = map.getContainerExtent();
        let maskExtent = this.prepareCanvas(), displayExtent = extent;
        if (maskExtent) {
            maskExtent = maskExtent.convertTo((c) => map._pointToContainerPoint(c));
            //out of layer mask
            if (!maskExtent.intersects(extent)) {
                this.completeRender();
                return;
            }
            displayExtent = extent.intersection(maskExtent);
        }
        if (!this._heater) {
            this._heater = simpleheat(this.canvas);
        }
        this._heater.radius(layer.options['radius'] || this._heater.defaultRadius, layer.options['blur']);
        if (layer.options['gradient']) {
            this._heater.gradient(layer.options['gradient']);
        }
        this._heater.max(layer.options['max']);
        //a cache of heat points' viewpoints.
        if (!this._heatViews) {
            this._heatViews = [];
        }
        const heats = layer.getData();
        if (heats.length === 0) {
            this.completeRender();
            return;
        }
        const data = this._heatData(heats, displayExtent);
        this._heater.data(data).draw(layer.options['minOpacity']);
        this.completeRender();
    }
    drawOnInteracting() {
        this.draw();
    }
    _heatData(heats, displayExtent) {
        const map = this.getMap(), layer = this.layer;
        const projection = map.getProjection();
        const data = [], r = this._heater._r, max = layer.options['max'] === undefined ? 1 : layer.options['max'], cellSize = r / 2, 
        // @ts-ignore
        grid = [], panePos = map.offsetPlatform(), offsetX = panePos.x % cellSize, offsetY = panePos.y % cellSize;
        let heat, p, cell, x, y, k;
        displayExtent = displayExtent
            .expand(r)
            .convertTo((c) => new maptalks.Point(map._containerPointToPrj(c)));
        this._heatRadius = r;
        for (let i = 0, l = heats.length; i < l; i++) {
            heat = heats[i];
            if (!this._heatViews[i]) {
                this._heatViews[i] = projection.project(new maptalks.Coordinate(heat[0], heat[1]));
            }
            p = this._heatViews[i];
            if (displayExtent.contains(p)) {
                p = map._prjToContainerPoint(p);
                x = Math.floor((p.x - offsetX) / cellSize) + 2;
                y = Math.floor((p.y - offsetY) / cellSize) + 2;
                k = (heat[2] !== undefined ? +heat[2] : 0.1) * layer.options['heatValueScale'];
                // @ts-ignore
                grid[y] = grid[y] || [];
                cell = grid[y][x];
                if (!cell) {
                    grid[y][x] = [p.x, p.y, k];
                }
                else {
                    cell[0] = (cell[0] * cell[2] + p.x * k) / (cell[2] + k); // x
                    cell[1] = (cell[1] * cell[2] + p.y * k) / (cell[2] + k); // y
                    cell[2] += k; // cumulated intensity value
                }
            }
        }
        for (let i = 0, l = grid.length; i < l; i++) {
            if (grid[i]) {
                for (let j = 0, ll = grid[i].length; j < ll; j++) {
                    cell = grid[i][j];
                    if (cell) {
                        data.push([Math.round(cell[0]), Math.round(cell[1]), Math.min(cell[2], max)]);
                    }
                }
            }
        }
        return data;
    }
    onZoomEnd() {
        delete this._heatViews;
        // eslint-disable-next-line prefer-rest-params
        super.onZoomEnd.apply(this, arguments);
    }
    onResize() {
        // eslint-disable-next-line prefer-rest-params
        super.onResize.apply(this, arguments);
        if (this.canvas) {
            this._heater._width = this.canvas.width;
            this._heater._height = this.canvas.height;
        }
    }
    onRemove() {
        this.clearHeatCache();
        delete this._heater;
    }
    clearHeatCache() {
        delete this._heatViews;
    }
});
var HeatLayer$1 = HeatLayer;

export { HeatLayer$1 as default };
