"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const React = __importStar(require("react"));
const react_dom_1 = require("react-dom");
const PropTypes = __importStar(require("prop-types"));
const components_1 = require("../components");
const Context_1 = __importDefault(require("../../Context"));
const utils_1 = require("../../utils");
const MapArrayField_1 = require("./MapArrayField");
const ReactContext_1 = __importDefault(require("../../ReactContext"));
class LocationChooserField extends React.Component {
    getUiSchema(props) {
        let uiSchema = (0, utils_1.getInnerUiSchema)(props.uiSchema);
        return Object.assign(Object.assign({}, uiSchema), { "ui:buttons": [
                ...(uiSchema["ui:buttons"] || []),
                React.createElement(LocationButton, { key: `$${this.props.idSchema.$id}-location`, that: this })
            ] });
    }
    render() {
        const SchemaField = this.props.registry.fields.SchemaField;
        return React.createElement(SchemaField, Object.assign({}, this.props, { uiSchema: this.getUiSchema(this.props) }));
    }
}
LocationChooserField.propTypes = {
    uiSchema: PropTypes.shape({
        "ui:options": PropTypes.shape({
            uiSchema: PropTypes.object,
            taxonField: PropTypes.string,
            geometryField: PropTypes.string,
            strategy: PropTypes.oneOf(["unit", "lolife", "lolifeUnit"]),
            mapOptions: PropTypes.object,
            maxShapes: PropTypes.number,
            preselectMarker: PropTypes.bool,
            color: PropTypes.string
        })
    }).isRequired,
    schema: PropTypes.shape({
        type: PropTypes.oneOf(["object"])
    }).isRequired,
    formData: PropTypes.object.isRequired
};
exports.default = LocationChooserField;
class LocationButton extends React.Component {
    constructor(props) {
        super(props);
        this.getIdx = () => {
            const { that } = this.props;
            const { $id } = that.props.idSchema;
            const splitted = $id.split("_");
            return parseInt(splitted[splitted.length - 1]);
        };
        this.getGeometryField = () => {
            const { that } = this.props;
            const { geometryField = "geometry" } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            return geometryField;
        };
        this.hasCoordinates = () => {
            return (0, MapArrayField_1.parseGeometries)(this.getGeometry()).length;
        };
        this.getGeometry = () => {
            const geometryField = this.getGeometryField();
            return (0, utils_1.parseJSONPointer)(this.props.that.props.formData, geometryField);
        };
        this.onMouseEnter = () => {
            const { that } = this.props;
            const idx = this.getIdx();
            this._hovered = true;
            if (typeof idx === "number") {
                that.props.formContext.services.customEvents.send(that.props.idSchema.$id, "startHighlight", { id: (0, utils_1.getUUID)(that.props.formData), idx });
            }
        };
        this.onMouseLeave = () => {
            const { that } = this.props;
            const idx = this.getIdx();
            this._hovered = false;
            if (typeof idx === "number") {
                that.props.formContext.services.customEvents.send(that.props.idSchema.$id, "endHighlight", { id: (0, utils_1.getUUID)(that.props.formData), idx });
            }
        };
        this.getUnitDrawFeatureStyle = () => ({ color: "#55AEFA" });
        this.getDataFeatureStyle = () => ({ color: "#aaaaaa", opacity: 0.7 });
        this.getData = () => {
            const { that } = this.props;
            const { strategy = "unit" } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            switch (strategy) {
                case "unit":
                    return this.getUnitData();
                case "lolifeUnit":
                case "lolife":
                    return this.getLolifeData();
            }
        };
        this.getUnitData = () => {
            const { that } = this.props;
            const mapContext = (0, Context_1.default)(`${that.props.formContext.contextId}_MAP`);
            const { map } = mapContext;
            const emptyFeatureCollection = { featureCollection: { type: "FeatureCollection", features: [] } };
            const gatheringData = map ? map.getDraw() : emptyFeatureCollection;
            const [unitGeometriesData, ...unitGeometryCollectionsData] = map
                ? map.data.filter(i => i) || [emptyFeatureCollection]
                : [emptyFeatureCollection];
            const idx = this.getIdx();
            const geometry = this.getGeometry();
            const draw = geometry && geometry.type
                ? {
                    featureCollection: { type: "FeatureCollection", features: [{ type: "Feature", geometry }] },
                    getFeatureStyle: this.getUnitDrawFeatureStyle,
                }
                : undefined;
            if (unitGeometriesData && draw)
                draw.getFeatureStyle = unitGeometriesData.getFeatureStyle;
            return [
                draw,
                [
                    {
                        featureCollection: gatheringData.featureCollection,
                        getFeatureStyle: this.getDataFeatureStyle
                    },
                    {
                        featureCollection: {
                            type: "FeatureCollection",
                            features: unitGeometriesData.featureCollection.features.filter(feature => feature.properties.idx !== idx)
                        },
                        getFeatureStyle: this.getDataFeatureStyle
                    },
                    ...unitGeometryCollectionsData.map(data => ({
                        featureCollection: {
                            type: "FeatureCollection",
                            features: data.featureCollection.features.filter(feature => feature.properties.idx !== idx)
                        },
                        getFeatureStyle: this.getDataFeatureStyle
                    }))
                ]
            ];
        };
        this.getLolifeData = () => {
            const { that } = this.props;
            const mapContext = (0, Context_1.default)(`${that.props.formContext.contextId}_MAP`);
            const { map } = mapContext;
            const { strategy = "unit" } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            let draw;
            if (strategy === "lolife") {
                draw = map.getDraw();
            }
            const id = (0, utils_1.getUUID)(that.props.formData);
            let data = map.data.filter((item) => {
                const isCurrent = item.featureCollection.features[0].properties.id === id;
                if (strategy === "lolifeUnit" && isCurrent) {
                    draw = Object.assign(Object.assign({}, item), { idx: map.drawIdx, getPopup: undefined, on: undefined, getFeatureStyle: this.getFeatureStyleWithHighlight(item.getFeatureStyle), getDraftStyle: () => this.getFeatureStyleWithHighlight(item.getFeatureStyle)({ feature: { properties: { id } } }, !!"higlight") });
                }
                return !isCurrent;
            }).map(item => (Object.assign(Object.assign({}, item), { getPopup: undefined, on: undefined, getFeatureStyle: this.getFeatureStyleWithLowerOpacity(item.getFeatureStyle) })));
            if (strategy === "lolifeUnit") {
                data.push(map.getDraw());
            }
            return [
                draw,
                data
            ];
        };
        this.onClick = () => {
            const { that } = this.props;
            const { disabled, readonly } = that.props;
            const mapContext = (0, Context_1.default)(`${that.props.formContext.contextId}_MAP`);
            const { map } = mapContext;
            this.triggerLayer = undefined;
            let [draw, data] = this.getData();
            data = data.map(d => (Object.assign(Object.assign({}, d), { editable: false })));
            const _a = map ? map.getOptions() : { mapOptions: {} }, { rootElem, customControls, zoom, center } = _a, mapOptions = __rest(_a, ["rootElem", "customControls", "zoom", "center"]);
            const { mapOptions: { marker = true, polyline = false, rectangle = false, polygon = false, circle = false } = {} } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            const modalMap = Object.assign(Object.assign({}, mapOptions), { data, draw: Object.assign(Object.assign({}, draw), { marker,
                    polyline,
                    rectangle,
                    polygon,
                    circle, onChange: this.onChange, editable: !readonly && !disabled }), controls: Object.assign(Object.assign({}, mapOptions.controls), { draw: Object.assign(Object.assign({}, ((mapOptions.controls || {}).draw || {})), { clear: true, delete: true }) }), fullscreenable: true, zoomToData: draw ? { draw: true } : true, clickBeforeZoomAndPan: false, onComponentDidMount: this.onMapMounted });
            if (center) {
                modalMap.center = center;
            }
            if (zoom) {
                modalMap.zoom = zoom;
            }
            this.setState({
                modalMap
            });
        };
        this.getGrey = () => ({ opacity: 0.6, color: "#888888" });
        this.getFeatureStyle = () => ({ color: "#75CEFA" });
        this.onChange = (events) => {
            const { that } = this.props;
            const { maxShapes = 1 } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            const _that = this;
            function close() {
                _that.triggerLayer && _that.triggerLayer.disable();
                _that.onHide();
            }
            const geometryField = this.getGeometryField();
            for (let event of events) {
                const { type } = event;
                const geometryRef = (0, utils_1.parseJSONPointer)(that.props.formData, geometryField);
                switch (type) {
                    case "create": {
                        if (geometryRef && geometryRef.type && (maxShapes > 1 || maxShapes === -1)) {
                            if (geometryRef.geometries && maxShapes !== -1 && geometryRef.geometries.length >= maxShapes) {
                                this.setState({ shapeAlert: { label: "tooManyShapes", max: maxShapes } });
                                return;
                            }
                            const geometries = geometryRef && geometryRef.geometries
                                ? geometryRef.geometries
                                : geometryRef && geometryRef.type
                                    ? [geometryRef]
                                    : [];
                            that.props.onChange((0, utils_1.updateFormDataWithJSONPointer)(that.props, { type: "GeometryCollection", geometries: [...geometries, event.feature.geometry] }, geometryField));
                        }
                        else {
                            that.props.onChange((0, utils_1.updateFormDataWithJSONPointer)(that.props, event.feature.geometry, geometryField));
                        }
                        // TODO LajiMap doesn't send a sequence of events containing multiple events if 
                        // it sends a create event, but this isn't necessarily true in the future and
                        // closing here wouldn't be right.
                        close();
                        break;
                    }
                    case "delete": {
                        if (!geometryRef || !geometryRef.type) {
                            break;
                        }
                        that.props.onChange((0, utils_1.updateFormDataWithJSONPointer)(that.props, undefined, geometryField));
                        break;
                    }
                    case "edit": {
                        if (!geometryRef || !geometryRef.type) {
                            break;
                        }
                        if (geometryRef.type === "GeometryCollection" && geometryRef.geometries) {
                            const geometry = Object.keys(event.features).reduce((geometry, idx) => {
                                const feature = event.features[idx];
                                geometry = Object.assign(Object.assign({}, geometry), { geometries: Object.assign(Object.assign({}, geometry.geometries), { [idx]: feature.geometry }) });
                                return geometry;
                            }, (0, utils_1.parseJSONPointer)(that.props.formData, geometryField));
                            that.props.onChange((0, utils_1.updateFormDataWithJSONPointer)(that.props, geometry, geometryField));
                        }
                        else if (geometryRef.type) {
                            that.props.onChange((0, utils_1.updateFormDataWithJSONPointer)(that.props, event.features[0].geometry, geometryField));
                        }
                    }
                }
                if (geometryRef
                    && geometryRef.geometries
                    && geometryRef.geometries.length <= maxShapes
                    && type !== "edit") {
                    this.setState({ shapeAlert: undefined });
                }
            }
        };
        this.onMapMounted = (map) => {
            const { that } = this.props;
            const { disabled, readonly } = that.props;
            const { preselectMarker = true } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            map.resetDrawUndoStack();
            if (!disabled && !readonly && preselectMarker) {
                this.triggerLayer = map.triggerDrawing("marker");
            }
        };
        this.getMiniMapData = () => {
            const { that } = this.props;
            const { strategy = "unit" } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            switch (strategy) {
                case "unit":
                    return this.getUnitMiniMapData();
                case "lolifeUnit":
                    return this.getLolifeUnitMiniMapData();
                case "lolife":
                    return this.getLolifeMiniMapData();
            }
        };
        this.getUnitMiniMapData = () => {
            const { that } = this.props;
            const mapContext = (0, Context_1.default)(`${that.props.formContext.contextId}_MAP`);
            const { map } = mapContext;
            const geometry = this.getGeometry();
            const data = [
                ...(map && map.getDraw() ? [Object.assign(Object.assign({}, map.getDraw()), { getFeatureStyle: this.getGrey })] : []),
                ...(map && map.data && map.data[0] ? [Object.assign(Object.assign({}, map.data[0]), { getFeatureStyle: this.getGrey })] : []),
                {
                    geoData: geometry,
                    getFeatureStyle: this.getFeatureStyle
                }
            ];
            return [
                data,
                { dataIdxs: [data.length - 1] }
            ];
        };
        this.getLolifeMiniMapData = () => {
            const { that } = this.props;
            const mapContext = (0, Context_1.default)(`${that.props.formContext.contextId}_MAP`);
            const { map } = mapContext;
            const draw = map.getDraw();
            return [
                [
                    draw,
                    ...map.data.map((item) => {
                        return Object.assign(Object.assign({}, item), { getPopup: undefined, on: undefined, editable: false, getFeatureStyle: item.getFeatureStyle });
                    })
                ],
                { dataIdxs: [draw.idx || 0] }
            ];
        };
        this.getLolifeUnitMiniMapData = () => {
            const { that } = this.props;
            const mapContext = (0, Context_1.default)(`${that.props.formContext.contextId}_MAP`);
            const { map } = mapContext;
            const id = (0, utils_1.getUUID)(that.props.formData);
            return [
                [
                    ...map.data.map((item) => (Object.assign(Object.assign({}, item), { getPopup: undefined, on: undefined, editable: false, getFeatureStyle: item.getFeatureStyle
                            ? item.featureCollection.features[0].properties.id === id
                                ? this.getFeatureStyleWithHighlight(item.getFeatureStyle)
                                : this.getFeatureStyleWithLowerOpacity(item.getFeatureStyle)
                            : undefined }))),
                    map.getDraw()
                ],
                { dataIdxs: [map.data.findIndex(d => d.featureCollection.features[0].properties.id === id)] }
            ];
        };
        this.getFeatureStyleWithLowerOpacity = getFeatureStyle => (...params) => {
            const style = getFeatureStyle(...params);
            return (0, MapArrayField_1.getFeatureStyleWithLowerOpacity)(style);
        };
        this.getFeatureStyleWithHighlight = getFeatureStyle => (...params) => {
            const style = getFeatureStyle(...params);
            return (0, MapArrayField_1.getFeatureStyleWithHighlight)(style);
        };
        this.onEntered = () => {
            const { that } = this.props;
            const mapContext = (0, Context_1.default)(`${that.props.formContext.contextId}_MAP`);
            if (!mapContext) {
                return;
            }
            const { map } = mapContext;
            let mapOptions = {};
            if (map) {
                const _a = map.getOptions(), { rootElem, zoom, center } = _a, _mapOptions = __rest(_a, ["rootElem", "zoom", "center"]);
                mapOptions = _mapOptions;
            }
            const [data, zoomToData] = this.getMiniMapData();
            this.setState({
                miniMap: Object.assign(Object.assign({}, mapOptions), { draw: false, controls: false, customControls: undefined, zoomToData,
                    data, clickBeforeZoomAndPan: false })
            });
        };
        this.onHide = () => {
            this.setState({ modalMap: undefined });
        };
        this.onModalMapKeyDown = (e) => {
            if (e.key === "Escape" && !this.modalMapRef.map.keyHandlerForType("keydown", e.nativeEvent)) {
                this.onHide();
            }
        };
        this.setMapRef = (elem) => {
            this.modalMapRef = elem;
        };
        this.setMiniMapRef = (elem) => {
            this.miniMapRef = elem;
        };
        this.setButtonRef = (elem) => {
            this.buttonRef = elem;
        };
        this.getButtonElem = () => (0, react_dom_1.findDOMNode)(this.buttonRef);
        this.renderButton = () => {
            const { that, glyph = "map-marker", label } = this.props;
            const id = that.props.idSchema.$id;
            const { color } = (0, utils_1.getUiOptions)(that.props.uiSchema);
            const hasCoordinates = this.hasCoordinates();
            const geometryField = this.getGeometryField();
            const hasErrors = (0, utils_1.parseJSONPointer)((0, utils_1.filteredErrors)(that.props.errorSchema), geometryField);
            const variant = hasErrors
                ? "danger"
                : undefined;
            const button = React.createElement(components_1.GlyphButton, { key: `${that.props.idSchema.$id}-location`, id: `${that.props.idSchema.$id}-location`, variant: variant, active: !!hasCoordinates, onMouseEnter: this.onMouseEnter, onMouseLeave: this.onMouseLeave, glyph: glyph, onClick: this.onClick, style: hasCoordinates && !hasErrors && color ? { backgroundColor: color } : undefined, ref: this.setButtonRef });
            const { Overlay, Popover, Tooltip } = this.context.theme;
            if (hasErrors) {
                return (React.createElement(React.Fragment, null,
                    button,
                    React.createElement(Overlay, { show: true, container: this, target: this.getButtonElem, placement: "left" },
                        React.createElement(Tooltip, { id: `laji-form-error-container-${id}_${(0, utils_1.JSONPointerToId)(geometryField)}`, className: "location-chooser-errors" },
                            React.createElement("ul", null, hasErrors.__errors.map((e, i) => React.createElement("li", { key: i }, (0, utils_1.formatErrorMessage)(e))))))));
            }
            else {
                const { translations } = that.props.formContext;
                const overlay = hasCoordinates ? (React.createElement(Popover, { id: `${id}-location-peeker`, title: `${translations.ChooseLocation} (${translations.below} ${translations.currentLocation})` },
                    React.createElement(MapArrayField_1.Map, Object.assign({}, this.state.miniMap, { hidden: !this.state.miniMap || this.state.modalMap, style: { width: 200, height: 200 }, singleton: true, formContext: that.props.formContext, bodyAsDialogRoot: false, syncZoomToDataOnDataChangeOnSingleton: true, ref: this.setMiniMapRef })))) : (React.createElement(Tooltip, { id: `${id}-location-peeker` }, label || that.props.formContext.translations.ChooseLocation));
                return (React.createElement(components_1.OverlayTrigger, { key: `${id}-set-coordinates-${glyph}`, overlay: overlay, placement: "left", hoverable: true, formContext: that.props.formContext, onEntered: hasCoordinates ? this.onEntered : undefined }, button));
            }
        };
        this.state = {};
    }
    componentWillUnmount() {
        this._hovered && this.onMouseLeave();
    }
    render() {
        const { that } = this.props;
        const { shapeAlert } = this.state;
        const { taxonField, title } = (0, utils_1.getUiOptions)(that.props.uiSchema);
        const { translations } = that.props.formContext;
        const { Modal, Alert } = this.context.theme;
        return (React.createElement(React.Fragment, null,
            this.renderButton(),
            this.state.modalMap &&
                React.createElement(Modal, { key: "map-modal", show: true, dialogClassName: "laji-form map-dialog", onHide: this.onHide, keyboard: false, onKeyDown: this.onModalMapKeyDown },
                    React.createElement(Modal.Header, { closeButton: true },
                        React.createElement(Modal.Title, null, title || `${translations.SetLocationToUnit}${that.props.formData[taxonField] ? ` (${that.props.formData[taxonField]})` : ""}`)),
                    React.createElement(Modal.Body, null,
                        shapeAlert && React.createElement(Alert, { variant: "danger" }, translations[shapeAlert.label] + shapeAlert.max),
                        React.createElement(MapArrayField_1.Map, Object.assign({}, this.state.modalMap, { singleton: true, formContext: that.props.formContext, ref: this.setMapRef, bodyAsDialogRoot: false }))))));
    }
}
LocationButton.contextType = ReactContext_1.default;
