"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 __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 Context_1 = __importDefault(require("../../Context"));
const deepmerge_1 = __importDefault(require("deepmerge"));
const suggestionParsers = {
    taxonGroup: suggestion => {
        return (suggestion.informalGroups || []).map(item => typeof item === "string" ? item : item.id);
    }
};
const parseQuery = (query, props, taxonGroups) => {
    return Object.keys(query).reduce((_query, key) => {
        if (typeof query[key] === "object") {
            const { parser, field } = query[key];
            const { formData = {} } = props;
            if (parser === "arrayJoin") {
                _query[key] = ((key === "informalTaxonGroup" ? taxonGroups : formData[field]) || []).join(",");
            }
        }
        else {
            _query[key] = query[key];
        }
        return _query;
    }, {});
};
/**
 * Uses AutosuggestWidget to apply autosuggested values to multiple object's fields. Options are passed to AutosuggestWidget.
 *
 * uischema = {"ui:options": {
 *  autosuggestField: <string> (field name which is used for api call. The suggestions renderer method is also defined by autosuggestField)
 *  suggestionInputField: <fieldName> (the field which is rendered as the autosuggest input)
 *  suggestionValueField: <fieldName> (the field which the value for autosuggest is pulled from)
 *  suggestionReceivers: {
 *    <fieldName>: <suggestion path>,     (when an autosuggestion is selected, these fields receive the autosuggestions value defined by suggestion path.
 *    <fieldName2>: <suggestion path 2>,   Example: autosuggestion = {key: "MLV.2", value: "kalalokki", informalGroups: ["linnut"]}
 *   }                                              suggestionReceivers: {someFieldName: "key", someFieldName2: "/informalgroups/0"}
 *                                         If fieldName start  with '$', then a function from autosuggestFieldSettings parses the suggestion. Example: $taxonGroup
 *                                         If fieldName start  with '/', it is handled as a JSON pointer.
 *  uiSchema: <uiSchema> (uiSchema which is passed to inner SchemaField)
 * }
 */
class AutosuggestField extends React.Component {
    static getName() { return "AutosuggestField"; }
    constructor(props) {
        super(props);
        this.UNSAFE_componentWillReceiveProps = (props) => {
            this.setState(this.getStateFromProps(props));
            if (this.onNextTick) {
                this.onNextTick();
                this.onNextTick = undefined;
            }
        };
        this.getTogglePersistenceContextKey = (props) => `AUTOSUGGEST_FIELD_TOGGLE_PERSISTENCE_${props.uiSchema["ui:options"].togglePersistenceKey}`;
        this.getInformalTaxonGroupsPersistenceContextKey = (props) => `AUTOSUGGEST_FIELD_PERSISTENCE_${props.uiSchema["ui:options"].informalTaxonGroupPersistenceKey}`;
        this.getStateFromProps = (props, toggled) => {
            let { schema, uiSchema, formData } = props;
            const context = (0, Context_1.default)(this.props.formContext.contextId);
            const uiOptions = (0, utils_1.getUiOptions)(uiSchema);
            const { informalTaxonGroups = "informalTaxonGroups", informalTaxonGroupPersistenceKey, togglePersistenceKey, suggestionInputField, suggestionReceivers } = uiOptions;
            if (togglePersistenceKey) {
                toggled = context[this.getTogglePersistenceContextKey(props)];
            }
            const { toggleable } = uiOptions;
            toggled = (toggled !== undefined)
                ? toggled
                : this.state
                    ? this.state.toggled
                    : togglePersistenceKey
                        ? context[this.getTogglePersistenceContextKey(props)] !== undefined
                            ? context[this.getTogglePersistenceContextKey(props)]
                            : (toggleable && toggleable.toggled || false)
                        : false;
            const taxonGroupID = !informalTaxonGroups
                ? undefined
                : informalTaxonGroupPersistenceKey !== undefined
                    ? context[this.getInformalTaxonGroupsPersistenceContextKey(props)]
                    : formData[informalTaxonGroups]
                        ? formData[informalTaxonGroups][0]
                        : undefined;
            let options = Object.assign(Object.assign({}, uiOptions), { onSuggestionSelected: this.onSuggestionSelected, onUnsuggestedSelected: this.onUnsuggestedSelected, inputProps: {
                    onChange: this.onInputChange
                }, isValueSuggested: this.isValueSuggested, getSuggestionFromValue: this.getSuggestionFromValue, onInformalTaxonGroupSelected: informalTaxonGroups ? this.onInformalTaxonGroupSelected : undefined, getSuggestionValue: this.getSuggestionValue, informalTaxonGroupsValue: props.formData[informalTaxonGroups], taxonGroupID, placeholder: toggled
                    ? typeof toggleable.placeholder === "string"
                        ? toggleable.placeholder
                        : this.props.formContext.translations.UnitAutosuggestFieldTogglePlaceholder
                    : uiOptions["ui:placeholder"], controlledValue: suggestionReceivers
                    && suggestionReceivers[suggestionInputField]
                    && suggestionReceivers[suggestionInputField] !== "key" });
            if (toggleable) {
                options.toggled = toggled;
                options.onToggle = this.onToggleChange;
                options = this.getActiveOptions(options, toggled);
            }
            if (suggestionInputField && props.formData && !(0, utils_1.isEmptyString)((0, utils_1.parseJSONPointer)(props.formData, suggestionInputField, !!"safe"))) {
                options.value = (0, utils_1.parseJSONPointer)(props.formData, suggestionInputField);
            }
            if (options.query) {
                options.query = parseQuery(options.query, props, [options.taxonGroupID]);
            }
            const innerUiSchema = (0, utils_1.getInnerUiSchema)(uiSchema);
            const _uiSchemaJSONPointer = (0, utils_1.uiSchemaJSONPointer)(schema, suggestionInputField);
            const suggestionInputFieldExistingUiSchema = (0, utils_1.parseJSONPointer)(innerUiSchema, _uiSchemaJSONPointer);
            let widgetProps = suggestionInputFieldExistingUiSchema || {};
            if (options.chooseImages && props.formContext.uiSchemaContext.isEdit && options.value) {
                widgetProps = Object.assign(Object.assign({}, widgetProps), { "ui:title": (0, utils_1.capitalizeFirstLetter)(options.orWriteSpeciesNameLabel || props.formContext.translations.orWriteSpeciesName) });
            }
            let _uiSchema = (0, utils_1.updateSafelyWithJSONPointer)(innerUiSchema, Object.assign(Object.assign({ "ui:widget": "AutosuggestWidget" }, widgetProps), { "ui:options": Object.assign(Object.assign({}, (0, utils_1.getUiOptions)((suggestionInputFieldExistingUiSchema || {})[suggestionInputField])), options) }), _uiSchemaJSONPointer);
            return { schema, uiSchema: _uiSchema, toggled, taxonGroupID };
        };
        this.getActiveOptions = (options, toggled) => {
            toggled = (toggled !== undefined) ? toggled : (this.state || {}).toggled;
            return toggled ? (0, deepmerge_1.default)(options, options.toggleable) : options;
        };
        this.getSuggestionValue = (suggestion, def) => {
            const { suggestionValueParse } = this.getActiveOptions((0, utils_1.getUiOptions)(this.props.uiSchema));
            return suggestionValueParse
                ? this.getSuggestionReceiverValue(suggestion, suggestionValueParse)
                : def;
        };
        this.onSuggestionSelected = (suggestion, mounted) => {
            if (suggestion === null)
                suggestion = undefined;
            let { formData, uiSchema, formContext } = this.props;
            const { suggestionReceivers, autosuggestField, suggestionValueField, autocopy, suggestionInputField } = this.getActiveOptions((0, utils_1.getUiOptions)(uiSchema));
            const handleSuggestionReceivers = (formData, suggestion) => {
                for (let fieldName in suggestionReceivers) {
                    const fieldVal = this.getSuggestionReceiverValue(suggestion, suggestionReceivers[fieldName]);
                    formData = (0, utils_1.updateFormDataWithJSONPointer)(Object.assign(Object.assign({}, this.props), { formData }), fieldVal, fieldName);
                }
                return formData;
            };
            if (autosuggestField === "unit") {
                let { unit } = suggestion;
                if (unit.unitType) {
                    unit.informalTaxonGroups = unit.unitType;
                    delete unit.unitType;
                }
                unit = (mounted && formContext.formDataTransformers || []).reduce((unit, { "ui:field": uiField, props: fieldProps }) => {
                    const { state = {} } = new fieldProps.registry.fields[uiField](Object.assign(Object.assign({}, fieldProps), { formData: unit }));
                    return state.formData;
                }, unit);
                formData = handleSuggestionReceivers(formData, {});
                formData = Object.assign(Object.assign({}, (0, utils_1.updateFormDataWithJSONPointer)(Object.assign(Object.assign({}, this.props), { formData }), undefined, suggestionValueField)), unit);
                if ((0, utils_1.isEmptyString)((0, utils_1.parseJSONPointer)(this.props.formData, suggestionInputField, !!"safe")) && autocopy && !this.props.formContext.utils.formDataEquals(this.props.formData, formData, this.props.idSchema.$id)) {
                    this.onNextTick = () => this.props.formContext.services.customEvents.send(this.props.idSchema.$id, "copy", autocopy);
                }
            }
            else {
                formData = handleSuggestionReceivers(formData, suggestion);
            }
            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.state.formData, pointer)), formData);
                lajiFormInstance.onChange((0, utils_1.updateSafelyWithJSONPointer)(lajiFormInstance.getFormData(), newFormData, pointer));
            }
        };
        this.onConfirmUnsuggested = (value) => {
            let { formData, uiSchema } = this.props;
            const { suggestionReceivers, suggestionInputField } = this.getActiveOptions((0, utils_1.getUiOptions)(uiSchema));
            Object.keys(suggestionReceivers).forEach(fieldName => {
                const defaultValue = (0, utils_1.getDefaultFormState)((0, utils_1.parseJSONPointer)(this.props.schema, (0, utils_1.schemaJSONPointer)(this.props.schema, fieldName)));
                formData = (0, utils_1.updateFormDataWithJSONPointer)(Object.assign(Object.assign({}, this.props), { formData }), defaultValue, fieldName);
            });
            formData = (0, utils_1.updateFormDataWithJSONPointer)(Object.assign(Object.assign({}, this.props), { formData }), value, suggestionInputField);
            this.props.onChange(formData);
        };
        this.onInputChange = ({ target: { value } }) => {
            let { formData, uiSchema } = this.props;
            const { inputTransformer } = this.getActiveOptions((0, utils_1.getUiOptions)(uiSchema));
            if (inputTransformer) {
                const regexp = new RegExp(inputTransformer.regexp);
                if (value.match(regexp)) {
                    if (!formData)
                        formData = {};
                    let formDataChange = {};
                    value = value.replace(regexp, "$1");
                    if (inputTransformer.transformations)
                        for (let transformField in inputTransformer.transformations) {
                            formDataChange[transformField] = inputTransformer.transformations[transformField];
                        }
                    formData = Object.assign(Object.assign({}, formData), formDataChange);
                    this.props.onChange(formData);
                }
            }
            return value;
        };
        this.isValueSuggested = () => {
            const { formData, uiSchema } = this.props;
            const { suggestionValueField } = this.getActiveOptions((0, utils_1.getUiOptions)(uiSchema));
            if (suggestionValueField) {
                return !!(0, utils_1.parseJSONPointer)(formData, suggestionValueField, !!"safe");
            }
            for (let fieldName in this.getActiveOptions((0, utils_1.getUiOptions)(uiSchema)).suggestionReceivers) {
                if (!formData || !(0, utils_1.parseJSONPointer)(formData, fieldName, !!"safe")) {
                    return false;
                }
            }
            return true;
        };
        this.getSuggestionFromValue = () => {
            const { formData, uiSchema } = this.props;
            const { suggestionValueField, suggestionInputField } = this.getActiveOptions((0, utils_1.getUiOptions)(uiSchema));
            const suggestionValue = (0, utils_1.parseJSONPointer)(formData, suggestionValueField, !!"safe");
            const suggestionInputValue = (0, utils_1.parseJSONPointer)(formData, suggestionInputField, !!"safe");
            const value = suggestionInputField && formData && !(0, utils_1.isEmptyString)(suggestionValue) ?
                suggestionInputValue : undefined;
            const key = suggestionValueField && formData && !(0, utils_1.isEmptyString)(suggestionValue) ?
                suggestionValue : undefined;
            let suggestion = undefined;
            if (value !== undefined && key !== undefined) {
                suggestion = { value, key };
            }
            return suggestion ? Promise.resolve(suggestion) : Promise.reject();
        };
        this.onInformalTaxonGroupSelected = (informalTaxonID) => {
            const { uiSchema } = this.props;
            const { informalTaxonGroups, informalTaxonGroupPersistenceKey } = this.getActiveOptions((0, utils_1.getUiOptions)(uiSchema));
            if (informalTaxonGroupPersistenceKey !== undefined) {
                (0, Context_1.default)(this.props.formContext.contextId)[this.getInformalTaxonGroupsPersistenceContextKey(this.props)] = informalTaxonID;
                this.setState(this.getStateFromProps(this.props));
            }
            else {
                this.props.onChange(Object.assign(Object.assign({}, this.props.formData), { [informalTaxonGroups]: [informalTaxonID] }));
            }
        };
        this.onToggleChange = (value) => {
            const { togglePersistenceKey } = (0, utils_1.getUiOptions)(this.props.uiSchema);
            if (togglePersistenceKey) {
                (0, Context_1.default)(this.props.formContext.contextId)[this.getTogglePersistenceContextKey(this.props)] = value;
            }
            this.setState(this.getStateFromProps(this.props, value));
        };
        this.props.formContext.services.settings.bind(this, props);
        this.state = this.getStateFromProps(props);
    }
    getSuggestionReceiverValue(suggestion, suggestionReceiver) {
        // undefined suggestion clears value.
        let fieldVal = undefined;
        if (typeof suggestion === "object") {
            const suggestionValPath = suggestionReceiver;
            if (suggestionValPath[0] === "$") {
                fieldVal = suggestionParsers[suggestionValPath.substring(1)](suggestion);
            }
            else {
                const fieldsToTry = suggestionValPath.split("||").map(s => s.trim());
                for (let field of fieldsToTry) {
                    fieldVal = (0, utils_1.parseJSONPointer)(suggestion, field);
                    if (fieldVal !== undefined) {
                        break;
                    }
                }
            }
        }
        return fieldVal;
    }
    render() {
        const { SchemaField } = this.props.registry.fields;
        return React.createElement(SchemaField, Object.assign({}, this.props, { uiSchema: this.state.uiSchema }));
    }
}
AutosuggestField.propTypes = {
    uiSchema: PropTypes.shape({
        "ui:options": PropTypes.shape({
            autosuggestField: PropTypes.string.isRequired,
            suggestionInputField: PropTypes.string.isRequired,
            allowNonsuggestedValue: PropTypes.bool,
            suggestionReceivers: PropTypes.object.isRequired,
            inputTransformer: PropTypes.shape({
                regexp: PropTypes.string.isRequired,
                transformations: PropTypes.object.isRequired
            }),
            informalTaxonGroups: PropTypes.string,
            informalTaxonGroupPersistenceKey: PropTypes.string
        }).isRequired,
        uiSchema: PropTypes.object
    }).isRequired,
    schema: PropTypes.shape({
        type: PropTypes.oneOf(["object"])
    }).isRequired,
    formData: PropTypes.object.isRequired
};
exports.default = AutosuggestField;
