"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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
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 PropTypes = __importStar(require("prop-types"));
const utils_1 = require("../../utils");
const ReactContext_1 = __importDefault(require("../../ReactContext"));
const AutosuggestWidget_1 = require("../widgets/AutosuggestWidget");
const components_1 = require("../components");
function addBold(original, substring) {
    const newOriginal = original.toLowerCase();
    const newString = substring.toLowerCase();
    const index = newOriginal.indexOf(newString);
    return index > -1 ?
        React.createElement(React.Fragment, null,
            original.slice(0, index),
            React.createElement("b", null, original.slice(index, index + substring.length)),
            original.slice(index + substring.length))
        : original;
}
class ScientificNameTaxonAutosuggestField extends React.Component {
    constructor() {
        super(...arguments);
        this.state = {};
        this.getUiSchema = (props) => {
            var _a;
            let { schema, uiSchema, formData = {} } = props;
            const uiOptions = (0, utils_1.getUiOptions)(uiSchema);
            const { taxonField, taxonRankField, authorField } = uiOptions;
            const scientificName = (0, utils_1.parseJSONPointer)(formData, taxonField, true);
            const taxonRank = (0, utils_1.parseJSONPointer)(formData, taxonRankField, true);
            const author = (0, utils_1.parseJSONPointer)(formData, authorField, true);
            let taxonRankLabel = undefined;
            if (taxonRank) {
                taxonRankLabel = (_a = (this.getTaxonRankOptions() || []).find(option => option.const === taxonRank)) === null || _a === void 0 ? void 0 : _a.title;
            }
            let options = Object.assign(Object.assign({}, uiOptions), { autosuggestField: "taxon", onSuggestionSelected: this.onSuggestionSelected, onUnsuggestedSelected: this.onUnsuggestionSelected, isValueSuggested: this.isValueSuggested, getSuggestionFromValue: this.getSuggestionFromValue, getSuggestionValue: this.getSuggestionValue, findExactMatch: this.findExactMatch, renderSuggestion: this.renderSuggestion, valueContext: { taxonRank, author }, taxonRankLabel, Wrapper: TaxonWrapper });
            if (!(0, utils_1.isEmptyString)(scientificName)) {
                options.value = scientificName;
            }
            const innerUiSchema = (0, utils_1.getInnerUiSchema)(uiSchema);
            const _uiSchemaJSONPointer = (0, utils_1.uiSchemaJSONPointer)(schema, taxonField);
            if (!_uiSchemaJSONPointer) {
                throw new Error("Invalid taxon field");
            }
            const taxonExistingUiSchema = (0, utils_1.parseJSONPointer)(innerUiSchema, _uiSchemaJSONPointer);
            let widgetProps = taxonExistingUiSchema || {};
            return (0, utils_1.updateSafelyWithJSONPointer)(innerUiSchema, Object.assign(Object.assign({ "ui:widget": "AutosuggestWidget" }, widgetProps), { "ui:options": Object.assign(Object.assign({}, (0, utils_1.getUiOptions)((taxonExistingUiSchema || {})[taxonField])), options) }), _uiSchemaJSONPointer);
        };
        this.getSuggestionValue = (suggestion) => {
            return suggestion.scientificName;
        };
        this.findExactMatch = (suggestions, inputValue = "") => {
            return suggestions.find(suggestion => { var _a; return (((_a = suggestion === null || suggestion === void 0 ? void 0 : suggestion.scientificName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === inputValue.trim().toLowerCase()); });
        };
        this.onSuggestionSelected = (suggestion, mounted) => {
            if (suggestion === null)
                suggestion = undefined;
            let { formData, uiSchema, formContext, registry, schema } = this.props;
            const { taxonField, taxonRankField, authorField } = (0, utils_1.getUiOptions)(uiSchema);
            if (typeof suggestion === "object") {
                this.setState({ suggestion });
                const scientificName = suggestion.scientificName;
                formData = (0, utils_1.updateFormDataWithJSONPointer)({ formData, registry, schema }, scientificName, taxonField);
                let taxonRank = suggestion.taxonRank;
                const taxonRankOptions = this.getTaxonRankOptions();
                if (taxonRankOptions && !taxonRankOptions.some(option => option.const === taxonRank)) {
                    taxonRank = undefined;
                }
                formData = (0, utils_1.updateFormDataWithJSONPointer)({ formData, registry, schema }, taxonRank, taxonRankField);
                const author = suggestion.scientificNameAuthorship;
                formData = (0, utils_1.updateFormDataWithJSONPointer)({ formData, registry, schema }, author, authorField);
            }
            if (mounted) {
                this.props.onChange(formData);
            }
            else {
                if (formContext.formDataTransformers) {
                    formData = formContext.formDataTransformers.reduce((unit, { "ui:field": uiField, props: fieldProps }) => {
                        let changed;
                        const getChanged = (_changed) => {
                            changed = _changed;
                        };
                        const field = new fieldProps.registry.fields[uiField](Object.assign(Object.assign({}, fieldProps), { formData: formData, onChange: getChanged }));
                        field.onChange(field.state && field.state.formData ? field.state.formData : formData);
                        return changed;
                    }, formData);
                }
                const lajiFormInstance = formContext.services.rootInstance;
                const pointer = this.props.formContext.services.ids.getJSONPointerFromLajiFormIdAndFormDataAndIdSchemaId(this.props.idSchema.$id, (0, utils_1.getFieldUUID)(this.props));
                const newFormData = Object.assign(Object.assign({}, (0, utils_1.parseJSONPointer)(lajiFormInstance.getFormData(), pointer)), formData);
                lajiFormInstance.onChange((0, utils_1.updateSafelyWithJSONPointer)(lajiFormInstance.getFormData(), newFormData, pointer));
            }
        };
        this.onUnsuggestionSelected = (value) => {
            this.setState({ suggestion: undefined });
            let { formData, uiSchema, registry, schema } = this.props;
            const { taxonField } = (0, utils_1.getUiOptions)(uiSchema);
            formData = (0, utils_1.updateFormDataWithJSONPointer)({ formData, registry, schema }, value, taxonField);
            this.props.onChange(formData);
        };
        this.isValueSuggested = (value, valueContext) => {
            const { suggestion } = this.state;
            if (!suggestion) {
                return false;
            }
            return this.suggestionMatchesData(suggestion, value, valueContext);
        };
        this.getSuggestionFromValue = (value, valueContext) => __awaiter(this, void 0, void 0, function* () {
            if (this.isValueSuggested(value, valueContext)) {
                return this.state.suggestion;
            }
            if (!value) {
                throw new Error("Missing scientific name");
            }
            if (!(valueContext === null || valueContext === void 0 ? void 0 : valueContext.taxonRank)) {
                throw new Error("Missing taxon rank");
            }
            const query = { query: value, limit: 100, nameTypes: "MX.scientificName", matchType: "exact" };
            const result = yield this.props.formContext.apiClient.get("/autocomplete/taxa", { query });
            for (const suggestion of result.results) {
                if (this.suggestionMatchesData(suggestion, value, valueContext)) {
                    this.setState({ suggestion });
                    return suggestion;
                }
            }
            throw new Error("Unknown scientific name");
        });
        this.suggestionMatchesData = (suggestion, value, valueContext) => {
            var _a, _b, _c;
            const normalizedValue = value === null || value === void 0 ? void 0 : value.replace(/\s+/g, " ").trim().toLowerCase();
            const normalizedAuthor = (_a = valueContext === null || valueContext === void 0 ? void 0 : valueContext.author) === null || _a === void 0 ? void 0 : _a.replace(/\s/g, "").trim().toLowerCase();
            return normalizedValue === ((_b = suggestion.scientificName) === null || _b === void 0 ? void 0 : _b.toLowerCase()) &&
                (valueContext === null || valueContext === void 0 ? void 0 : valueContext.taxonRank) === suggestion.taxonRank &&
                normalizedAuthor === ((_c = suggestion.scientificNameAuthorship) === null || _c === void 0 ? void 0 : _c.replace(/\s/g, "").toLowerCase());
        };
        this.getTaxonRankOptions = () => {
            let { uiSchema } = this.props;
            const { taxonRankField } = (0, utils_1.getUiOptions)(uiSchema);
            const taxonRankSchemaPointer = (0, utils_1.schemaJSONPointer)(this.props.schema, taxonRankField);
            if (taxonRankSchemaPointer === undefined) {
                throw new Error("Invalid taxon rank field");
            }
            return (0, utils_1.parseJSONPointer)(this.props.schema, taxonRankSchemaPointer).oneOf;
        };
    }
    renderSuggestion(suggestion, inputValue) {
        let value = suggestion.scientificName;
        if (suggestion.nameType === "MX.scientificName" && inputValue) {
            value = addBold(value, inputValue);
        }
        return (React.createElement("span", { className: "simple-option" },
            React.createElement(React.Fragment, null,
                value,
                (0, AutosuggestWidget_1.renderTaxonIcons)(suggestion))));
    }
    render() {
        const { SchemaField } = this.props.registry.fields;
        return React.createElement(SchemaField, Object.assign({}, this.props, { uiSchema: this.getUiSchema(this.props) }));
    }
}
ScientificNameTaxonAutosuggestField.contextType = ReactContext_1.default;
ScientificNameTaxonAutosuggestField.propTypes = {
    uiSchema: PropTypes.shape({
        "ui:options": PropTypes.shape({
            taxonField: PropTypes.string.isRequired,
            taxonRankField: PropTypes.string.isRequired,
            authorField: PropTypes.string.isRequired,
            allowNonsuggestedValue: PropTypes.bool
        }).isRequired,
        uiSchema: PropTypes.object
    }).isRequired,
    schema: PropTypes.shape({
        type: PropTypes.oneOf(["object"])
    }).isRequired,
    formData: PropTypes.object.isRequired
};
exports.default = ScientificNameTaxonAutosuggestField;
class _TaxonWrapper extends React.Component {
    render() {
        const { id, formContext, suggestion, children, placement, inputValue, isSuggested, options } = this.props;
        const { Popover, Tooltip } = this.context.theme;
        const tooltipElem = (React.createElement(Tooltip, { id: `${id}-popover-tooltip` }, formContext.translations.OpenSpeciedCard));
        const taxonLinkElem = (React.createElement(components_1.OverlayTrigger, { overlay: tooltipElem, style: { display: "inline" } },
            React.createElement("a", { href: `http://tun.fi/${suggestion === null || suggestion === void 0 ? void 0 : suggestion.id}`, target: "_blank", rel: "noopener noreferrer" }, suggestion === null || suggestion === void 0 ? void 0 : suggestion.id)));
        let popover;
        if ((0, utils_1.isEmptyString)(inputValue)) {
            popover = React.createElement(React.Fragment, null);
        }
        else {
            popover = (React.createElement(Popover, { id: `${id}-popover` },
                !isSuggested && this.props.formContext.translations.UnknownName,
                isSuggested && (React.createElement("div", null, options === null || options === void 0 ? void 0 :
                    options.taxonRankLabel,
                    " ",
                    suggestion.cursiveName ? React.createElement("i", null, suggestion.scientificName) : suggestion.scientificName,
                    " ",
                    suggestion.scientificNameAuthorship,
                    " ",
                    (0, AutosuggestWidget_1.renderTaxonIcons)(suggestion),
                    " (",
                    taxonLinkElem,
                    ")"))));
        }
        return (React.createElement(components_1.OverlayTrigger, { hoverable: true, placement: placement, overlay: popover, ref: this.props.overlayRef, formContext: this.props.formContext }, children));
    }
}
_TaxonWrapper.contextType = ReactContext_1.default;
const TaxonWrapper = React.forwardRef((props, ref) => React.createElement(_TaxonWrapper, Object.assign({}, props, { overlayRef: ref })));
