"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var core_1 = require("@jsonforms/core");
var JsonRefs = require("json-refs");
var isObject = function (schema) {
    return schema.properties !== undefined;
};
var isArray = function (schema) {
    return schema.items !== undefined;
};
/**
 * Returns the label of a property based on the best-effort solution
 * @param property The property contains a schema, label and a property as key
 * @returns {string}
 */
exports.findPropertyLabel = function (property) {
    return _.find([
        property.schema.title,
        property.label,
        property.property
    ], function (n) { return !_.isEmpty(n); });
};
/**
 * Finding required definitions from parentSchema by using schema refs
 *
 * @param parentSchema The root schema
 * @param allRefs All the refs of root schema
 * @param schemaRefs The current schema refs
 * @param extractedReferences Contains the definition attributes for the current schema
 * @returns SchemaRefs all the required reference paths for subschema
 */
var findReferences = function (parentSchema, allRefs, schemaRefs, extractedReferences) {
    return _.reduce(schemaRefs, function (prev, schemaRefValue) {
        var refs = _.pickBy(prev, _.flip(function (key) { return _.startsWith(key, schemaRefValue.uri); }));
        if (extractedReferences[schemaRefValue.uri]) {
            refs = undefined;
        }
        if (!extractedReferences[schemaRefValue.uri]) {
            extractedReferences[schemaRefValue.uri] = schemaRefValue.uri;
            prev = _.omitBy(prev, function (value) { return value.uri === schemaRefValue.uri; });
        }
        if (refs !== undefined) {
            findReferences(parentSchema, prev, refs, extractedReferences);
        }
        return prev;
    }, allRefs);
};
/**
 * Calculate references for a given schema and copy definitions into the schema
 *
 * @param parentSchema root schema which is used to find all the schema refs
 * @param schema current subschema without resolved references
 * @returns JsonSchema current subschema with resolved references
 */
exports.makeSchemaSelfContained = function (parentSchema, schema) {
    var schemaRefs = JsonRefs.findRefs(schema, { resolveCirculars: true });
    var allRefs = JsonRefs.findRefs(parentSchema, { resolveCirculars: true });
    var extractedReferences;
    findReferences(parentSchema, allRefs, schemaRefs, extractedReferences = {});
    var refList = _.values(extractedReferences);
    if (!_.isEmpty(refList)) {
        _.each(refList, function (ref) {
            var _a, _b, _c;
            var propertyKey = ref.substring((ref.indexOf('/') + 1), (ref.lastIndexOf('/')));
            var property = ref.substring((ref.lastIndexOf('/') + 1));
            if (_.has(parentSchema, propertyKey) &&
                _.has(parentSchema, propertyKey + "." + property)) {
                if (_.get(schema, propertyKey)) {
                    _.set(schema, propertyKey, __assign({}, _.get(schema, propertyKey), (_a = {},
                        _a[property] = _.get(parentSchema, [propertyKey, property]),
                        _a)));
                }
                else {
                    schema = __assign({}, schema, (_b = {},
                        _b[propertyKey] = (_c = {},
                            _c[property] = _.get(parentSchema, [propertyKey, property]),
                            _c),
                        _b));
                }
            }
        });
    }
    return schema;
};
/**
 * Create a self contained schema.
 * @param parentSchema The schema to use for resolving
 * @param refPath The path to resolve
 * @return a JsonSchema that is self-contained
 */
var resolveAndMakeSchemaSelfContained = function (parentSchema, refPath) {
    var schema = core_1.resolveSchema(parentSchema, refPath);
    return __assign({}, schema, exports.makeSchemaSelfContained(parentSchema, schema));
};
/**
 * Returns a flattened list of container properties.
 * A property is being considered a container property if it is an array of objects,
 * meaning that all its children are non-primitive.
 *
 * @param {string} property The schema key from which this property was created.
 * @param {string} label The name of the schema the property describes.
 * @param {JsonSchema} schema The schema is the JsonSchema this property describes.
 * @param {JsonSchema} rootSchema The parent schema
 * @param {boolean} isInContainer To indicate whether the schema is in a container or not
 *                                Properties that are described in array are considered as
 *                                being in a container.
 * @returns {@link Property[]} An array of properties where each property describes
 *                             a self-contained schema for the corresponding schema
 */
var findContainerProps = function (property, label, schemaPath, schema, rootSchema, isInContainer, visited, recurse) {
    if (schema.$ref !== undefined) {
        if (visited.indexOf(schema.$ref) !== -1) {
            return [];
        }
        return findContainerProps(property, schema.$ref === '#' ?
            undefined :
            schema.$ref.substring(schema.$ref.lastIndexOf('/') + 1), schemaPath, resolveAndMakeSchemaSelfContained(rootSchema, schema.$ref), rootSchema, isInContainer, visited.slice().concat(schema.$ref), recurse);
    }
    else if (isObject(schema)) {
        var props = [];
        if (isInContainer) {
            var prop = {
                property: property,
                label: label,
                schema: schema,
                schemaPath: schemaPath
            };
            props.push(prop);
            if (!recurse) {
                return props;
            }
        }
        return Object.keys(schema.properties)
            .reduce(function (prev, currentProp) {
            return prev.concat(findContainerProps(currentProp, currentProp, (schemaPath.length > 0 ? schemaPath + '.' : '') + "properties." + currentProp, schema.properties[currentProp], rootSchema, false, visited, recurse));
        }, props);
    }
    else if (isArray(schema) && !Array.isArray(schema.items)) {
        return findContainerProps(property, label, schemaPath + ".items", schema.items, rootSchema, true, visited, recurse);
    }
    else if (schema.anyOf !== undefined) {
        // TODO: oneOf?
        var init = [];
        return _.reduce(schema.anyOf, function (prev, anyOfSubSchema, index) {
            return prev.concat(findContainerProps(property, label, schemaPath + ".anyOf." + index, anyOfSubSchema, rootSchema, isInContainer, visited, recurse));
        }, init);
    }
    return [];
};
/**
 * Retrieves an array of properties based on the provided schema.
 *
 * @param schema the schema to check for container properties
 * @param rootSchema the root schema
 * @param recurse whether to recurse in case properties have already been found.
 *        Set to true for finding all valid, i.e. nested container properties
 * @return The array of {@link Property} or empty if no properties are available
 * @see Property
 */
exports.findContainerProperties = function (schema, rootSchema, recurse) {
    return findContainerProps('root', 'root', '', schema, rootSchema, false, [], recurse);
};
//# sourceMappingURL=property.util.js.map