"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Component_1 = __importDefault(require("../_classes/component/Component"));
const DataGrid_1 = __importDefault(require("../datagrid/DataGrid"));
const lodash_1 = __importDefault(require("lodash"));
const eventemitter3_1 = __importDefault(require("eventemitter3"));
const utils_1 = require("../../utils/utils");
class DataMapComponent extends DataGrid_1.default {
    static schema(...extend) {
        return Component_1.default.schema({
            label: 'Data Map',
            key: 'dataMap',
            type: 'datamap',
            clearOnHide: true,
            addAnother: 'Add Another',
            disableAddingRemovingRows: false,
            keyBeforeValue: true,
            valueComponent: {
                type: 'textfield',
                key: 'value',
                label: 'Value',
                input: true,
            },
            input: true,
            validate: {
                maxLength: 0,
                minLength: 0,
            },
        }, ...extend);
    }
    static get builderInfo() {
        return {
            title: 'Data Map',
            icon: 'th-list',
            group: 'data',
            documentation: '/userguide/form-building/data-components#data-map',
            showPreview: false,
            weight: 20,
            schema: DataMapComponent.schema(),
        };
    }
    get schema() {
        const schema = super.schema;
        if (this.components && this.components.length > 0) {
            schema.valueComponent = this.components[this.components.length - 1].schema;
        }
        return lodash_1.default.omit(schema, 'components');
    }
    static savedValueTypes(schema) {
        return ((0, utils_1.getComponentSavedTypes)(schema) || [
            utils_1.componentValueTypes.object,
        ]);
    }
    constructor(component, options, data) {
        super(component, options, data);
        this.type = 'datamap';
    }
    init() {
        this.components = [];
        this.rows = [];
        this.createRows();
        this.visibleColumns = {
            key: true,
            [this.valueKey]: true,
        };
        this.component.valueComponent.hideLabel = true;
    }
    get defaultSchema() {
        return DataMapComponent.schema();
    }
    get emptyValue() {
        return {};
    }
    get dataValue() {
        if (!this.key) {
            return this.emptyValue;
        }
        return lodash_1.default.get(this.data, this.key, this.emptyValue);
    }
    set dataValue(value) {
        super.dataValue = value;
    }
    get defaultValue() {
        const value = super.defaultValue;
        if (Array.isArray(value)) {
            return value[0];
        }
        return this.emptyValue;
    }
    get keySchema() {
        return {
            type: 'textfield',
            input: true,
            hideLabel: true,
            label: this.component.keyLabel || 'Key',
            key: '__key',
            disableBuilderActions: true,
        };
    }
    get valueKey() {
        return this.component.valueComponent.key;
    }
    getRowValues() {
        const dataValue = this.dataValue;
        if (this.builderMode) {
            return [
                dataValue,
            ];
        }
        if (lodash_1.default.isEmpty(dataValue)) {
            return [];
        }
        return Object.keys(dataValue).map(() => dataValue);
    }
    getComponentsContainer() {
        if (this.builderMode) {
            return this.getComponents().map((comp) => comp.component);
        }
        return super.getComponentsContainer();
    }
    get iteratableRows() {
        return this.rows.map((row) => {
            return {
                components: row,
                data: lodash_1.default.mapValues(row, (comp) => comp.dataValue)
            };
        });
    }
    componentContext(component) {
        return this.iteratableRows[component.row].data[component.key];
    }
    hasHeader() {
        return true;
    }
    hasRemoveButtons() {
        return (!this.component.disableAddingRemovingRows &&
            !this.options.readOnly &&
            !this.disabled &&
            this.fullMode);
    }
    getColumns() {
        const keySchema = Object.assign({}, this.keySchema);
        const valueSchema = Object.assign({}, this.component.valueComponent);
        keySchema.hideLabel = false;
        valueSchema.hideLabel = false;
        return this.component.keyBeforeValue
            ? [
                keySchema,
                valueSchema,
            ]
            : [
                valueSchema,
                keySchema,
            ];
    }
    getRowKey(rowIndex) {
        const keys = Object.keys(this.dataValue);
        if (!keys[rowIndex]) {
            keys[rowIndex] = (0, utils_1.uniqueKey)(this.dataValue, this.defaultRowKey);
        }
        return keys[rowIndex];
    }
    get defaultRowKey() {
        return 'key';
    }
    setRowComponentsData(rowIndex, rowData) {
        lodash_1.default.each(this.rows[rowIndex], (component) => {
            if (component.key === '__key') {
                component.data = {
                    __key: Object.keys(rowData)[rowIndex],
                };
            }
            else {
                component.data = rowData;
            }
        });
    }
    getValueAsString(value, options) {
        if ((options === null || options === void 0 ? void 0 : options.email) && this.visible && !this.skipInEmail && lodash_1.default.isObject(value)) {
            let result = `
        <table border="1" style="width:100%">
          <tbody>
      `;
            result = Object.keys(value).reduce((result, key) => {
                const componentInstance = this.findComponentInstance(key);
                const viewValue = componentInstance
                    ? componentInstance.getView(value[key], options)
                    : this.getView(value[key], options);
                result += `
          <tr>
            <th style="padding: 5px 10px;">${key}</th>
            <td style="width:100%;padding:5px 10px;">${viewValue}</td>
          </tr>
        `;
                return result;
            }, result);
            result += `
          </tbody>
        </table>
      `;
            return result;
        }
        if (lodash_1.default.isEmpty(value)) {
            return '';
        }
        if (options === null || options === void 0 ? void 0 : options.modalPreview) {
            delete options.modalPreview;
            return this.getDataValueAsTable(value, options);
        }
        return typeof value === 'object' ? '[Complex Data]' : value;
    }
    findComponentInstance(key) {
        var _a;
        if (!this.rows || !this.rows.length) {
            return null;
        }
        // Find component instance with matching key
        const foundRow = lodash_1.default.find(this.rows, (row) => { var _a; return ((_a = row === null || row === void 0 ? void 0 : row[this.valueKey]) === null || _a === void 0 ? void 0 : _a.key) === key; });
        if (foundRow === null || foundRow === void 0 ? void 0 : foundRow[this.valueKey]) {
            return foundRow[this.valueKey];
        }
        // If not found by key, return the first row's value component as fallback
        return ((_a = this.rows[0]) === null || _a === void 0 ? void 0 : _a[this.valueKey]) || null;
    }
    getDataValueAsTable(value, options) {
        let result = `
      <table border="1" style="width:100%">
        <tbody>
    `;
        if (this.visible && lodash_1.default.isObject(value)) {
            Object.keys(value).forEach((key) => {
                const componentInstance = this.findComponentInstance(key);
                const viewValue = componentInstance
                    ? componentInstance.getView(value[key], options)
                    : this.getView(value[key], options);
                result += `
          <tr>
            <th style="padding: 5px 10px;">${key}</th>
            <td style="width:100%;padding:5px 10px;">${viewValue}</td>
          </tr>
        `;
            });
        }
        result += `
        </tbody>
      </table>
    `;
        return result;
    }
    createRowComponents(row, rowIndex) {
        // Use original value component API key in builder mode to be able to edit value component
        let key = this.builderMode ? this.valueKey : this.getRowKey(rowIndex);
        // Create a new event emitter since fields are isolated.
        const options = lodash_1.default.clone(this.options);
        options.events = new eventemitter3_1.default();
        options.name += `[${rowIndex}]`;
        options.row = `${rowIndex}`;
        options.rowIndex = rowIndex;
        const components = {};
        components['__key'] = this.createComponent(this.keySchema, options, {
            __key: this.builderMode ? this.defaultRowKey : key,
        });
        components['__key'].on('componentChange', (event) => {
            const dataValue = this.dataValue;
            const newKey = (0, utils_1.uniqueKey)(dataValue, event.value);
            dataValue[newKey] = dataValue[key];
            delete dataValue[key];
            const comp = components[this.valueKey];
            comp.component.key = newKey;
            key = newKey;
        });
        const valueComponent = lodash_1.default.clone(this.component.valueComponent);
        valueComponent.key = key;
        const componentOptions = lodash_1.default.clone(this.options);
        componentOptions.row = options.row;
        if (this.submissionTimezone) {
            componentOptions.submissionTimezone = this.submissionTimezone;
            if (valueComponent.type === 'datetime') {
                valueComponent.widget = Object.assign(Object.assign({}, valueComponent.widget), { submissionTimezone: this.submissionTimezone });
            }
        }
        const createdComponent = this.createComponent(valueComponent, componentOptions, this.dataValue);
        // Ensure submissionTimezone is set on datetime component instance's widget and options
        if ((createdComponent === null || createdComponent === void 0 ? void 0 : createdComponent.type) === 'datetime' && this.submissionTimezone) {
            createdComponent.component.widget = Object.assign(Object.assign({}, createdComponent.component.widget), { submissionTimezone: this.submissionTimezone });
            createdComponent.options.submissionTimezone = this.submissionTimezone;
        }
        components[this.valueKey] = createdComponent;
        return components;
    }
    get canAddColumn() {
        return false;
    }
    addChildComponent(component) {
        this.component.valueComponent = component;
    }
    saveChildComponent(component) {
        // Update the Value Component, the Key Component is not allowed to edit
        if (component.key !== this.keySchema.key) {
            this.component.valueComponent = component;
        }
    }
    removeChildComponent() {
        const defaultSchema = DataMapComponent.schema();
        this.component.valueComponent = defaultSchema.valueComponent;
    }
    addRow() {
        var _a;
        const index = this.rows.length;
        this.rows[index] = this.createRowComponents(this.dataValue, index);
        this.redraw();
        (_a = this.triggerChange) === null || _a === void 0 ? void 0 : _a.call(this);
    }
    removeRow(index) {
        var _a;
        const keys = Object.keys(this.dataValue);
        if (keys[index]) {
            delete this.dataValue[keys[index]];
        }
        this.rows.splice(index, 1);
        this.redraw();
        (_a = this.triggerChange) === null || _a === void 0 ? void 0 : _a.call(this);
    }
    setValue(value, flags = {}) {
        const changed = this.hasChanged(value, this.dataValue);
        this.dataValue = value;
        this.createRows();
        this.updateOnChange(flags, changed);
        return changed;
    }
    checkColumns() {
        if (this.builderMode || !this.dataValue || !Object.keys(this.dataValue).length) {
            return { rebuild: false, show: true };
        }
        if (Object.keys(this.dataValue).length > (this.rows || []).length) {
            return { rebuild: true, show: true };
        }
        return { rebuild: false, show: true };
    }
}
exports.default = DataMapComponent;
