"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.exportConfigEntityByPathV4 = exports.exportPageV4 = void 0;
const exportCustomColumn_1 = require("./exportCustomColumn");
const common_1 = require("../../common");
const application_1 = require("../application");
const manifest_1 = require("./manifest");
const flexibleColumnLayout_1 = require("./flexibleColumnLayout");
const ux_specification_types_1 = require("@sap/ux-specification-types");
const utils_1 = require("../../common/utils");
const factory_1 = require("./factory");
const utils_2 = require("./utils");
const fpmCustomPage_1 = require("./fpmCustomPage");
const utils_3 = require("../utils/utils");
const viewProperties = ['key', 'index', 'label', 'visible', 'showCounts', 'entitySet', 'keepPreviousPersonalization'];
const doTransfer = [...viewProperties, 'views', 'initialLoad', 'template'];
/**
 * Returns a fresh export results object with default values.
 *
 * @param manifest - manifest to clone
 * @returns New export results object with default values.
 */
const prepareDefaultExportResult = (manifest) => {
    const exportResults = ux_specification_types_1.defaultExportResult;
    exportResults.fragments = [];
    exportResults.views = [];
    exportResults.manifest = JSON.parse(JSON.stringify(manifest));
    return exportResults;
};
/**
 * Determines the target annotation of a table based on the provided IDs and transfer parameters.
 *
 * @param ids - List of IDs of parent elements.
 * @param transferParameters - Parameters used for transferring data.
 * @param configObject - The configuration object containing table details.
 * @returns An object containing the encoded and decoded target annotation.
 */
function determineTargetAnnotationOfTable(ids, transferParameters, configObject) {
    let targetAnnotationEncoded = `@${"com.sap.vocabularies.UI.v1.LineItem" /* UIAnnotationTerms.LineItem */}`;
    let targetAnnotation = `@${"com.sap.vocabularies.UI.v1.LineItem" /* UIAnnotationTerms.LineItem */}`;
    const annotationPath = transferParameters.appSchema.definitions[ux_specification_types_1.DefinitionName.TableSPV]?.[ux_specification_types_1.SchemaTag.annotationPath];
    if (ids.length > 1 && transferParameters.config['table']?.annotationPath) {
        const annoPath = transferParameters.appSchema.definitions[ux_specification_types_1.DefinitionName.ALPTable]?.[ux_specification_types_1.SchemaTag.annotationPath];
        targetAnnotationEncoded = targetAnnotation = annoPath && annoPath.substr(annoPath.lastIndexOf('@'));
    }
    else if (configObject.table?.annotationPath) {
        //view definition
        targetAnnotationEncoded = targetAnnotation = `@${configObject.table.annotationPath}`;
    }
    else if (ids[0] === ux_specification_types_1.PropertyName.table && annotationPath) {
        const annotationPathArray = annotationPath.split('/');
        targetAnnotationEncoded = targetAnnotation = annotationPathArray[annotationPathArray.length - 1];
    }
    return { targetAnnotationEncoded, targetAnnotation };
}
/**
 * Determines the target annotation of a chart based on the provided IDs and transfer parameters.
 *
 * @param ids - List of IDs of parent elements.
 * @param transferParameters - Parameters used for transferring data.
 * @param configObject - The configuration object containing chart details.
 * @returns An object containing the encoded and decoded target annotation.
 */
function determineTargetAnnotationOfChart(ids, transferParameters, configObject) {
    let targetAnnotationEncoded, targetAnnotation;
    if (ids.length > 1) {
        const annoPath = transferParameters.appSchema.definitions[ux_specification_types_1.DefinitionName.ALPChart]?.[ux_specification_types_1.SchemaTag.annotationPath];
        targetAnnotationEncoded = targetAnnotation = annoPath && annoPath.substr(annoPath.lastIndexOf('@'));
    }
    else {
        //chart level
        targetAnnotationEncoded = targetAnnotation = `@${configObject.chart?.annotationPath}`;
    }
    return { targetAnnotationEncoded, targetAnnotation };
}
/**
 * Determines the target annotation based on the parent view.
 *
 * @param ids - List of IDs of parent elements.
 * @param targetAnnotation - The target annotation to be determined.
 * @param targetAnnotationEncoded - The encoded target annotation to be determined.
 * @param parentDefinition - The parent definition containing annotation details.
 * @param transferParameters - Parameters used for transferring data.
 * @returns An object containing the updated targetAnnotation and targetAnnotationEncoded.
 */
function determineTargetAnnotationBasedOnParentView(ids, targetAnnotation, targetAnnotationEncoded, parentDefinition, transferParameters) {
    if (ids.length === 3) {
        //the parent definition comprises the annotation of the visualization of the (Selection)PresentationVariant.
        targetAnnotation = targetAnnotationEncoded =
            parentDefinition[ux_specification_types_1.SchemaTag.annotationPath] &&
                parentDefinition[ux_specification_types_1.SchemaTag.annotationPath].substr(parentDefinition[ux_specification_types_1.SchemaTag.annotationPath].lastIndexOf('@'));
    }
    else if (ids.length > 3) {
        //the own definition comprises the lineItem or chart annotation from the visualization
        const ownDefinitionName = parentDefinition.$ref?.split(common_1.DEFINITION_LINK_PREFIX)[1];
        const ownDefinition = ownDefinitionName && transferParameters.appSchema.definitions[ownDefinitionName];
        const annotation = ownDefinition?.[ux_specification_types_1.SchemaTag.annotationPath] &&
            ownDefinition[ux_specification_types_1.SchemaTag.annotationPath].substr(ownDefinition[ux_specification_types_1.SchemaTag.annotationPath].lastIndexOf('@'));
        targetAnnotation = targetAnnotationEncoded = ownDefinition['entitySet']
            ? `/${ownDefinition['entitySet']}/${annotation}`
            : annotation;
    }
    return { targetAnnotation, targetAnnotationEncoded };
}
/**
 * Determines the target annotation of a view.
 *
 * @param {TransferParameterTypeV4} transferParameters - list of parameters passed from API
 * @param {string[]} ids - list of ids of parent elements
 * @returns An object containing the target annotation and encoded target annotation.
 */
function determineTargetAnnotationOfView(transferParameters, ids) {
    let targetAnnotation, targetAnnotationEncoded;
    const parentDefinition = transferParameters.appSchema.definitions.MultiTableModeV4?.properties[ids[2]];
    if (parentDefinition) {
        ({ targetAnnotation, targetAnnotationEncoded } = determineTargetAnnotationBasedOnParentView(ids, targetAnnotation, targetAnnotationEncoded, parentDefinition, transferParameters));
    }
    else {
        //newly created view ==> compare annotationPath with that of the table
        const viewPathInConfig = transferParameters.config[ids[0]][ids[1]][ids[2]].annotationPath;
        let tableInSchema = transferParameters.appSchema.definitions.Table;
        if (tableInSchema[ux_specification_types_1.SchemaTag.annotationPath] &&
            tableInSchema[ux_specification_types_1.SchemaTag.annotationPath].endsWith(viewPathInConfig)) {
            if (tableInSchema.$ref === '#/definitions/TableSPV') {
                tableInSchema = transferParameters.appSchema.definitions.TableSPV;
                //the view got derived from the previous table
                const annoParts = tableInSchema[ux_specification_types_1.SchemaTag.annotationPath].split('/');
                targetAnnotation = targetAnnotationEncoded = annoParts[annoParts.length - 1];
                return { targetAnnotation, targetAnnotationEncoded };
            }
        }
    }
    delete transferParameters.appSchema.definitions.Table[ux_specification_types_1.SchemaTag.annotationPath];
    return { targetAnnotation, targetAnnotationEncoded };
}
/**
 * Determines the target annotation of header sections based on the provided IDs, custom flag, and optional title.
 *
 * @param ids - List of IDs of parent elements.
 * @param custom - Flag indicating whether the section is custom.
 * @param title - Optional title of the section.
 * @returns An object containing the custom flag, encoded target annotation, and decoded target annotation.
 */
function determineTargetAnnotationOfHeaderSections(ids, custom, title) {
    let targetAnnotation, targetAnnotationEncoded;
    // check if id is in `@com.sap.vocabularies.UI.v1.DataPoint#FlightPrice` format
    const isAnnotationBased = ids[2].split(`@${ux_specification_types_1.UIVOCABULARY}`)[1];
    if (!isAnnotationBased) {
        custom = true;
        targetAnnotationEncoded = ids[2];
        targetAnnotation = targetAnnotationEncoded && targetAnnotationEncoded.replace(/::/g, '/');
    }
    else {
        if (title && title.indexOf(ux_specification_types_1.FACETTITLEPREFIX) > -1) {
            targetAnnotation = targetAnnotationEncoded = title.split(ux_specification_types_1.FACETTITLEPREFIX)[1];
        }
        else if (ids[2]) {
            targetAnnotation = targetAnnotationEncoded = ids[2].split('@')[1];
        }
        else if (targetAnnotationEncoded) {
            targetAnnotation = targetAnnotationEncoded = targetAnnotationEncoded.replace(/::/g, '/');
        }
    }
    return { custom, targetAnnotationEncoded, targetAnnotation };
}
/**
 * Determines the target annotation of sections based on the provided IDs and custom flag.
 *
 * @param ids - List of IDs of parent elements.
 * @param custom - Flag indicating whether the section is custom.
 * @returns An object containing the custom flag, encoded target annotation, and decoded target annotation.
 */
function determineTargetAnnotationOfSections(ids, custom) {
    const isAnnotationBased = ids[1].split(`@${ux_specification_types_1.UIVOCABULARY}`)[1];
    let targetAnnotation, targetAnnotationEncoded;
    if (ids[0] === 'sections' && !isAnnotationBased) {
        custom = true;
        targetAnnotationEncoded = ids[1];
        targetAnnotation = targetAnnotationEncoded && targetAnnotationEncoded.replace(/::/g, '/');
    }
    else {
        const sectionId = (0, utils_2.extractLastIdPart)(ids[1]);
        targetAnnotationEncoded = sectionId && sectionId.replace(/\//g, '::');
        targetAnnotation = targetAnnotationEncoded && targetAnnotationEncoded.replace(/::/g, '/');
    }
    return { custom, targetAnnotationEncoded, targetAnnotation };
}
/**
 * Determines the target annotation for a header section field group.
 *
 * @param ids - List of IDs of parent elements.
 * @returns An object containing encoded target annotation, and decoded target annotation.
 */
function determineTargetAnnotationOfHeaderSectionFieldsGroups(ids) {
    const sectionId = (0, utils_2.extractLastIdPart)(ids[2] ?? '');
    return { targetAnnotationEncoded: sectionId.replace(/\//g, '::'), targetAnnotation: sectionId };
}
/**
 * Determines the target annotation that is relevant for the sync rule.
 *
 * @param ids - list of ids of parent elements
 * @param transferParameters - list of parameters passed from API
 * @param configObject - the given section of config.json, to be exported
 * @returns an object containing the encoded target annotation, decoded target annotation, and custom flag
 */
function determineTargetAnnotation(ids, transferParameters, configObject) {
    let targetAnnotation, targetAnnotationEncoded, custom = false;
    if (ids[0] === ux_specification_types_1.PropertyName.table && ids[1] !== ux_specification_types_1.PropertyName.views) {
        ({ targetAnnotationEncoded, targetAnnotation } = determineTargetAnnotationOfTable(ids, transferParameters, configObject));
    }
    else if (ids[0] === ux_specification_types_1.PropertyName.chart && transferParameters.templateType === ux_specification_types_1.TemplateType.AnalyticalListPageV4) {
        ({ targetAnnotationEncoded, targetAnnotation } = determineTargetAnnotationOfChart(ids, transferParameters, configObject));
    }
    else if (ids[0] === ux_specification_types_1.PropertyName.sections) {
        ({ custom, targetAnnotationEncoded, targetAnnotation } = determineTargetAnnotationOfSections(ids, custom));
    }
    else if (ids[1] === ux_specification_types_1.PropertyName.sections && ids[4] === 'fields') {
        ({ targetAnnotationEncoded, targetAnnotation } = determineTargetAnnotationOfHeaderSectionFieldsGroups(ids));
    }
    else if (ids[1] === ux_specification_types_1.PropertyName.sections) {
        //header sections
        ({ custom, targetAnnotationEncoded, targetAnnotation } = determineTargetAnnotationOfHeaderSections(ids, custom, transferParameters.title));
    }
    else if (ids[1] === ux_specification_types_1.PropertyName.views) {
        ({ targetAnnotation, targetAnnotationEncoded } = determineTargetAnnotationOfView(transferParameters, ids));
    }
    return { targetAnnotationEncoded, targetAnnotation, custom };
}
/**
 * Checks whether the given element is part of an action group based on the presence of a specific data field.
 *
 * @param {string} element - The string representing the element to be checked.
 * @returns {boolean} - Returns true if the element contains the data field indicating it belongs to an action group, otherwise false.
 */
function isGroupingElement(element) {
    return element.includes(utils_3.DATA_FIELD_FOR_ACTION_GROUP);
}
/**
 * Calculates an additional key for the sync rule from the given ID's.
 *
 * @param {string[]} ids - list of ids of parent elements
 * @returns the key for the sync rule
 */
function determineKeyForSyncRule(ids) {
    let idIndex = 0;
    for (let index = ids.length - 1; index > -1; index--) {
        const element = ids[index];
        if ([
            ux_specification_types_1.PropertyName.actions,
            ux_specification_types_1.PropertyName.columns,
            ux_specification_types_1.PropertyName.views,
            ux_specification_types_1.PropertyName.visualFilters,
            ux_specification_types_1.PropertyName.selectionFields,
            ux_specification_types_1.PropertyName.fields
        ].includes(ux_specification_types_1.PropertyName[element]) ||
            isGroupingElement(element)) {
            idIndex = index + 1;
            break;
        }
    }
    return idIndex > 0 ? ids[idIndex]?.replace(/\//gi, '??') : undefined;
}
/**
 * Creates a new object in manifest.
 *
 * @param {ExportToManifestParams} params - list of parameters
 */
function createNewObjectInManifest(params) {
    if (params.syncRule.manifest.export && typeof params.syncRule.manifest.export === 'function') {
        params.syncRule.manifest.export(params.manifestSection, params.configObject, params.configPropertyKey, params.manifestKey, params.syncRuleKey);
    }
    else if (!params.manifestSection[params.manifestKey]) {
        params.manifestSection[params.manifestKey] = {};
    }
}
/**
 * Transfers a property value from config to manifest.
 *
 * @param {ExportToManifestParams} params - list of parameters
 * @param propertyDefinition - schema definition of a particular property
 * @param nextDefinition - schema definition referred by $ref
 * @returns a boolean indicator if any change happened to the manifest
 */
function transferPropertyToManifest(params, propertyDefinition, nextDefinition) {
    let changed = false;
    if (!(propertyDefinition['type'] !== 'array' &&
        nextDefinition?.['targetDefinition']?.type === 'object' &&
        nextDefinition?.['targetDefinition']?.properties)) {
        if (params.syncRule.manifest.export !== false) {
            if (params.syncRule.manifest.export && typeof params.syncRule.manifest.export === 'function') {
                params.syncRule.manifest.export(params.manifestSection, params.configObject, params.configPropertyKey, params.manifestKey, params.syncRuleKey);
            }
            else if ((Array.isArray(params.configObject[params.configPropertyKey]) &&
                !params.configObject[params.configPropertyKey].length) ||
                (!Array.isArray(params.configObject[params.configPropertyKey]) &&
                    params.configObject[params.configPropertyKey] === undefined)) {
                delete params.manifestSection[params.manifestKey];
            }
            else {
                params.manifestSection[params.manifestKey] = params.configObject[params.configPropertyKey];
            }
            changed = true;
        }
    }
    else if (propertyDefinition['type'] !== 'array' && params.manifestSection[params.manifestKey] === undefined) {
        createNewObjectInManifest(params);
        changed = true;
    }
    return changed;
}
/**
 * Deletes a setting from manifest.
 *
 * @param {string[]} ids - list of parent & child ids
 * @param {ExportToManifestParams} params - special transfer structure for the export to manifest
 * @param {TransferParameterTypeV4} transferParameters - general transfer structure of the export
 * @param {object} manifestSection - the given section in manifest
 * @param {string} path - the xpath expression for the manifest section
 * @param {string} targetAnnotation - The decoded target annotation.
 * @param {string} targetAnnotationEncoded - The encoded target annotation.
 * @returns a boolean indicator if any change happened to the manifest
 */
function deleteFromManifest(ids, params, transferParameters, manifestSection, path, targetAnnotation, targetAnnotationEncoded) {
    let changed = false;
    //special condition to prevent table export overwriting views' export and vice versa
    //(but deletion must be possible for the non-remaining table configuration)
    const multiViewDefinitions = transferParameters.appSchema.definitions.MultiTableModeV4;
    const parentDefinition = multiViewDefinitions?.properties[ids[2]];
    const isViewsDefined = isMultiViewsEnabled(transferParameters.config);
    if (ids[0] !== 'table' ||
        (ids[1] !== 'views' && !isViewsDefined) ||
        (ids[1] === 'views' &&
            (isViewsDefined ||
                (!transferParameters.config['defaultTemplateAnnotationPath'] &&
                    ids.length === 4 &&
                    viewProperties.indexOf(params.configPropertyKey) > -1) ||
                (transferParameters.config['defaultTemplateAnnotationPath'] &&
                    parentDefinition?.['annotationPath'] &&
                    !parentDefinition['annotationPath'].endsWith(transferParameters.config['defaultTemplateAnnotationPath'])) ||
                (ids[1] === 'views' && ids.length === 4 && doTransfer.indexOf(params.configPropertyKey) > -1)))) {
        if (params.syncRule.manifest.delete) {
            const deleteHandler = params.syncRule.manifest.delete;
            if (typeof deleteHandler === 'function') {
                deleteHandler(manifestSection, params.configObject, params.configPropertyKey, params.manifestKey, params.syncRuleKey, transferParameters.config);
                changed = true;
            }
        }
        else if (manifestSection[params.manifestKey] !== undefined) {
            delete manifestSection[params.manifestKey];
            changed = true;
        }
    }
    (0, utils_1.deleteEmptyStructure)(transferParameters.exportResults.manifest, path, transferParameters.deletionRequest, transferParameters.entityPathArray, targetAnnotation, targetAnnotationEncoded);
    return changed;
}
/**
 * Transfers a change in config.json to manifest.
 *
 * @param ids - list of parent & child ids
 * @param syncRule - syncRule (metadata)
 * @param parameters - list of parameters passed from API
 * @param nextDefinition - schema definition referred by $ref
 * @returns a boolean indicator if any change happened to the manifest
 */
function exportManifestChange(ids, syncRule, parameters, nextDefinition) {
    let changed = false;
    const originalIds = [...ids];
    //eliminate subsections' hierarchy
    let index = ids.indexOf('subsections');
    while (index > -1) {
        ids.splice(index - 1, 2);
        index = ids.indexOf('subsections');
    }
    const { targetAnnotationEncoded, targetAnnotation, custom } = determineTargetAnnotation(ids, parameters.transferParameters, parameters.configObject);
    const syncRuleKey = determineKeyForSyncRule(ids);
    const path = syncRule.manifest.path(parameters.transferParameters.pageNameArray, parameters.transferParameters.exportResults.manifest, targetAnnotationEncoded, syncRuleKey, targetAnnotation, parameters.configObject, originalIds);
    const manifestSection = path
        ? (0, common_1.getManifestSectionByPathV4)(parameters.transferParameters.exportResults.manifest, path, targetAnnotation, custom ? undefined : targetAnnotationEncoded)
        : undefined;
    if (manifestSection) {
        const exportParams = {
            syncRule,
            manifestSection,
            configObject: parameters.configObject,
            configPropertyKey: parameters.key,
            manifestKey: syncRule.manifest.key || parameters.key,
            syncRuleKey
        };
        if (parameters.configObject[parameters.key] === undefined ||
            (parameters.transferParameters.deletionRequest &&
                (0, common_1.arrayIncludes)([...parameters.breadcrumbs, parameters.key], parameters.transferParameters.entityPathArray))) {
            changed = deleteFromManifest(ids, exportParams, parameters.transferParameters, manifestSection, path, targetAnnotation, targetAnnotationEncoded);
        }
        else {
            changed = transferPropertyToManifest(exportParams, parameters.propertyDefinition, nextDefinition);
        }
    }
    return changed;
}
/**
 * Evaluates an export rule for a property or object, transfers to flex change or manifest setting.
 *
 * @param {TransferEntryParameters} parameters - list of parameters
 * @param ids - list of parent & child ids
 * @param nextDefinition - schema definition referred by $ref
 */
function evaluateExportRule(parameters, ids, nextDefinition) {
    const syncRule = (0, common_1.getReflectMetadata)(parameters.configObject, parameters.key);
    if (syncRule) {
        if (syncRule.xml) {
            const templateName = syncRule.xml(parameters.configObject);
            (0, fpmCustomPage_1.exportXMLFragmentConfig)(parameters.transferParameters, templateName, nextDefinition['configObject']);
        }
        else if (syncRule.manifest) {
            const changed = exportManifestChange(ids, syncRule, parameters, nextDefinition);
            if (changed) {
                parameters.transferParameters.exportResults.manifestChangeIndicator = ux_specification_types_1.ChangeIndicator.Updated;
            }
        }
    }
}
/**
 * Processes an (object-like) sub-object of the app schema and transfer all connected config entries to manifest.
 *
 * @param {TransferEntryParameters} parameters - list of parameters
 * @param {NextDefinition} nextDefinition - the next definition in app schema to be processed
 * @param currentConfigObject - current config object
 * @param ids - list of parent & child ID's
 * @param localIdParts - list of ID parts for building the stable ID
 */
function transferSubObject(parameters, nextDefinition, currentConfigObject, ids, localIdParts) {
    let objects = [];
    if (parameters.propertyDefinition.$ref && nextDefinition) {
        objects.push(nextDefinition.configObject);
    }
    else if (parameters.propertyDefinition['anyOf']) {
        objects.push(currentConfigObject);
    }
    else if (parameters.propertyDefinition.type === 'array') {
        objects = currentConfigObject;
    }
    else {
        objects.push(currentConfigObject);
    }
    parameters.index = 0;
    for (const innerObject of objects) {
        parameters.transferParameters.title = nextDefinition?.title;
        transferManifestEntriesAndFlexChange(parameters.transferParameters, innerObject, [...ids], localIdParts, [...parameters.breadcrumbs, parameters.key], nextDefinition?.targetDefinition);
        parameters.index++;
    }
}
/**
 * Creates an ALP 'views' entry in the resulting manifest if not yet existing.
 * Adds the secondary = table view if missing.
 *
 * @param {TransferParameterTypeV4} transferParameters - list of parameters passed from API
 * @param configObject - current object in config.json
 * @param key - The key representing the property name or identifier.
 * @returns A boolean indicating whether the views entry got created or if processing should stop.
 * - a boolean indicating that the views entry got created.
 * - a boolean indicating if the processing of sub-objects shall be stopped
 */
function createALPTableView(transferParameters, configObject, key) {
    let newElementCreated = false;
    if (!configObject[key] || Object.keys(configObject[key]).length === 0 || !configObject.chart) {
        return { newElementCreated, stopProcessing: true };
    }
    const path = (0, application_1.getSettingsPathForPage)(transferParameters.pageNameArray) + `/views`;
    const views = (0, common_1.getManifestSectionByPathV4)(transferParameters.exportResults.manifest, path);
    if (!views['paths'] || Object.keys(views['paths']).length === 0) {
        views['paths'] = [
            {
                secondary: [
                    {
                        annotationPath: configObject[key].annotationPath
                    }
                ]
            }
        ];
        newElementCreated = true;
    }
    else if (!views['paths'][0].secondary) {
        views['paths'][0].secondary = [
            {
                annotationPath: configObject[key].annotationPath
            }
        ];
        newElementCreated = true;
    }
    return { newElementCreated, stopProcessing: false };
}
/**
 * Creates an ALP 'views' entry in the resulting manifest if not yet existing.
 * Adds the primary = chart view if missing.
 *
 * @param {TransferEntryParameters} parameters - list of parameters
 * @returns A boolean indicating whether the views entry got created or if processing should stop.
 * - a boolean indicating that the views entry got created.
 * - a boolean indicating if the processing of sub-objects shall be stopped
 */
function createALPChartView(parameters) {
    let newElementCreated = false;
    const key = parameters.key;
    if (!parameters.configObject[key] || Object.keys(parameters.configObject[key]).length === 0) {
        return { newElementCreated, stopProcessing: true };
    }
    const path = (0, application_1.getSettingsPathForPage)(parameters.transferParameters.pageNameArray) + `/views`;
    const views = (0, common_1.getManifestSectionByPathV4)(parameters.transferParameters.exportResults.manifest, path);
    if (!views['paths'] || Object.keys(views['paths']).length === 0) {
        views['paths'] = [
            {
                primary: [
                    {
                        annotationPath: parameters.configObject[key].annotationPath
                    }
                ]
            }
        ];
        newElementCreated = true;
    }
    else if (!views['paths'][0].primary) {
        views['paths'][0].primary = [
            {
                annotationPath: parameters.configObject[key].annotationPath
            }
        ];
        newElementCreated = true;
    }
    if (!views['paths'][0].secondary) {
        views['paths'][0].secondary = [
            {
                annotationPath: parameters.configObject?.table?.annotationPath || "com.sap.vocabularies.UI.v1.LineItem" /* UIAnnotationTerms.LineItem */
            }
        ];
    }
    return { newElementCreated, stopProcessing: false };
}
/**
 * Creates an controlConfiguration with filterFields in the resulting manifest if not yet existing.
 * Adds the current visual filters if missing.
 *
 * @param {TransferParameterTypeV4} transferParameters - list of parameters passed from API
 * @param configObject - current object in config.json
 * @returns a boolean indicating that the filter fields entry got created.
 */
function createVisualFilters(transferParameters, configObject) {
    if (!configObject || Object.keys(configObject).length === 0) {
        return false;
    }
    let newElementCreated = false;
    const path = (0, application_1.getSettingsPathForPage)(transferParameters.pageNameArray);
    const settings = (0, common_1.getManifestSectionByPathV4)(transferParameters.exportResults.manifest, path);
    const selectionFields = `@${"com.sap.vocabularies.UI.v1.SelectionFields" /* UIAnnotationTerms.SelectionFields */}`;
    // No visual filters yet
    if (!settings['controlConfiguration']) {
        settings['controlConfiguration'] = { [selectionFields]: { filterFields: {} } };
        newElementCreated = true;
    }
    else if (!settings['controlConfiguration'][selectionFields]) {
        settings['controlConfiguration'][selectionFields] = { filterFields: {} };
        newElementCreated = true;
    }
    else if (!settings['controlConfiguration'][selectionFields]['filterFields']) {
        settings['controlConfiguration'][selectionFields]['filterFields'] = {};
        newElementCreated = true;
    }
    if (newElementCreated) {
        const filterFields = settings['controlConfiguration'][selectionFields]['filterFields'];
        Object.keys(configObject).forEach((filterName) => {
            const filterConfig = configObject[filterName];
            filterFields[filterName] = {};
            for (const property in filterConfig) {
                // Special handling for visualFilterValueList (split)
                if (property === 'visualFilterValueList') {
                    filterFields[filterName]['visualFilter'] = { valueList: filterConfig[property] };
                }
                else {
                    filterFields[filterName][property] = filterConfig[property];
                }
            }
        });
    }
    return newElementCreated;
}
/**
 * Creates an LR 'views' entry in the resulting manifest if not yet existing yet.
 * Adds the current table view if missing.
 *
 * @param index - The index of the current item in the list.
 * @param {TransferParameterTypeV4} transferParameters - list of parameters passed from API
 * @param key - The key representing the property name or identifier.
 * @param configObject - current object in config.json
 * @returns a boolean indicating that the views entry got created.
 */
function createLRTableView(index, transferParameters, key, configObject) {
    if (!configObject || Object.keys(configObject).length === 0) {
        return false;
    }
    let newElementCreated = false;
    const path = (0, application_1.getSettingsPathForPage)(transferParameters.pageNameArray) + `/views`;
    const views = (0, common_1.getManifestSectionByPathV4)(transferParameters.exportResults.manifest, path);
    const newEntry = {
        key: configObject['key'],
        annotationPath: configObject.annotationPath
    };
    if (!views['paths'] || Object.keys(views['paths']).length === 0) {
        //No views yet
        views['paths'] = [newEntry];
        newElementCreated = true;
    }
    else {
        const existingIndex = views['paths'].findIndex((view) => view.key === configObject['key'] || view.key === key);
        if (existingIndex === -1) {
            //Given view does not exist yet
            views['paths'].push(newEntry);
            newElementCreated = true;
        }
        else if (existingIndex !== configObject.index) {
            //Reordering scenario
            const savedContent = JSON.parse(JSON.stringify(views['paths'][existingIndex]));
            views['paths'].splice(existingIndex, 1);
            views['paths'].splice(index, 0, savedContent);
            newElementCreated = true;
        }
    }
    return newElementCreated;
}
/**
 * Special logic to create new elements in manifest.
 *
 * @param {TransferEntryParameters} parameters - list of parameters
 * @param {string} definitionName - schema definition name of the current object
 * @param {boolean} preprocessed - indicates that the element was already processed
 * @returns A boolean indicating whether the definition was processed.
 * - newElementCreated - indicates that a new element was created ==> no further sync rule on this level
 * - createNewElement - indicates that further processing shall be stopped
 */
function createNewElement(parameters, definitionName, preprocessed) {
    let newElementCreated = false;
    const stopProcessing = false;
    if (definitionName && !preprocessed && !parameters.transferParameters.deletionRequest) {
        if (definitionName.startsWith(ux_specification_types_1.DefinitionName.CustomColumn)) {
            newElementCreated = (0, exportCustomColumn_1.createNewCustomColumn)(parameters.key, parameters.transferParameters, parameters.configObject[parameters.key], parameters.transferParameters.logger, parameters.breadcrumbs);
        }
        else if (definitionName === ux_specification_types_1.DefinitionName.ALPChartView &&
            parameters.key === ux_specification_types_1.PropertyName.chart &&
            parameters.configObject) {
            return createALPChartView(parameters);
        }
        else if (definitionName === ux_specification_types_1.DefinitionName.ALPTableView &&
            parameters.key === ux_specification_types_1.PropertyName.table &&
            parameters.configObject) {
            return createALPTableView(parameters.transferParameters, parameters.configObject, parameters.key);
        }
        else if (parameters.key &&
            (definitionName.startsWith(ux_specification_types_1.DefinitionName.LRTableView) ||
                definitionName.startsWith(ux_specification_types_1.DefinitionName.LRChartView))) {
            newElementCreated = createLRTableView(parameters.index, parameters.transferParameters, parameters.key, parameters.configObject[parameters.key]);
        }
        else if (parameters.key && definitionName.startsWith(ux_specification_types_1.DefinitionName.VisualFilters)) {
            newElementCreated = createVisualFilters(parameters.transferParameters, parameters.configObject[parameters.key]);
        }
    }
    return { newElementCreated, stopProcessing };
}
/**
 * Function checks if passed schema definition is traversable.
 * It checks if definition has reference or object properties defined directly in definition.
 *
 * @param {Definition} definition Definition to check.
 * @returns True if passed schema definition is traversable.
 */
function isDefinitionTraversable(definition) {
    return (!!definition.$ref ||
        (typeof definition.properties === 'object' && Object.keys(definition.properties).length !== 0) ||
        !!definition.anyOf);
}
/**
 * Transfers one entry of a list (aka one property of the config) to the manifest.
 *
 * @param {TransferEntryParameters} parameters - list of parameters
 * @param {string[]} parentIds - array of collected parent ids
 * @param {string[]} stableIdParts - list of parts of the stable ID for flex changes
 * @param {boolean} preprocessed - indicates that the list was already preprocessed by following the config order
 */
function transferSortedListEntry(parameters, parentIds, stableIdParts, preprocessed = false) {
    const currentConfigObject = parameters.configObject[parameters.key];
    const childId = (0, common_1.getChildId)(parameters.configObject, parameters.key);
    const nextDefinition = (0, utils_1.getNextTargetDefinition)(parameters.transferParameters.appSchema, parameters.transferParameters.title, currentConfigObject, parameters.propertyDefinition, parameters.key, parameters.transferParameters.factory, parameters.transferParameters.pageType);
    const { newElementCreated, stopProcessing } = createNewElement(parameters, parameters.propertyDefinition.$ref?.split(common_1.DEFINITION_LINK_PREFIX)[1], preprocessed);
    const ids = childId ? [...parentIds, childId] : [...parentIds, parameters.key];
    if (!newElementCreated) {
        evaluateExportRule(parameters, ids, nextDefinition);
    }
    if (parameters.propertyDefinition &&
        ((typeof currentConfigObject === 'object' && Object.keys(currentConfigObject).length !== 0) ||
            (isDefinitionTraversable(parameters.propertyDefinition) && nextDefinition?.configObject)) &&
        !stopProcessing) {
        const localIdParts = childId ? [...stableIdParts, childId] : stableIdParts;
        transferSubObject(parameters, nextDefinition, currentConfigObject, ids, localIdParts);
    }
}
/**
 * Function for special handling of anyOf definitions during the export.
 *
 * @param transferEntryParameters - Parameters for transferring configuration entries with anyOf definitions.
 * @param anyOfDefinitions - the array of definitions from the anyOf (of additionalProperties)
 * @param schemaDefinition - parent definition, comprising properties as well as additionalProperties
 * @param parentIds - array of collected parent ids
 * @param stableIdParts - list of parts of the stable ID for flex changes
 * @returns A boolean indicating whether the definition was processed.
 */
function transferConfigEntriesWithAnyOfDefinitions(transferEntryParameters, anyOfDefinitions, schemaDefinition, parentIds, stableIdParts) {
    let preprocessed = false;
    if (anyOfDefinitions) {
        const configEntries = Object.entries(transferEntryParameters.configObject);
        if (!configEntries || configEntries.length < 1) {
            return preprocessed;
        }
        configEntries.sort((a, b) => a[1] && a[1]['index'] - b[1]['index']);
        //follow the config order, to support reordering
        configEntries.forEach((configEntry) => {
            if (schemaDefinition.properties?.[configEntry[0]]) {
                transferEntryParameters.key = configEntry[0];
                transferEntryParameters.propertyDefinition = schemaDefinition.properties[configEntry[0]];
                //already part of schema
                transferSortedListEntry(transferEntryParameters, parentIds, stableIdParts);
                preprocessed = true;
            }
            else if (typeof configEntry[1] === 'object') {
                //match against potential schema definitions
                anyOfDefinitions.forEach((anyOfDefinition) => {
                    if (anyOfDefinition.$ref) {
                        transferEntryParameters.key = configEntry[0];
                        transferEntryParameters.propertyDefinition = anyOfDefinition;
                        transferSortedListEntry(transferEntryParameters, parentIds, stableIdParts);
                        preprocessed = true;
                    }
                });
            }
            transferEntryParameters.index++;
        });
    }
    return preprocessed;
}
/**
 * Method updates list of definition entries from schema with entries for insertion based on `additionalProperties` entry.
 *
 * @param {TransferParameterTypeV4} transferParameters - list of parameters passed from API.
 * @param {Definition} additionalProperties - allowed additional properties.
 * @param {[string, DefinitionOrBoolean][]} list - list with entries.
 * @param {{ [key: string]: unknown }} configObject - configuration object.
 */
function mergeEntriesForInsert(transferParameters, additionalProperties, list, configObject) {
    const keys = (0, common_1.findMatchingKeysByAdditionalProperties)(transferParameters.appSchema, additionalProperties, configObject);
    if (keys) {
        if (!list) {
            list = [];
        }
        // Merge new entries for creation into list
        for (const key of keys) {
            // Check if entry from config does not exist in list - potential insert.
            if (!list.some((item) => item[0] === key)) {
                list.push([key, additionalProperties]);
            }
        }
    }
}
/**
 * Sorts the properties of a schema definition, considering the propertyIndex.
 *
 * @param properties - The properties of a schema definition to be sorted.
 * @returns a sorted list of properties, as object entries
 */
function sortProperties(properties) {
    let sortedList = properties && Object.entries(properties);
    if (sortedList?.[0]?.[1][ux_specification_types_1.SchemaTag.propertyIndex]) {
        sortedList = Object.entries(properties).sort((a, b) => a[1][ux_specification_types_1.SchemaTag.propertyIndex] - b[1][ux_specification_types_1.SchemaTag.propertyIndex]);
    }
    return sortedList;
}
/**
 *
 * @param additionalProperties - additionalProperties definition at the given schema definition
 * @param transferParameters - list of parameters passed from API
 * @param transferEntryParameters - Parameters for transferring configuration entries with anyOf definitions.
 * @param schemaDefinition - the current schema definition
 * @param parentIds - array of collected parent ids
 * @param stableIdParts - list of parts of the stable ID for flex changes
 * @returns an indicator showing if the defintion was processed.
 */
function checkIfAnyOfDefinition(additionalProperties, transferParameters, transferEntryParameters, schemaDefinition, parentIds, stableIdParts) {
    let preprocessed = false;
    if (additionalProperties) {
        let anyOfDefinitions = additionalProperties.anyOf;
        if (!anyOfDefinitions) {
            const referredDefinition = additionalProperties.$ref?.split(common_1.DEFINITION_LINK_PREFIX)[1];
            anyOfDefinitions = referredDefinition
                ? transferParameters.appSchema.definitions[referredDefinition]?.anyOf
                : undefined;
        }
        preprocessed = transferConfigEntriesWithAnyOfDefinitions(transferEntryParameters, anyOfDefinitions, schemaDefinition, parentIds, stableIdParts);
    }
    return preprocessed;
}
/**
 * Determines whether multi-view support is enabled based on the page table config.
 *
 * @param config Page configuration object.
 * @returns Returns true if multi-view is enabled, otherwise false.
 */
function isMultiViewsEnabled(config) {
    if (!config['table']?.views) {
        return false;
    }
    // Make sure views are populated - there could be setup without real views, but with atomic properties on views level
    const viewsProperties = config['table']?.views ?? {};
    for (const key in viewsProperties) {
        // Treat as multi-view if at least one property in viewsProperties is an object
        // Currently, the only possible object property is `view`
        if (typeof viewsProperties[key] === 'object') {
            return true;
        }
    }
}
/**
 * Recursive function that traverses the content of the current object. It reads decorators and fills exportResultManifest entries and flex changes.
 *
 * @param {TransferParameterTypeV4} transferParameters - list of parameters passed from API
 * @param {{ [key: string]: any }} configObject - current object to traverse
 * @param {string[]} parentIds - array of collected parent ids
 * @param {string[]} stableIdParts - list of parts of the stable ID for flex changes
 * @param {string[]} breadcrumbs - array of sequence of properties that we are currently processing. Required for instance for table column name
 * @param {Definition} schemaDefinition - the current entry point or definition in the app schema
 */
function transferManifestEntriesAndFlexChange(transferParameters, configObject, parentIds, stableIdParts, breadcrumbs, schemaDefinition) {
    if (!schemaDefinition?.properties && !schemaDefinition?.additionalProperties) {
        return;
    }
    let index = 0;
    //In case of additionalProperties = flexible array definition ==> new entries from the config shall be exported too
    const transferEntryParameters = {
        transferParameters,
        breadcrumbs,
        key: undefined,
        index,
        configObject,
        propertyDefinition: undefined
    };
    const additionalProperties = schemaDefinition.additionalProperties;
    const preprocessed = checkIfAnyOfDefinition(additionalProperties, transferParameters, transferEntryParameters, schemaDefinition, parentIds, stableIdParts);
    //Sort if there is a propertyIndex, so that the exported list ends up in manifest in the right order
    const sortedList = sortProperties(schemaDefinition.properties);
    if (additionalProperties?.$ref) {
        // Merge new entries for creation into list
        mergeEntriesForInsert(transferParameters, additionalProperties, sortedList, configObject);
    }
    index = 0;
    if (sortedList) {
        sortedList.forEach((element) => {
            if (transferParameters.entityPathArray) {
                if (transferParameters.entityPathArray[breadcrumbs.length] !== element[0] &&
                    !(0, common_1.arrayIncludes)(breadcrumbs, transferParameters.entityPathArray)) {
                    return;
                }
            }
            transferEntryParameters.key = element[0];
            transferEntryParameters.propertyDefinition = element[1];
            transferEntryParameters.index = index;
            transferSortedListEntry(transferEntryParameters, parentIds, stableIdParts, preprocessed);
            if (element[1].$ref) {
                index++;
            }
        });
    }
}
/**
 * Instantiates the export classes for a single object page section.
 *
 * @param objectPageConfig - the given object page configuration
 * @param sectionId - section identificator to add class
 * @param factory - The metadata factory used to create instances of export classes.
 */
function getClassesForSingleSection(objectPageConfig, sectionId, factory) {
    const customSections = objectPageConfig.sections;
    if (customSections && customSections[sectionId]) {
        customSections[sectionId] = factory.createInstance(ux_specification_types_1.PageTypeV2.ObjectPage, 'ObjectPageCustomSectionView', customSections[sectionId], customSections[sectionId]['className']);
    }
}
/**
 * Recursive sub-function for handling subsections.
 *
 * @param section - Object page section in config
 * @param factory - The metadata factory used to create instances of export classes.
 */
function getSubsectionClasses(section, factory) {
    if (section['subsections']) {
        let subSection;
        for (const subSectionId in section['subsections']) {
            subSection = section['subsections'][subSectionId];
            getClassesForSingleSection(subSection, subSectionId, factory);
            getSubsectionClasses(subSection, factory);
        }
    }
}
/**
 * Instantiates the export classes for the object page section.
 *
 * @param objectPageConfig - the given object page configuration
 * @param objectPage - the overall object page export class where the sections shall be included
 * @param factory - The metadata factory used to create instances of export classes.
 */
function getExportClassesForSections(objectPageConfig, objectPage, factory) {
    let section;
    for (const sectionId in objectPageConfig.sections) {
        section = objectPage.sections[sectionId];
        getClassesForSingleSection(objectPageConfig, sectionId, factory);
        getSubsectionClasses(section, factory);
    }
}
/**
 * Calculates the baseId.
 *
 * @param {v4.ListReportConfigV4 | v4.ObjectPageConfigV4} config - content of the config file
 * @param {ExportListReportV4Parameters | ExportObjectPageV4Parameters} exportParameters - all API parameters needed for the export
 * @returns the baseId for changes
 */
function calculateBaseId(config, exportParameters) {
    const pageLayoutInformation = (0, common_1.getPageLayoutInformation)(config);
    return `${exportParameters.appId}::${pageLayoutInformation.id}::${exportParameters.page.name}--`;
}
/**
 * Run through the given ObjectPage config and return respective exportResultManifest entry and flex changes.
 *
 * @param {ExportObjectPageV4Parameters} exportParameters - all API parameters needed for the export
 * @param {UI5Version} ui5Version - SAP UI5 version
 * @param {boolean} deletionRequest - if set to true, any manifest setting specified by entityPath gets deleted even if it comprises any unknown property
 * @returns ExportResults - The export result comprises the enhanced manifest as well as a list of flex changes.
 */
const exportObjectPage = (exportParameters, ui5Version, deletionRequest = false) => {
    const exportResults = prepareDefaultExportResult(exportParameters.manifest);
    const factory = new factory_1.MetadataInstanceFactoryV4();
    const pageInstance = factory.createPageInstance(ux_specification_types_1.PageTypeV4.ObjectPage, exportParameters.page.config);
    if (exportParameters.page.config?.[ux_specification_types_1.PropertyName.sections]) {
        getExportClassesForSections(exportParameters.page.config, pageInstance, factory);
    }
    const transferParameters = {
        exportResults,
        appId: exportParameters.appId,
        appRootPath: exportParameters.appRootPath,
        baseId: calculateBaseId(pageInstance, exportParameters),
        appSchema: exportParameters.jsonSchema,
        config: pageInstance,
        ui5Version,
        factory,
        pageNameArray: [exportParameters.page.name],
        pageType: ux_specification_types_1.PageTypeV4.ObjectPage,
        title: undefined,
        logger: exportParameters.logger,
        templateType: ux_specification_types_1.TemplateType.ListReportObjectPageV4,
        fragments: exportParameters['fragments'],
        entityPathArray: exportParameters['entityPath'],
        deletionRequest
    };
    transferManifestEntriesAndFlexChange(transferParameters, pageInstance, [], //no parentIds
    [], // no stableIds
    [], // no breadcrumbs
    exportParameters.jsonSchema //starting point for target definition
    );
    return exportResults;
};
/**
 * Run through the given List Page (LR or ALP) config and return respective exportResultManifest entry and flex changes.
 *
 * @param {ExportListReportV4Parameters} exportParameters - all API parameters needed for the export
 * @param {Factory} factory - metadata factory
 * @param {v4.ListReportConfigV4} config - content of the config file, filled into a factory class for metadata access
 * @param {PageTypeV4} pageType - Page type (enum value)
 * @param {UI5Version} ui5Version - SAP UI5 version
 * @param {TemplateType} templateType - Template type (enum value)
 * @param {boolean} deletionRequest - if set to true, any manifest setting specified by entityPath gets deleted even if it comprises any unknown property
 * @returns ExportResults - The export result comprises the enhanced manifest as well as a list of flex changes.
 */
const exportListPage = (exportParameters, factory, config, pageType, ui5Version, templateType, deletionRequest = false) => {
    const exportResults = prepareDefaultExportResult(exportParameters.manifest);
    const appSchema = exportParameters.jsonSchema;
    const definitionName = config.table?.[ux_specification_types_1.PropertyName.annotationPath]
        ? ux_specification_types_1.DefinitionName.ALPTableView
        : ux_specification_types_1.DefinitionName.Table;
    const tableDefinition = (config.table = factory.createInstance(pageType, definitionName, config.table));
    let tableDefinitionName = ux_specification_types_1.DefinitionName.LineItems;
    if (appSchema.definitions[ux_specification_types_1.DefinitionName.TableSPV]) {
        const tableRef = appSchema.definitions[ux_specification_types_1.DefinitionName.TableSPV].properties.columns['$ref'];
        tableDefinitionName = tableRef.split(common_1.DEFINITION_LINK_PREFIX)[1];
    }
    const appSchemaActions = appSchema.definitions[`${ux_specification_types_1.DefinitionName.Actions}<${tableDefinitionName}>`];
    if (tableDefinition.toolBar?.actions && appSchemaActions) {
        for (const actionKey in appSchemaActions.properties) {
            tableDefinition.toolBar.actions[actionKey] = factory.createInstance(pageType, ux_specification_types_1.DefinitionName.ToolBarAction, tableDefinition.toolBar.actions[actionKey]);
        }
    }
    const transferParameters = {
        exportResults,
        appId: exportParameters.appId,
        appRootPath: exportParameters.appRootPath,
        baseId: calculateBaseId(config, exportParameters),
        appSchema: exportParameters.jsonSchema,
        config,
        ui5Version,
        factory,
        pageNameArray: [exportParameters.page.name],
        pageType: ux_specification_types_1.PageTypeV4.ObjectPage,
        title: undefined,
        logger: exportParameters.logger,
        templateType,
        fragments: exportParameters['fragments'],
        entityPathArray: exportParameters['entityPath'],
        deletionRequest
    };
    transferManifestEntriesAndFlexChange(transferParameters, config, [], //no parentIds
    [], // no stableIds
    [], // no breadcrumbs
    appSchema //starting point for target definition
    );
    return exportResults;
};
/**
 * Run through the given ListReport config and return respective exportResultManifest entry and flex changes.
 *
 * @param {ExportListReportV4Parameters} exportParameters - all API parameters needed for the export
 * @param {UI5Version} ui5Version - SAP UI5 version
 * @param {TemplateType} templateType - Template type (enum value)
 * @param {boolean} deletionRequest - if set to true, any manifest setting specified by entityPath gets deleted even if it comprises any unknown property
 * @returns ExportResults - The export result comprises the enhanced manifest as well as a list of flex changes.
 */
const exportListReportPage = (exportParameters, ui5Version, templateType, deletionRequest = false) => {
    const factory = new factory_1.MetadataInstanceFactoryV4();
    const listReport = factory.createPageInstance(ux_specification_types_1.PageTypeV2.ListReport, exportParameters.page.config);
    return exportListPage(exportParameters, factory, listReport, ux_specification_types_1.PageTypeV4.ListReport, ui5Version, templateType, deletionRequest);
};
/**
 * Run through the given Analytical List Page config and return respective exportResultManifest entry and flex changes.
 *
 * @param {ExportAnalyticalListPageV4Parameters} exportParameters - all API parameters needed for the export
 * @param {UI5Version} ui5Version - SAP UI5 version
 * @param {boolean} deletionRequest - if set to true, any manifest setting specified by entityPath gets deleted even if it comprises any unknown property
 * @returns ExportResults - The export result comprises the enhanced manifest as well as a list of flex changes.
 */
const exportAnalyticalListPage = (exportParameters, ui5Version, deletionRequest = false) => {
    return exportListReportPage(exportParameters, ui5Version, ux_specification_types_1.TemplateType.AnalyticalListPageV4, deletionRequest);
};
/**
 * Method applies passed export rule for passed property and path.
 *
 * @param rule - sync rule to apply
 * @param path - path in manifest which shold be updated by export
 * @param key - property name/key
 * @param application - V4 application (app.json config file)
 * @param manifest - Manifest of the given app
 * @param appSchema - App schema
 */
const applyExportRule = (rule, path, key, application, manifest, appSchema) => {
    let manifestSection = (0, common_1.getManifestSectionByPathV4)(manifest, path, undefined, undefined, true);
    let existing = true;
    if (manifestSection === undefined) {
        existing = false;
        manifestSection = (0, common_1.prepareValueForPath)();
    }
    const exportParams = {
        syncRule: rule,
        manifestSection,
        configObject: application,
        configPropertyKey: key,
        manifestKey: key,
        syncRuleKey: key
    };
    const propertyDefinition = appSchema.definitions.AppSettings.properties[key];
    transferPropertyToManifest(exportParams, propertyDefinition, {});
    if (!existing && Object.keys(manifestSection).length) {
        // Apply virtual object to existing manifest
        (0, common_1.setManifestSectionByPathV4)(manifest, path, manifestSection);
    }
};
/**
 * API for the export of a V4 app configuration.
 * Export means the transfer of the properties and values of the given config to manifest entries or flex changes.
 *
 * @param application - V4 application (app.json config file)
 * @param manifest - Manifest of the given app
 * @param appSchema - App schema
 * @returns ExportResults = object comprising the updated manifest and a list of flex changes
 */
const exportApplicationV4 = (application, manifest, appSchema) => {
    const exportResults = prepareDefaultExportResult(manifest);
    const applicationV4 = Object.assign(new application_1.ApplicationV4(), application);
    //transform application settings
    applicationV4.settings = Object.assign(new application_1.AppSettings(), applicationV4.settings);
    for (const key in appSchema.definitions.AppSettings.properties) {
        const syncRule = (0, common_1.getReflectMetadata)(applicationV4.settings, key);
        if (syncRule && syncRule.manifest) {
            const path = syncRule.manifest.path();
            if (syncRule.manifest.export) {
                applyExportRule(syncRule, path, key, application, exportResults.manifest, appSchema);
            }
            else {
                // Read section/segment with 'readOnly=true'
                let manifestSection = (0, common_1.getManifestSectionByPathV4)(exportResults.manifest, path, undefined, undefined, true);
                const settingProperty = application.settings?.[key];
                // Property could be empty object or undefined, check for both
                if (settingProperty !== undefined &&
                    (typeof settingProperty !== 'object' || Object.keys(settingProperty).length !== 0)) {
                    // Read section/segment with 'readOnly=false'(create object/array if existing value not found by given path)
                    manifestSection =
                        manifestSection ??
                            (0, common_1.getManifestSectionByPathV4)(exportResults.manifest, path, undefined, undefined, false);
                    manifestSection[key] = application.settings[key];
                }
                else if (manifestSection && key in manifestSection) {
                    delete manifestSection[key];
                    (0, utils_1.deleteEmptyStructure)(exportResults.manifest, path, false);
                }
            }
        }
    }
    //transform routing
    exportResults.manifest[ux_specification_types_1.ManifestSection.ui5].routing = (0, manifest_1.transformRoutingV4)(application['home'], application['pages'], exportResults.manifest);
    // Update FCL data
    (0, flexibleColumnLayout_1.updateFcl)(exportResults.manifest[ux_specification_types_1.ManifestSection.ui5], application);
    return exportResults;
};
/**
 * General API for the export of a V4 config page.
 * Export means the transfer of the properties and values of the given config to manifest entries or flex changes.
 *
 * @param exportParametersV4 - list of parameters for the export; the list varies for the different schema types
 * @param ui5Version - the minUi5Version of the app
 * @returns ExportResults = object comprising the updated manifest and a list of flex changes
 */
const exportPageV4 = (exportParametersV4, ui5Version) => {
    if (exportParametersV4[ux_specification_types_1.SchemaType.ListReport]) {
        return exportListReportPage(exportParametersV4[ux_specification_types_1.SchemaType.ListReport], ui5Version, ux_specification_types_1.TemplateType.ListReportObjectPageV4);
    }
    else if (exportParametersV4[ux_specification_types_1.SchemaType.ObjectPage]) {
        return exportObjectPage(exportParametersV4[ux_specification_types_1.SchemaType.ObjectPage], ui5Version);
    }
    else if (exportParametersV4[ux_specification_types_1.SchemaType.Application]) {
        const { application, manifest, jsonSchema } = exportParametersV4[ux_specification_types_1.SchemaType.Application];
        return exportApplicationV4(application, manifest, jsonSchema);
    }
    else if (exportParametersV4[ux_specification_types_1.SchemaType.AnalyticalListPage]) {
        return exportAnalyticalListPage(exportParametersV4[ux_specification_types_1.SchemaType.AnalyticalListPage], ui5Version);
    }
    else if (exportParametersV4[ux_specification_types_1.SchemaType.FPMCustomPage]) {
        return (0, fpmCustomPage_1.exportFPMCustomPageConfig)(exportParametersV4[ux_specification_types_1.SchemaType.FPMCustomPage], ux_specification_types_1.defaultExportResult);
    }
};
exports.exportPageV4 = exportPageV4;
/**
 * Run through the given CustomPage config and return respective export result.
 *
 * @param exportParameters - all API parameters needed for the export
 * @returns Export result.
 */
const exportCustomPage = (exportParameters) => {
    const { jsonSchema, views, page, manifest, logger } = exportParameters;
    const exportResults = prepareDefaultExportResult(exportParameters.manifest);
    let viewData = undefined;
    if (jsonSchema && Object.keys(jsonSchema).length > 0) {
        viewData = (0, utils_3.getPageCustomViewFile)(views || [], page.name, manifest, exportParameters.logger);
    }
    if (viewData) {
        (0, fpmCustomPage_1.getExtensionExportResult)(viewData, exportResults, page.config, jsonSchema, 'view', logger);
    }
    return exportResults;
};
/**
 * Exports the config change of a single entity of the config, as indicated by an entityPath.
 * Export means the transfer of the properties and values of the given config to manifest entries or flex changes.
 *
 * @param exportParametersV4 - list of parameters for the export; the list varies for the different schema types
 * @param ui5Version - the minUi5Version of the app
 * @param deletionRequest - if set to true, any manifest setting specified by entityPath gets deleted even if it comprises any unknown property
 * @returns ExportResults = object comprising the updated manifest and a list of flex changes
 */
const exportConfigEntityByPathV4 = (exportParametersV4, ui5Version, deletionRequest = false) => {
    if (exportParametersV4[ux_specification_types_1.SchemaType.ListReport]) {
        return exportListReportPage(exportParametersV4[ux_specification_types_1.SchemaType.ListReport], ui5Version, ux_specification_types_1.TemplateType.ListReportObjectPageV4, deletionRequest);
    }
    else if (exportParametersV4[ux_specification_types_1.SchemaType.ObjectPage]) {
        return exportObjectPage(exportParametersV4[ux_specification_types_1.SchemaType.ObjectPage], ui5Version, deletionRequest);
    }
    else if (exportParametersV4[ux_specification_types_1.SchemaType.AnalyticalListPage]) {
        return exportAnalyticalListPage(exportParametersV4[ux_specification_types_1.SchemaType.AnalyticalListPage], ui5Version, deletionRequest);
    }
    else if (exportParametersV4[ux_specification_types_1.SchemaType.FPMCustomPage]) {
        return exportCustomPage(exportParametersV4[ux_specification_types_1.SchemaType.FPMCustomPage]);
    }
};
exports.exportConfigEntityByPathV4 = exportConfigEntityByPathV4;
//# sourceMappingURL=export.js.map