"use strict";
var __assign = (this && this.__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;
};
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;
};
var deepCopy = function (object) {
    return JSON.parse(JSON.stringify(object));
};
/**
 * 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.schema.$id,
        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 propertyKey = ref.substring((ref.indexOf('/') + 1), (ref.lastIndexOf('/')));
            var property = ref.substring((ref.lastIndexOf('/') + 1));
            if (_.has(parentSchema, propertyKey) && _.has(parentSchema, propertyKey + "." + property)) {
                if (schema[propertyKey]) {
                    schema[propertyKey] = __assign({}, schema[propertyKey], (_a = {},
                        _a[property] = parentSchema[propertyKey][property],
                        _a));
                }
                else {
                    schema = __assign({}, schema, (_b = {},
                        _b[propertyKey] = (_c = {}, _c[property] = parentSchema[propertyKey][property], _c),
                        _b));
                }
            }
            var _a, _b, _c;
        });
    }
    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
 */
exports.makeSelfContainedSchema = function (parentSchema, refPath) {
    var schema = core_1.resolveSchema(parentSchema, refPath);
    schema = deepCopy(schema);
    if (_.isEmpty(schema.$id)) {
        schema.$id = '#' + refPath;
    }
    schema = __assign({}, schema, exports.makeSchemaSelfContained(parentSchema, schema));
    return schema;
};
/**
 * Calculates label for an item which is resolved in anyOf
 * @param {string} id The schema id
 * @returns {string}
 */
var calculateLabel = function (id) { return (_.startsWith(id, '#') ? id.substring(id.lastIndexOf('#') + 1) : id); };
/**
 * 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
 *                                they are in a container.
 * @param {boolean} hasOnlyOwnChildren To accept only corresponding children of a property
 * @returns {@link Property[]} An array of properties where each property describes
 *                             a self-contained schema for the corresponding schema
 */
exports.findContainerProperties = function (property, label, schema, rootSchema, isInContainer, hasOnlyOwnChildren) {
    if (hasOnlyOwnChildren === void 0) { hasOnlyOwnChildren = false; }
    if (schema.$ref !== undefined) {
        return exports.findContainerProperties(property, schema.$ref === '#' ? undefined : schema.$ref.substring(schema.$ref.lastIndexOf('/') + 1), exports.makeSelfContainedSchema(rootSchema, schema.$ref), rootSchema, isInContainer, hasOnlyOwnChildren);
    }
    if (isObject(schema)) {
        if (isInContainer) {
            var prop = {
                property: property,
                label: label,
                schema: schema
            };
            return [prop];
        }
        if (hasOnlyOwnChildren) {
            // All children of the property are known at this point
            // Don't need to resolve schema further
            return [];
        }
        return Object.keys(schema.properties)
            .reduce(function (prev, currentProp) {
            return prev.concat(exports.findContainerProperties(currentProp, currentProp, schema.properties[currentProp], rootSchema, false, true));
        }, []);
    }
    if (isArray(schema) && !Array.isArray(schema.items)) {
        return exports.findContainerProperties(property, label, schema.items, rootSchema, true, hasOnlyOwnChildren);
    }
    if (schema.anyOf !== undefined) {
        return schema.anyOf
            .reduce(function (prev, currentProp) {
            return prev.concat(exports.findContainerProperties(property, currentProp.$id !== undefined ? calculateLabel(currentProp.$id) : undefined, currentProp, rootSchema, isInContainer, hasOnlyOwnChildren));
        }, []);
    }
    return [];
};
/**
 * Retrieves an array of properties based on the provided schema.
 *
 * @param schema The schema to check for properties
 * @return The array of {@link Property} or empty if no properties are available
 * @see Property
 */
exports.retrieveContainerProperties = function (schema, rootSchema) {
    return exports.findContainerProperties('root', 'root', schema, rootSchema, false);
};
/**
 * Returns a flattened map of sub schema ID to 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 schema The root schema
 * @param containerProperties All the available containers in the given schema
 *
 * @returns {{[p: string]: Property[]}}
 */
exports.findAllContainerProperties = function (schema, rootSchema, containerProperties) {
    if (containerProperties === void 0) { containerProperties = {}; }
    if (!_.has(containerProperties, schema.$id)) {
        var props = exports.retrieveContainerProperties(schema, rootSchema);
        if (props.length !== 0) {
            containerProperties = __assign({}, containerProperties, (_a = {}, _a[schema.$id] = props, _a));
            return _.reduce(props, function (prev, curProp) {
                return exports.findAllContainerProperties(curProp.schema, rootSchema, prev);
            }, containerProperties);
        }
    }
    return containerProperties;
    var _a;
};
//# sourceMappingURL=property.util.js.map