"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetamodelService = void 0;
const tslib_1 = require("tslib");
const adaptable_metamodel_1 = require("../../metamodel/adaptable.metamodel");
const DocumentationLinkConstants_1 = require("../Constants/DocumentationLinkConstants");
const StringExtensions_1 = tslib_1.__importDefault(require("../Extensions/StringExtensions"));
const AdaptableLogger_1 = require("../../agGrid/AdaptableLogger");
const DefaultAdaptableOptions_1 = require("../../AdaptableOptions/DefaultAdaptableOptions");
const supportedMetamodelTypes = ['s', 'n', 'b', 'a', 'f', 'R'];
class MetamodelService {
    constructor(getAdaptableOptions, validateOptions) {
        this.gridInfoOptions = new Map();
        this.getAdaptableOptions = () => null;
        this.getAdaptableOptions = getAdaptableOptions;
        this.gridInfoOptions = this.buildGridInfoOptions();
        if (validateOptions) {
            this.validateAdaptableOptionsValues();
        }
    }
    getGridInfoOptions() {
        return this.gridInfoOptions;
    }
    getGridInfoNoCodeOptions() {
        return this.buildGridInfoOptions({
            filterItemProperty: (itemProperty) => {
                return itemProperty.noCode === 'item';
            },
            filterEmptyAdaptableOptions: false,
        });
    }
    validateAdaptableOptionsValues() {
        const adaptableOptionsValues = this.getAdaptableOptions();
        const adaptableOptionsMetamodel = this.getAdaptableOptionsMetamodel();
        const adaptableOptionsDefaultValues = this.getAdaptableOptionsDefaultValues();
        if (adaptableOptionsValues.adaptableId?.includes('.')) {
            AdaptableLogger_1.AdaptableLogger.consoleWarnBase("The 'AdaptableOptions.adaptableId' property should NOT include a '.' character.  We strongly recommend to remove it to avoid unexpected AdapTable behavior.");
        }
        const validationErrors = [];
        this.validateOptionsObject(validationErrors, 'AdaptableOptions', adaptableOptionsValues, adaptableOptionsMetamodel, adaptableOptionsDefaultValues);
        if (validationErrors.length) {
            AdaptableLogger_1.AdaptableLogger.consoleWarnBase([
                'AdaptableOptions validation errors:',
                '\n',
                ...validationErrors,
                '\n',
                `See AdaptableOptions documentation at ${DocumentationLinkConstants_1.AdaptableOptionsDocsLink} for details`,
            ].join('\n'));
        }
    }
    validateOptionsObject(validationErrors, optionsObjectName, optionsObject, optionsObjectMetamodel, optionsObjectDefaultValues) {
        Object.entries(optionsObject).forEach(([optionKey, optionValue]) => {
            // ignore fdc3options
            if (optionKey === 'fdc3Options') {
                return;
            }
            const optionMetamodel = optionsObjectMetamodel?.props?.find((metamodelProperty) => metamodelProperty.name === optionKey);
            // Return if property not found - log if not 'Revision' or 'Uuid' or 'AdaptableVersion'
            if (!optionMetamodel) {
                if (optionKey === 'Revision' || optionKey === 'Uuid' || optionKey === 'AdaptableVersion') {
                    return;
                }
                else {
                    validationErrors.push(`${optionsObjectName}.${optionKey} (value=${optionValue}) :: unknown/unsupported property, will be ignored`);
                    return;
                }
            }
            // let's try to validate the type of the provided value
            const expectedOptionsValueType = this.getExpectedOptionsValueType(optionMetamodel);
            const providedOptionsValueType = Array.isArray(optionValue) ? 'a' : typeof optionValue;
            if (!expectedOptionsValueType) {
                return;
            }
            // if it's a REFERENCE, we try to go (recursively) deeper
            if (expectedOptionsValueType === 'R') {
                const referenceObjectName = optionMetamodel.ref;
                const referenceObject = optionsObject[optionKey];
                const referenceObjectMetamodel = this.getAdaptableMetamodel()[referenceObjectName];
                if (referenceObject && referenceObjectMetamodel?.kind === 'I') {
                    this.validateOptionsObject(validationErrors, referenceObjectName, referenceObject, referenceObjectMetamodel, optionsObjectDefaultValues?.[optionKey]);
                }
            }
            else if (supportedMetamodelTypes.includes(providedOptionsValueType) &&
                providedOptionsValueType !== expectedOptionsValueType) {
                validationErrors.push(`${optionsObjectName}.${optionKey} (value=${optionValue}) :: wrong type (${providedOptionsValueType}), expected ${expectedOptionsValueType}`);
            }
        });
    }
    getExpectedOptionsValueType(metamodelProperty) {
        const metamodelPropertyKind = metamodelProperty.kind;
        if (supportedMetamodelTypes.includes(metamodelPropertyKind)) {
            return metamodelPropertyKind;
        }
    }
    buildGridInfoOptions(options) {
        const { filterItemProperty, filterEmptyAdaptableOptions = true } = options || {};
        const gridInfoOptions = new Map();
        const adaptableMetamodel = this.getAdaptableMetamodel();
        const adaptableOptionsMetamodel = this.getAdaptableOptionsMetamodel();
        const adaptableOptionsValues = this.getAdaptableOptions();
        const adaptableOptionsDefaultValues = this.getAdaptableOptionsDefaultValues();
        if (!adaptableOptionsMetamodel) {
            // should never happen
            return gridInfoOptions;
        }
        // root-level properties are grouped in a synthetic 'Base Options' container
        const baseOptionsItems = this.mapGridInfoContainerItems(adaptableOptionsMetamodel, adaptableOptionsValues, adaptableOptionsDefaultValues, filterItemProperty);
        gridInfoOptions.set('baseOptions', {
            containerLabel: 'Base Options',
            items: baseOptionsItems,
        });
        // map containers
        adaptableOptionsMetamodel.props
            .filter((optionItem) => optionItem.gridInfo === 'container')
            .forEach((containerOptionItem) => {
            const containerMetamodelName = containerOptionItem.ref;
            const adaptableOptionsName = containerOptionItem.name;
            const containerOptionsMetamodel = adaptableMetamodel[containerMetamodelName];
            // @ts-ignore
            const containerOptionsValues = adaptableOptionsValues[adaptableOptionsName];
            // @ts-ignore
            const containerOptionsDefaultValues = adaptableOptionsDefaultValues[adaptableOptionsName];
            if (!containerOptionsMetamodel ||
                (!containerOptionsValues && filterEmptyAdaptableOptions) ||
                !containerOptionsDefaultValues) {
                // should never happen
                return;
            }
            const optionItems = this.mapGridInfoContainerItems(containerOptionsMetamodel, containerOptionsValues, containerOptionsDefaultValues, filterItemProperty);
            gridInfoOptions.set(containerOptionItem.name, {
                containerLabel: this.extractUiLabel(containerOptionItem),
                items: optionItems,
            });
        });
        return gridInfoOptions;
    }
    mapGridInfoContainerItems(optionItemContainer, adaptableOptionsValues, defaultAdaptableOptionsValues, filter = (itemProperty) => itemProperty.gridInfo === 'item') {
        return optionItemContainer.props.filter(filter).map((itemProperty) => {
            return {
                name: itemProperty.name,
                value: adaptableOptionsValues?.[itemProperty.name],
                defaultValue: defaultAdaptableOptionsValues[itemProperty.name],
                kind: itemProperty.kind,
                description: StringExtensions_1.default.UnescapeHtmlEntities(itemProperty.desc),
                uiLabel: this.extractUiLabel(itemProperty),
            };
        });
    }
    getAdaptableOptionsDefaultValues() {
        return (0, DefaultAdaptableOptions_1.getDefaultAdaptableOptions)();
    }
    getAdaptableMetamodel() {
        return adaptable_metamodel_1.ADAPTABLE_METAMODEL;
    }
    getAdaptableOptionsMetamodel() {
        return this.getAdaptableMetamodel()['AdaptableOptions'];
    }
    extractUiLabel(item) {
        return item.uiLabel || this.formatCamelCaseToHumanText(item.name);
    }
    formatCamelCaseToHumanText(camelCase) {
        if (!camelCase || camelCase == null) {
            return null;
        }
        const rex = /([A-Z])([A-Z])([a-z])|([a-z])([A-Z])/g;
        const words = camelCase.replace(rex, '$1$4 $2$3$5').replace('.', ' ').split(' ');
        return words
            .map((word) => word.substring(0, 1).toUpperCase() +
            (word.length > 1 ? word.substring(1, word.length) : ''))
            .join(' ');
    }
}
exports.MetamodelService = MetamodelService;
