"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
// (C) 2007-2020 GoodData Corporation
var React = require("react");
var classnames_1 = require("classnames");
var get = require("lodash/get");
var isEqual = require("lodash/isEqual");
var noop = require("lodash/noop");
var mapboxgl = require("mapbox-gl");
var geoChartDataLayers_1 = require("./geoChartDataLayers");
var geoChartDataSource_1 = require("./geoChartDataSource");
var geoChart_1 = require("../../../constants/geoChart");
require("../../../../styles/scss/geoChart.scss");
var geoChartTooltip_1 = require("./geoChartTooltip");
var viewport_1 = require("../../../helpers/geoChart/viewport");
var common_1 = require("../../../helpers/geoChart/common");
var drilldownEventing_1 = require("../../visualizations/utils/drilldownEventing");
var GeoChartRenderer = /** @class */ (function (_super) {
    __extends(GeoChartRenderer, _super);
    function GeoChartRenderer(props) {
        var _this = _super.call(this, props) || this;
        _this.setChartRef = function (ref) {
            _this.chartRef = ref;
        };
        _this.createMap = function () {
            var _a = _this.props, config = _a.config, data = _a.geoData.location.data;
            var _b = (config || {}).isExportMode, isExportMode = _b === void 0 ? false : _b;
            var isViewportFrozen = _this.isViewportFrozen();
            _this.chart = new mapboxgl.Map(__assign({}, geoChart_1.DEFAULT_MAPBOX_OPTIONS, viewport_1.getViewportOptions(data, config), { container: _this.chartRef, 
                // If true, the map’s canvas can be exported to a PNG using map.getCanvas().toDataURL().
                // This is false by default as a performance optimization.
                interactive: !isViewportFrozen, preserveDrawingBuffer: isExportMode }));
        };
        _this.updateMapWithConfig = function (prevConfig, prevColorStrategy) {
            if (_this.shouldResetMap(prevConfig, prevColorStrategy)) {
                // Config for clustering and pushpin size lead to change layer setting
                // Then calling resetMap here is needed
                _this.resetMap();
            }
            _this.updatePanAndZoom();
            _this.updateViewport(prevConfig);
        };
        _this.resetMap = function () {
            _this.cleanupMap();
            _this.setupMap();
        };
        _this.shouldResetMap = function (prevConfig, prevColorStrategy) {
            var _a = _this.props, colorStrategy = _a.colorStrategy, config = _a.config;
            if (common_1.isPointsConfigChanged(prevConfig.points, config.points)) {
                return true;
            }
            if (common_1.isColorAssignmentItemChanged(prevColorStrategy.getColorAssignment(), colorStrategy.getColorAssignment())) {
                return true;
            }
            return false;
        };
        _this.isViewportFrozen = function () {
            var config = _this.props.config;
            return get(config, "viewport.frozen", false);
        };
        _this.removeMapControls = function () {
            if (_this.navigationControlButton) {
                _this.chart.removeControl(_this.navigationControlButton);
                _this.navigationControlButton = null;
            }
        };
        _this.addMapControls = function () {
            if (!_this.navigationControlButton) {
                _this.navigationControlButton = new mapboxgl.NavigationControl({
                    showCompass: false,
                });
                _this.chart.addControl(_this.navigationControlButton, "bottom-right");
            }
        };
        _this.toggleMapControls = function () {
            var isViewportFrozen = _this.isViewportFrozen();
            if (!isViewportFrozen) {
                _this.addMapControls();
            }
            else {
                _this.removeMapControls();
            }
        };
        _this.toggleInteractionEvents = function () {
            var isViewportFrozen = _this.isViewportFrozen();
            if (!_this.chart) {
                return;
            }
            var action = isViewportFrozen ? "disable" : "enable";
            geoChart_1.INTERACTION_EVENTS.forEach(function (interactionEvent) { return _this.chart[interactionEvent][action](); });
        };
        _this.updatePanAndZoom = function () {
            _this.toggleMapControls();
            _this.toggleInteractionEvents();
        };
        _this.updateViewport = function (prevConfig) {
            var _a = _this.props, config = _a.config, data = _a.geoData.location.data;
            var prevViewport = prevConfig.viewport;
            var viewport = config.viewport;
            if (isEqual(prevViewport, viewport)) {
                return;
            }
            var bounds = viewport_1.getViewportOptions(data, config).bounds;
            if (bounds) {
                _this.chart.fitBounds(bounds, geoChart_1.DEFAULT_MAPBOX_OPTIONS.fitBoundsOptions);
            }
        };
        _this.setFilterMap = function () {
            var selectedSegmentItems = _this.props.config.selectedSegmentItems;
            if (_this.chart.getLayer(geoChart_1.DEFAULT_LAYER_NAME)) {
                _this.chart.setFilter(geoChart_1.DEFAULT_LAYER_NAME, geoChartDataLayers_1.createPushpinFilter(selectedSegmentItems));
            }
        };
        _this.handleMapEvent = function () {
            var chart = _this.chart;
            chart.on("click", geoChart_1.DEFAULT_LAYER_NAME, _this.handleMapClick);
            chart.on("idle", _this.handleMapIdle);
            chart.on("load", _this.setupMap);
            chart.on("load", _this.adjustChartHeight);
            chart.on("mouseenter", geoChart_1.DEFAULT_LAYER_NAME, _this.handlePushpinMouseEnter);
            chart.on("mouseleave", geoChart_1.DEFAULT_LAYER_NAME, _this.handlePushpinMouseLeave);
            chart.on("moveend", _this.handlePushpinMoveEnd);
            chart.on("zoomend", _this.handlePushpinZoomEnd);
        };
        /*
        Fired after the last frame rendered before the map enters an "idle" state:
            - No camera transitions are in progress
            - All currently requested tiles have loaded
            - All fade/transition animations have completed
        This is called one time only
        */
        _this.handleMapIdle = function () {
            var _a = _this, chart = _a.chart, afterRender = _a.props.afterRender;
            chart.off("idle", _this.handleMapIdle);
            afterRender();
        };
        _this.setupMap = function () {
            var _a = _this, chart = _a.chart, handleLayerLoaded = _a.handleLayerLoaded, props = _a.props;
            var colorStrategy = props.colorStrategy, config = props.config, geoData = props.geoData;
            var _b = (config || {}).points, _c = (_b === void 0 ? {} : _b).groupNearbyPoints, groupNearbyPoints = _c === void 0 ? true : _c;
            var hasClustering = common_1.isClusteringAllowed(geoData, groupNearbyPoints);
            var dataSourceProps = {
                colorStrategy: colorStrategy,
                config: config,
                geoData: geoData,
                hasClustering: hasClustering,
            };
            chart.addSource(geoChart_1.DEFAULT_DATA_SOURCE_NAME, geoChartDataSource_1.createPushpinDataSource(dataSourceProps));
            if (!hasClustering) {
                chart.addLayer(geoChartDataLayers_1.createPushpinDataLayer(geoChart_1.DEFAULT_DATA_SOURCE_NAME, geoData, config), "state-label");
            }
            else {
                chart.addLayer(geoChartDataLayers_1.createClusterPoints(geoChart_1.DEFAULT_DATA_SOURCE_NAME));
                chart.addLayer(geoChartDataLayers_1.createClusterLabels(geoChart_1.DEFAULT_DATA_SOURCE_NAME));
                // un-clustered points will be rendered under state/county label
                chart.addLayer(geoChartDataLayers_1.createUnclusterPoints(geoChart_1.DEFAULT_DATA_SOURCE_NAME), "state-label");
            }
            // keep listening to the data event until the style is loaded
            chart.on("data", handleLayerLoaded);
        };
        _this.adjustChartHeight = function () {
            var _a = _this, chart = _a.chart, chartRef = _a.chartRef;
            if (!chartRef) {
                return;
            }
            var chartHeight = chartRef.clientHeight;
            var parentHeight = chartRef.parentElement.clientHeight;
            var shouldResize = chartHeight <= geoChart_1.ZOOM_CONTROLS_HEIGHT && geoChart_1.ZOOM_CONTROLS_HEIGHT <= parentHeight;
            if (shouldResize) {
                // set min height to re-position mapbox attribution and zoom control, in case there are too many top legend items
                // that take all visible height of widget and make geo chart container's height zero
                chartRef.style.minHeight = parentHeight + "px";
                chart.resize();
            }
        };
        _this.handleLayerLoaded = function () {
            var chart = _this.chart;
            if (!chart.isStyleLoaded()) {
                return;
            }
            chart.off("data", _this.handleLayerLoaded);
        };
        _this.createTooltip = function () {
            _this.tooltip = new mapboxgl.Popup(geoChart_1.DEFAULT_TOOLTIP_OPTIONS);
        };
        _this.cleanupMap = function () {
            _this.removeLayer(geoChart_1.DEFAULT_LAYER_NAME);
            _this.removeLayer(geoChart_1.DEFAULT_CLUSTER_LAYER_NAME);
            _this.removeLayer(geoChart_1.DEFAULT_CLUSTER_LABELS_CONFIG.id);
            if (_this.chart.getSource(geoChart_1.DEFAULT_DATA_SOURCE_NAME)) {
                _this.chart.removeSource(geoChart_1.DEFAULT_DATA_SOURCE_NAME);
            }
        };
        _this.removeMap = function () {
            if (!_this.chart) {
                return;
            }
            // try catch to hide the mapbox's error message
            // TypeError: Cannot read property 'off' of undefined
            // mapbox is trying to call its function after deleted
            // https://github.com/mapbox/mapbox-gl-js/blob/master/src/ui/control/navigation_control.js#L118
            try {
                _this.chart.remove();
            }
            catch (_a) {
                return;
            }
        };
        _this.handlePushpinMoveEnd = function (e) {
            var target = e.target;
            var onCenterPositionChanged = _this.props.onCenterPositionChanged;
            var _a = target.getCenter(), lng = _a.lng, lat = _a.lat;
            var center = { lng: lng, lat: lat };
            onCenterPositionChanged(center);
        };
        _this.handlePushpinZoomEnd = function (e) {
            var target = e.target;
            var onZoomChanged = _this.props.onZoomChanged;
            var zoom = target.getZoom();
            onZoomChanged(zoom);
        };
        _this.handleMapClick = function (e) {
            var _a = _this.props, viewport = _a.config.viewport, drillableItems = _a.drillableItems, drillConfig = _a.drillConfig, execution = _a.execution, geoData = _a.geoData;
            var features = e.features, originalEvent = e.originalEvent;
            var _b = features[0], coordinates = _b.geometry.coordinates, properties = _b.properties;
            // Disable drilling in edit/export mode
            if (get(viewport, "frozen", false)) {
                return;
            }
            // without this, the drill dialog will be closed
            originalEvent.stopPropagation();
            return drilldownEventing_1.handleGeoPushpinDrillEvent(drillableItems, drillConfig, execution, geoData, properties, coordinates, originalEvent.target);
        };
        _this.handlePushpinMouseEnter = function (e) {
            var _a = _this, chart = _a.chart, props = _a.props, tooltip = _a.tooltip;
            var config = props.config;
            return geoChartTooltip_1.handlePushpinMouseEnter(e, chart, tooltip, config);
        };
        _this.handlePushpinMouseLeave = function (e) {
            var _a = _this, chart = _a.chart, props = _a.props, tooltip = _a.tooltip;
            var config = props.config;
            return geoChartTooltip_1.handlePushpinMouseLeave(e, chart, tooltip, config);
        };
        mapboxgl.accessToken = props.config.mapboxToken;
        return _this;
    }
    GeoChartRenderer.prototype.componentDidUpdate = function (prevProps) {
        var _a = this.props, selectedSegmentItems = _a.config.selectedSegmentItems, colorStrategy = _a.colorStrategy;
        var prevConfig = prevProps.config, prevColorStrategy = prevProps.colorStrategy;
        var _b = (prevConfig || {}).selectedSegmentItems, prevSelectedSegmentItems = _b === void 0 ? [] : _b;
        // reisze map when component is updated
        // for exemple: toggle legend, change position of legend
        this.chart.resize();
        // only update map when style is ready
        // work around for ticket SD-898
        // avoid refresh whole map will be fixed in ticket SD-899
        if (!this.chart.isStyleLoaded()) {
            return;
        }
        var isColorChanged = common_1.isColorAssignmentItemChanged(prevColorStrategy.getColorAssignment(), colorStrategy.getColorAssignment());
        var selectedSegmentItemsChanged = selectedSegmentItems && !isEqual(selectedSegmentItems, prevSelectedSegmentItems);
        if (!isColorChanged && selectedSegmentItemsChanged) {
            return this.setFilterMap();
        }
        this.updateMapWithConfig(prevConfig, prevColorStrategy);
    };
    GeoChartRenderer.prototype.componentDidMount = function () {
        this.createTooltip();
        this.createMap();
        this.createMapControls();
        this.handleMapEvent();
    };
    GeoChartRenderer.prototype.componentWillUnmount = function () {
        this.removeMap();
    };
    GeoChartRenderer.prototype.render = function () {
        var _a = this.props.config.isExportMode, isExportMode = _a === void 0 ? false : _a;
        var classNames = classnames_1.default("s-gd-geo-chart-renderer", "mapbox-container", {
            isExportMode: isExportMode,
            "s-isExportMode": isExportMode,
        });
        return React.createElement("div", { className: classNames, ref: this.setChartRef });
    };
    GeoChartRenderer.prototype.createMapControls = function () {
        var isViewportFrozen = this.isViewportFrozen();
        this.chart.addControl(new mapboxgl.AttributionControl({
            compact: true,
        }));
        if (!isViewportFrozen) {
            this.addMapControls();
        }
    };
    GeoChartRenderer.prototype.removeLayer = function (layerName) {
        if (this.chart.getLayer(layerName)) {
            this.chart.removeLayer(layerName);
        }
    };
    GeoChartRenderer.defaultProps = {
        config: {
            mapboxToken: "",
        },
        afterRender: noop,
        onZoomChanged: noop,
        onCenterPositionChanged: noop,
    };
    return GeoChartRenderer;
}(React.Component));
exports.default = GeoChartRenderer;
//# sourceMappingURL=GeoChartRenderer.js.map