"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MeasureContext = void 0;
var tslib_1 = require("tslib");
var common_1 = require("../api/common");
var i18n_1 = require("../api/i18n");
var number_1 = require("../utils/number");
var olObservable = tslib_1.__importStar(require("ol/Observable"));
var olSphere = tslib_1.__importStar(require("ol/sphere"));
var LineString_1 = tslib_1.__importDefault(require("ol/geom/LineString"));
var Polygon_1 = tslib_1.__importDefault(require("ol/geom/Polygon"));
var logger_1 = require("../utils/logger");
var proj_1 = require("ol/proj");
var LAYER_NAME = "measure-layer";
function isArbitraryProjection(proj) {
    if (proj instanceof proj_1.Projection) {
        return /XY-[A-Z]+/.test(proj.getCode());
    }
    return false;
}
/**
 * @hidden
 */
var MeasureContext = /** @class */ (function () {
    function MeasureContext(viewer, mapName, parent) {
        var _this = this;
        this.onDrawStart = function (evt) {
            // set sketch
            _this.sketch = evt.feature;
            /** @type {ol.Coordinate|undefined} */
            var tooltipCoord = evt.coordinate;
            if (_this.sketch) {
                var g = _this.sketch.getGeometry();
                if (g) {
                    _this.listener = g.on('change', function (e) {
                        var geom = e.target;
                        var output;
                        if (geom instanceof Polygon_1.default) {
                            var _a = _this.formatArea(geom), o = _a[0], total = _a[1], segments = _a[2];
                            output = o;
                            if (_this.callback) {
                                _this.callback.updateSegments("Area", total, segments);
                            }
                            tooltipCoord = geom.getInteriorPoint().getCoordinates();
                        }
                        else if (geom instanceof LineString_1.default) {
                            var _b = _this.formatLength(geom), o = _b[0], total = _b[1], segments = _b[2];
                            output = o;
                            if (_this.callback) {
                                _this.callback.updateSegments("LineString", total, segments);
                            }
                            tooltipCoord = geom.getLastCoordinate();
                        }
                        else {
                            output = "";
                        }
                        if (_this.measureTooltipElement) {
                            _this.measureTooltipElement.innerHTML = output;
                        }
                        _this.measureTooltip.setPosition(tooltipCoord);
                    });
                }
            }
        };
        this.onDrawEnd = function () {
            if (_this.measureTooltipElement) {
                _this.measureTooltipElement.className = 'tooltip tooltip-static';
            }
            _this.measureTooltip.setOffset([0, -7]);
            // unset sketch
            _this.sketch = null;
            // unset tooltip so that a new one can be created
            _this.measureTooltipElement = null;
            _this.createMeasureTooltip();
            olObservable.unByKey(_this.listener);
        };
        this.onMouseMove = function (evt) {
            if (evt.dragging || !_this.parent) {
                return;
            }
            var locale = _this.parent.getLocale();
            /** @type {string} */
            var helpMsg = (0, i18n_1.tr)("MEASUREMENT_START_DRAWING", locale);
            if (_this.sketch) {
                var geom = (_this.sketch.getGeometry());
                if (geom instanceof Polygon_1.default) {
                    helpMsg = (0, i18n_1.tr)("MEASUREMENT_CONTINUE_POLYGON", locale);
                }
                else if (geom instanceof LineString_1.default) {
                    helpMsg = (0, i18n_1.tr)("MEASUREMENT_CONTINUE_LINE", locale);
                }
            }
            _this.helpTooltipElement.innerHTML = helpMsg;
            _this.helpTooltip.setPosition(evt.coordinate);
            _this.helpTooltipElement.classList.remove('hidden');
        };
        this.measureOverlays = [];
        this.viewer = viewer;
        this.mapName = mapName;
        this.parent = parent;
        this.layerName = "".concat(LAYER_NAME, "-").concat(mapName);
        this.olFactory = viewer.getOLFactory();
        this.measureLayer = this.olFactory.createVectorLayer({
            source: this.olFactory.createVectorSource()
        });
        this.measureLayer.set(common_1.LayerProperty.IS_MEASURE, true);
        this.measureLayer.setStyle(this.createMeasureStyle());
    }
    /**
     * Establishes the new parent component for this context. Is called by the
     * new parent component when it mounts and is resuming from previously recorded
     * measurements and is not meant to be called directly
     *
     * @param parent The new parent component
     * @since 0.12
     */
    MeasureContext.prototype.setParent = function (parent) {
        this.parent = parent;
    };
    /**
     * Detaches the parent component from this context. Is called by the parent
     * component when it unmounts and is not meant to be called directly.
     *
     * @since 0.12
     */
    MeasureContext.prototype.detachParent = function () {
        this.parent = undefined;
    };
    /**
     * Format length output.
     * @param {LineString} line The line.
     * @return {string} The formatted length.
     */
    MeasureContext.prototype.formatLength = function (line) {
        var length;
        var segments;
        var locale;
        var isArbitrary = false;
        var sourceProj = this.viewer.getProjection();
        if (this.parent) {
            locale = this.parent.getLocale();
            var coordinates = line.getCoordinates();
            segments = [];
            length = 0;
            if (isArbitraryProjection(sourceProj)) {
                isArbitrary = true;
                // Do euclidean distance
                for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                    var c1 = coordinates[i];
                    var c2 = coordinates[i + 1];
                    var a = c1[0] - c2[0];
                    var b = c1[1] - c2[1];
                    var dist = Math.sqrt(a * a + b * b);
                    length += dist;
                    segments.push({ segment: (i + 1), length: dist });
                }
            }
            else {
                for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                    var c1 = this.olFactory.transformCoordinate(coordinates[i], sourceProj, 'EPSG:4326');
                    var c2 = this.olFactory.transformCoordinate(coordinates[i + 1], sourceProj, 'EPSG:4326');
                    var dist = olSphere.getDistance(c1, c2);
                    length += dist;
                    segments.push({ segment: (i + 1), length: dist });
                }
            }
        }
        else {
            length = NaN;
        }
        var output;
        if (isArbitrary && sourceProj instanceof proj_1.Projection) {
            output = "".concat((0, number_1.roundTo)(length, 2), " ").concat(sourceProj.getUnits());
        }
        else {
            if (length > 100) {
                output = (0, i18n_1.tr)("UNIT_FMT_KM", locale, { value: ((0, number_1.roundTo)(length / 1000, 2)) });
            }
            else {
                output = (0, i18n_1.tr)("UNIT_FMT_M", locale, { value: ((0, number_1.roundTo)(length, 2)) });
            }
        }
        return [output, length, segments];
    };
    /**
     * Format area output.
     * @param {Polygon} polygon The polygon.
     * @return {string} Formatted area.
     */
    MeasureContext.prototype.formatArea = function (polygon) {
        var area;
        var segments;
        var locale;
        var isArbitrary = false;
        var sourceProj = this.viewer.getProjection();
        if (this.parent) {
            locale = this.parent.getLocale();
            segments = [];
            var geom = polygon;
            if (isArbitraryProjection(sourceProj)) {
                isArbitrary = true;
                area = geom.getArea();
                // Do euclidean distance
                var ring = geom.getLinearRing(0);
                if (ring) {
                    var coordinates = ring.getCoordinates();
                    for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                        var c1 = coordinates[i];
                        var c2 = coordinates[i + 1];
                        var a = c1[0] - c2[0];
                        var b = c1[1] - c2[1];
                        var dist = Math.sqrt(a * a + b * b);
                        segments.push({ segment: (i + 1), length: dist });
                    }
                }
            }
            else {
                area = olSphere.getArea(geom, { projection: sourceProj });
                (0, logger_1.debug)("Polygon area: ".concat(area));
                var ring = geom.getLinearRing(0);
                if (ring) {
                    var coordinates = ring.getCoordinates();
                    for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
                        // Unlike getArea(), getDistance() requires that our input coordinates are in
                        // EPSG:4326 first
                        var c1 = this.olFactory.transformCoordinate(coordinates[i], sourceProj, 'EPSG:4326');
                        var c2 = this.olFactory.transformCoordinate(coordinates[i + 1], sourceProj, 'EPSG:4326');
                        var dist = olSphere.getDistance(c1, c2);
                        segments.push({ segment: (i + 1), length: dist });
                    }
                }
            }
        }
        else {
            area = NaN;
        }
        var output;
        if (isArbitrary && sourceProj instanceof proj_1.Projection) {
            output = "".concat((0, number_1.roundTo)(area, 2), " ").concat(sourceProj.getUnits(), "<sup>2</sup>");
        }
        else {
            //TODO: The dynamic switch in display of measurement unit based on size of value may prove to be
            //jarring, so we should provide a fixed unit value option in the future that will be used to 
            //display regardless of the size of value
            if (area > 10000) {
                output = (0, i18n_1.tr)("UNIT_FMT_SQKM", locale, {
                    value: "".concat(((0, number_1.roundTo)(area / 1000000, 2)))
                });
            }
            else {
                output = (0, i18n_1.tr)("UNIT_FMT_SQM", locale, {
                    value: "".concat(((0, number_1.roundTo)(area, 2)))
                });
            }
        }
        return [output, area, segments];
    };
    MeasureContext.prototype.getMapName = function () { return this.mapName; };
    MeasureContext.prototype.createMeasureStyle = function () {
        return this.olFactory.createStyle({
            fill: this.olFactory.createStyleFill({
                color: 'rgba(255, 255, 255, 0.2)'
            }),
            stroke: this.olFactory.createStyleStroke({
                color: '#ffcc33',
                width: 2
            }),
            image: this.olFactory.createStyleCircle({
                radius: 7,
                fill: this.olFactory.createStyleFill({
                    color: '#ffcc33'
                })
            })
        });
    };
    MeasureContext.prototype.createDrawInteraction = function (type) {
        var source = this.measureLayer.getSource();
        return this.olFactory.createInteractionDraw({
            source: source,
            type: type,
            style: this.olFactory.createStyle({
                fill: this.olFactory.createStyleFill({
                    color: 'rgba(255, 255, 255, 0.2)'
                }),
                stroke: this.olFactory.createStyleStroke({
                    color: 'rgba(0, 0, 0, 0.5)',
                    lineDash: [10, 10],
                    width: 2
                }),
                image: this.olFactory.createStyleCircle({
                    radius: 5,
                    stroke: this.olFactory.createStyleStroke({
                        color: 'rgba(0, 0, 0, 0.7)'
                    }),
                    fill: this.olFactory.createStyleFill({
                        color: 'rgba(255, 255, 255, 0.2)'
                    })
                })
            })
        });
    };
    MeasureContext.prototype.removeElement = function (el) {
        if (el && el.parentNode) {
            el.parentNode.removeChild(el);
        }
    };
    /**
     * Creates a new help tooltip
     */
    MeasureContext.prototype.createHelpTooltip = function () {
        this.removeElement(this.helpTooltipElement);
        this.helpTooltipElement = document.createElement('div');
        this.helpTooltipElement.className = 'tooltip hidden';
        this.helpTooltip = this.olFactory.createOverlay({
            element: this.helpTooltipElement,
            offset: [15, 0],
            positioning: "center-left"
        });
        this.viewer.addOverlay(this.helpTooltip);
    };
    /**
     * Creates a new measure tooltip
     */
    MeasureContext.prototype.createMeasureTooltip = function () {
        this.removeElement(this.measureTooltipElement);
        this.measureTooltipElement = document.createElement('div');
        this.measureTooltipElement.className = 'tooltip tooltip-measure';
        //Stash the old overlay
        if (this.measureTooltip) {
            this.measureOverlays.push(this.measureTooltip);
        }
        this.measureTooltip = this.olFactory.createOverlay({
            element: this.measureTooltipElement,
            offset: [0, -15],
            positioning: "bottom-center"
        });
        this.viewer.addOverlay(this.measureTooltip);
    };
    MeasureContext.prototype.setActiveInteraction = function (type) {
        if (this.draw) {
            this.draw.un("drawstart", this.onDrawStart);
            this.draw.un("drawend", this.onDrawEnd);
            this.viewer.removeInteraction(this.draw);
        }
        this.draw = this.createDrawInteraction(type);
        if (this.draw) {
            this.draw.on("drawstart", this.onDrawStart);
            this.draw.on("drawend", this.onDrawEnd);
            this.viewer.addInteraction(this.draw);
        }
    };
    MeasureContext.prototype.startMeasure = function () {
        if (this.parent) {
            var type = this.parent.getCurrentDrawType();
            if (type) {
                this.createMeasureTooltip();
                this.createHelpTooltip();
                this.setActiveInteraction(type);
                this.viewer.addHandler('pointermove', this.onMouseMove);
            }
        }
    };
    MeasureContext.prototype.endMeasure = function () {
        this.viewer.removeHandler('pointermove', this.onMouseMove);
        if (this.draw) {
            this.draw.un("drawstart", this.onDrawStart);
            this.draw.un("drawend", this.onDrawEnd);
            this.viewer.removeInteraction(this.draw);
            //this.draw = null;
        }
        this.removeElement(this.helpTooltipElement);
    };
    MeasureContext.prototype.clearMeasurements = function () {
        var _a;
        (_a = this.measureLayer.getSource()) === null || _a === void 0 ? void 0 : _a.clear();
        for (var _i = 0, _b = this.measureOverlays; _i < _b.length; _i++) {
            var ov = _b[_i];
            this.viewer.removeOverlay(ov);
        }
        this.measureOverlays.length = 0; //Clear
        if (this.callback) {
            this.callback.clearSegments();
        }
    };
    MeasureContext.prototype.handleDrawTypeChange = function () {
        if (this.parent) {
            var type = this.parent.getCurrentDrawType();
            if (type) {
                this.setActiveInteraction(type);
            }
        }
    };
    /**
     * Since 0.12, mapName is required to explicitly state which map you are activating the context for
     *
     * @param {string} mapName
     * @param {IMeasureCallback} callback
     * @memberof MeasureContext
     */
    MeasureContext.prototype.activate = function (mapName, callback) {
        this.callback = callback;
        (0, logger_1.debug)("Activating measure context for ".concat(this.mapName));
        for (var _i = 0, _a = this.measureOverlays; _i < _a.length; _i++) {
            var ov = _a[_i];
            this.viewer.addOverlay(ov);
        }
        this.viewer.getLayerManager(mapName).addLayer(this.layerName, this.measureLayer, true);
    };
    /**
     * Since 0.12, mapName is required to explicitly state which map you are deactivating the context for
     *
     * @param {string} mapName
     * @memberof MeasureContext
     */
    MeasureContext.prototype.deactivate = function (mapName) {
        this.callback = undefined;
        (0, logger_1.debug)("De-activating measure context for ".concat(this.mapName));
        this.endMeasure();
        for (var _i = 0, _a = this.measureOverlays; _i < _a.length; _i++) {
            var ov = _a[_i];
            this.viewer.removeOverlay(ov);
        }
        this.viewer.getLayerManager(mapName).removeLayer(this.layerName);
    };
    return MeasureContext;
}());
exports.MeasureContext = MeasureContext;
//# sourceMappingURL=measure-context.js.map