"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 ConditionalUiSchemaField_1 = require("./ConditionalUiSchemaField");
const FilterArrayField_1 = require("./FilterArrayField");
const utils_1 = require("../../utils");
const Context_1 = __importDefault(require("../../Context"));
const ArrayFieldTemplate_1 = require("../templates/ArrayFieldTemplate");
const memoizee_1 = __importDefault(require("memoizee"));
class MultiArrayField extends React.Component {
    constructor() {
        super(...arguments);
        this.onChange = (0, memoizee_1.default)((idx) => (formData) => {
            let offset = 0;
            for (let i = 0; i < idx; i++) {
                offset += Object.keys(this.groupedItems[i]).length;
            }
            let { persistenceKey } = (0, utils_1.getUiOptions)(this.props.uiSchema);
            const lengthChange = formData.length - this.groupedItems[idx].length;
            const context = (0, Context_1.default)(`${persistenceKey}_MULTI`);
            // Fix idxs map for items after this group.
            if (lengthChange && context.nonGroupedMap) {
                const changes = {};
                const changeFrom = {};
                for (let i = offset + Object.keys(this.groupedItems[idx]).length; i < (this.props.formData || []).length; i++) {
                    if (context.nonGroupedMap[i]) {
                        changes[i + lengthChange] = context.nonGroupedMap[i];
                        changeFrom[i] = true;
                    }
                }
                context.nonGroupedMap = Object.assign(Object.assign({}, context.nonGroupedMap), changes);
                Object.keys(changeFrom).filter(idx => !(idx in changes)).forEach(idx => {
                    delete context.nonGroupedMap[idx];
                });
            }
            if (typeof persistenceKey === "string" && formData.length !== this.groupedItems[idx].length) {
                if (!context.nonGroupedMap) {
                    context.nonGroupedMap = {};
                }
                context.nonGroupedMap[offset + formData.length - 1] = idx;
            }
            formData.forEach(item => {
                this.groupItemIds[idx][(0, utils_1.getUUID)(item)] = true;
            });
            const newFormData = this.groupedItems.reduce((flat, groupItems, _idx) => {
                if (_idx === idx)
                    return [...flat, ...formData];
                return [...flat, ...groupItems];
            }, []);
            this.props.onChange(newFormData);
        });
    }
    render() {
        const { props } = this;
        let { groups, cache = true, arrayMerge, persistByParent, persistenceKey, renderNonGrouped = false, nonGroupedOperations } = (0, utils_1.getUiOptions)(this.props.uiSchema);
        if (groups.length && !this.cache) {
            this.cache = Array(groups.length).fill(undefined).map(_ => ({}));
        }
        if (groups.length) {
            this.arrayKeyFunctions = Array(groups.length + 1).fill(undefined).map((_, idx) => getArrayKeyFunctions(this, idx));
        }
        if (groups.length && !this.groupItemIds) {
            const getGroupItemIds = () => Array(groups.length + 1).fill(undefined).map(_ => ({}));
            if (persistByParent) {
                const parentId = this.props.formContext._parentLajiFormId;
                const context = (0, Context_1.default)(`${parentId}_MULTI`);
                this.groupItemIds = context.groupItemIds || getGroupItemIds();
                context.groupItemIds = this.groupItemIds;
            }
            else if (typeof persistenceKey === "string") {
                const context = (0, Context_1.default)(`${persistenceKey}_MULTI`);
                this.groupItemIds = context.groupItemIds || getGroupItemIds();
                context.groupItemIds = this.groupItemIds;
            }
            else {
                this.groupItemIds = getGroupItemIds();
            }
        }
        const groupedItems = Array(groups.length + 1).fill(undefined).map(_ => []);
        const addToGroup = (groupIdx, item) => {
            const id = (0, utils_1.getUUID)(item);
            this.groupItemIds[groupIdx][id] = true;
            groupedItems[groupIdx].push(item);
            return true;
        };
        const nonGrouped = [];
        (props.formData || []).forEach((item, idx) => {
            const id = (0, utils_1.getUUID)(item);
            // Restore persisted group
            for (let g = 0; g < this.groupItemIds.length; g++) {
                if (this.groupItemIds[g][id]) {
                    addToGroup(g, item);
                    return;
                }
            }
            const addedToGroup = groups.some((group, groupIdx) => {
                const { rules = [] } = group;
                let passes;
                if (cache) {
                    const check = (0, utils_1.checkArrayRules)(rules, { formData: props.formData || [] }, idx, this.cache[groupIdx]);
                    const { cache } = check;
                    passes = check.passes;
                    this.cache[groupIdx] = cache;
                }
                else {
                    passes = (0, utils_1.checkArrayRules)(rules, { formData: props.formData || [] }, idx);
                }
                if (passes) {
                    return addToGroup(groupIdx, item);
                }
            });
            if (!addedToGroup) {
                nonGrouped.push([item, idx]);
            }
        });
        nonGrouped.forEach(([item, idx]) => {
            const context = (0, Context_1.default)(`${persistenceKey}_MULTI`);
            const groupIdx = this.groupItemIds.length - 1;
            if (typeof persistenceKey === "string" && (context.nonGroupedMap || {})[idx] !== undefined && this.groupItemIds[groupIdx]) {
                addToGroup(context.nonGroupedMap[idx], item);
            }
            else {
                addToGroup(groupIdx, item);
            }
        });
        this.groupedItems = groupedItems;
        const withoutNonGrouped = [...groupedItems];
        withoutNonGrouped.pop();
        let offset = 0;
        const offsets = (renderNonGrouped ? groupedItems : withoutNonGrouped).map((group, idx) => {
            let idxOffsets;
            if (!idx) {
                idxOffsets = {};
            }
            else {
                idxOffsets = group.reduce((g, i, _idx) => {
                    g[_idx] = offset;
                    return g;
                }, {});
            }
            offset += group.length;
            return idxOffsets;
        }, []);
        return (renderNonGrouped ? groupedItems : withoutNonGrouped).map((items, idx) => {
            const operations = renderNonGrouped && idx === groupedItems.length - 1
                ? nonGroupedOperations
                : groups[idx].operations;
            const innerUiSchema = (0, utils_1.getInnerUiSchema)(props.uiSchema);
            let uiSchema = operations
                ? (0, ConditionalUiSchemaField_1.computeUiSchema)(innerUiSchema, operations, arrayMerge)
                : innerUiSchema;
            uiSchema = (0, utils_1.updateSafelyWithJSONPointer)(uiSchema, this.arrayKeyFunctions[idx], "/ui:options/arrayKeyFunctions");
            uiSchema = (0, utils_1.updateSafelyWithJSONPointer)(uiSchema, offsets[idx], "/ui:options/idxOffsets");
            let offset = 0;
            for (let i = 0; i < idx; i++) {
                offset += this.groupedItems[i].length;
            }
            uiSchema = Object.assign(Object.assign({}, uiSchema), { "ui:options": Object.assign(Object.assign({}, (0, utils_1.getUiOptions)(uiSchema)), { startIdx: offset }) });
            const errorSchema = {};
            for (const key in props.errorSchema) {
                if (key >= offset && key < offset + this.groupedItems[idx].length) {
                    errorSchema[key - offset] = Object.assign({}, props.errorSchema[key]);
                }
            }
            const { SchemaField } = this.props.registry.fields;
            return (React.createElement(SchemaField, Object.assign({ key: idx }, props, { uiSchema: uiSchema, errorSchema: errorSchema, formData: items, onChange: this.onChange(idx) })));
        });
    }
}
MultiArrayField.propTypes = {
    uiSchema: PropTypes.shape({
        "ui:options": PropTypes.shape({
            rules: FilterArrayField_1.arrayRulesPropType,
            itemsOperations: PropTypes.oneOfType([
                ConditionalUiSchemaField_1.operationPropType,
                PropTypes.arrayOf(ConditionalUiSchemaField_1.operationPropType)
            ]),
            cache: PropTypes.bool
        })
    }),
    schema: PropTypes.shape({
        type: PropTypes.oneOf(["array"])
    }).isRequired,
    formData: PropTypes.array
};
exports.default = MultiArrayField;
const getArrayKeyFunctions = (that) => {
    const { arrayKeyFunctions: optionsArrayKeyFunctions } = (0, utils_1.getUiOptions)(that.props.uiSchema);
    const _arrayKeyFunctions = optionsArrayKeyFunctions || ArrayFieldTemplate_1.arrayKeyFunctions;
    return Object.assign(Object.assign({}, _arrayKeyFunctions), { insert: (e, _props) => {
            const props = _props.getProps();
            const inputElem = (0, utils_1.findNearestParentTabbableElem)(document.activeElement);
            if (!inputElem) {
                return false;
            }
            if (!inputElem.id.startsWith(props.idSchema.$id)) {
                return false;
            }
            const itemIdx = inputElem.id.replace(props.idSchema.$id, "").replace(/^_?([0-9]+).*$/, "$1");
            const { startIdx } = (0, utils_1.getUiOptions)(props.uiSchema);
            if (itemIdx < startIdx || itemIdx >= startIdx + (props.formData || []).length) {
                return false;
            }
            return _arrayKeyFunctions.insert(e, _props);
        }, navigateArray: (e, options) => {
            const _options = Object.assign(Object.assign({}, options), { getProps: () => (Object.assign(Object.assign({}, options.getProps()), { formData: that.props.formData })) });
            return _arrayKeyFunctions.navigateArray(e, _options);
        } });
};
