"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var def = require("raml-definition-system");
var universes = require("../tools/universe");
var universeHelpers = require("../tools/universeHelpers");
var services = def;
var search = require("../../search/search-interface");
var AttributeDefaultsCalculator = /** @class */ (function () {
    /**
    /**
     *
     * @param enabled - if false, defaults calculator will not return defaults from
     * attrValueOrDefault method, only original values.
     * @constructor
     */
    function AttributeDefaultsCalculator(enabled, toHighLevel) {
        if (toHighLevel === void 0) { toHighLevel = false; }
        this.enabled = enabled;
        this.toHighLevel = toHighLevel;
        /**
         * These calculators are only applied when default calculator is generally disabled (this.enabled==false)
         * and should cover the cases when we -need- to insert some calculated value in any case
         * and helpers should be avoided for some reason.
         * @type {UnconditionalRequiredPropertyCalculator[]}
         */
        this.unconditionalValueCalculators = [
            new UnconditionalRequiredPropertyCalculator(),
        ];
        this.valueCalculators = [
            new RequiredPropertyCalculator(),
            new TypePropertyCalculator(),
            new DisplayNamePropertyCalculator(),
            new MediaTypeCalculator(),
            new SecuredByPropertyCalculator(this.toHighLevel),
            new ProtocolsPropertyCalculator(),
            new VersionParamEnumCalculator()
        ];
    }
    /**
     * Return attribute default value if defaults calculator is enabled.
     * If attribute value is null or undefined, returns attribute default.
     */
    AttributeDefaultsCalculator.prototype.attributeDefaultIfEnabled = function (node, attributeProperty) {
        if (!this.enabled)
            return this.getUnconditionalAttributeDefault(attributeProperty, node);
        return this.getAttributeDefault(node, attributeProperty);
    };
    AttributeDefaultsCalculator.prototype.getUnconditionalAttributeDefault = function (attributeProperty, node) {
        if (!node || !attributeProperty)
            return null;
        for (var i = 0; i < this.unconditionalValueCalculators.length; i++) {
            var calculator = this.unconditionalValueCalculators[i];
            if (calculator.matches(attributeProperty, node)) {
                var value = calculator.calculate(attributeProperty, node);
                if (value != null) {
                    return value;
                }
            }
        }
        return null;
    };
    /**
     * Returns attribute default.
     */
    AttributeDefaultsCalculator.prototype.getAttributeDefault = function (node, attributeProperty) {
        if (!node || !attributeProperty)
            return null;
        try {
            return this.getAttributeDefault2(attributeProperty, node);
        }
        catch (Error) {
            console.log(Error);
            return null;
        }
    };
    AttributeDefaultsCalculator.prototype.getWrapperAttributeDefault = function (wrapperNode, attributeName) {
        var highLevelNode = wrapperNode.highLevel();
        if (highLevelNode == null)
            return null;
        var property = highLevelNode.definition().property(attributeName);
        if (property == null)
            return null;
        return this.getAttributeDefault(highLevelNode, property);
    };
    /**
     * Returns attribute default.
     * There are so many arguments instead of just providing a single AST node and getting
     * anything we want from it as sometimes we create fake nodes in helpers and thus
     * do not have actual high-level nodes at hands.
     */
    AttributeDefaultsCalculator.prototype.getAttributeDefault2 = function (attributeProperty, node) {
        for (var i = 0; i < this.valueCalculators.length; i++) {
            var calculator = this.valueCalculators[i];
            if (calculator.matches(attributeProperty, node)) {
                var value = calculator.calculate(attributeProperty, node);
                if (value != null) {
                    return value;
                }
            }
        }
        if (universeHelpers.isTypeProperty(attributeProperty)
            && node.attr("schema")
            && node.attr("schema").value() != null) {
            return null;
        }
        //static values defined in definition system via defaultValue, defaultIntegerValue
        // and defaultBooleanValue annotations.
        if (attributeProperty.defaultValue() != null) {
            return attributeProperty.defaultValue();
        }
        return null;
    };
    AttributeDefaultsCalculator.prototype.isEnabled = function () {
        return this.enabled;
    };
    AttributeDefaultsCalculator.prototype.insertionKind = function (node, attributeProperty) {
        for (var i = 0; i < this.valueCalculators.length; i++) {
            var calculator = this.valueCalculators[i];
            if (calculator.matches(attributeProperty, node)) {
                return calculator.kind(node, attributeProperty);
            }
        }
        if (attributeProperty.defaultValue() != null) {
            return InsertionKind.BY_DEFAULT;
        }
        return null;
    };
    return AttributeDefaultsCalculator;
}());
exports.AttributeDefaultsCalculator = AttributeDefaultsCalculator;
var InsertionKind;
(function (InsertionKind) {
    InsertionKind[InsertionKind["CALCULATED"] = 0] = "CALCULATED";
    InsertionKind[InsertionKind["BY_DEFAULT"] = 1] = "BY_DEFAULT";
})(InsertionKind = exports.InsertionKind || (exports.InsertionKind = {}));
var MediaTypeCalculator = /** @class */ (function () {
    function MediaTypeCalculator() {
    }
    MediaTypeCalculator.prototype.calculate = function (attributeProperty, node) {
        var root = search.declRoot(node);
        if (root && universeHelpers.isApiSibling(root.definition())) {
            var defaultMediaTypeAttr = root.attr(universes.Universe10.Api.properties.mediaType.name);
            if (defaultMediaTypeAttr) {
                return defaultMediaTypeAttr.value();
            }
        }
        return null;
    };
    MediaTypeCalculator.prototype.matches = function (attributeProperty, node) {
        if (!universeHelpers.isNameProperty(attributeProperty)) {
            return false;
        }
        var nodeDefinition = node.definition();
        if (!nodeDefinition) {
            return false;
        }
        if (!(universeHelpers.isBodyLikeType(nodeDefinition)
            || universeHelpers.isTypeDeclarationSibling(nodeDefinition))) {
            return false;
        }
        var parentNode = node.parent();
        if (parentNode == null) {
            return false;
        }
        var parentDefinition = parentNode.definition();
        if (parentDefinition == null) {
            return false;
        }
        if (!(universeHelpers.isResponseType(parentDefinition)
            || universeHelpers.isMethodBaseSibling(parentDefinition))) {
            return false;
        }
        var ancestor = parentNode;
        while (ancestor) {
            var aDef = ancestor.definition();
            if (universeHelpers.isTraitType(aDef)) {
                return false;
            }
            if (universeHelpers.isResourceTypeType(aDef)) {
                return false;
            }
            ancestor = ancestor.parent();
        }
        return true;
    };
    MediaTypeCalculator.prototype.kind = function (node, attributeProperty) {
        return InsertionKind.CALCULATED;
    };
    return MediaTypeCalculator;
}());
var DisplayNamePropertyCalculator = /** @class */ (function () {
    function DisplayNamePropertyCalculator() {
    }
    DisplayNamePropertyCalculator.prototype.calculate = function (attributeProperty, node) {
        var nodeDefinition = node.definition();
        if (nodeDefinition == null) {
            return null;
        }
        var adapter = nodeDefinition.getAdapter(services.RAMLService);
        var keyProperty = adapter.getKeyProp();
        if (keyProperty != null) {
            var attributeValue = node.attrValue(keyProperty.nameId());
            if (attributeValue != null) {
                return attributeValue;
            }
            else {
                return new AttributeDefaultsCalculator(true).getAttributeDefault(node, keyProperty);
            }
        }
        return null;
    };
    DisplayNamePropertyCalculator.prototype.matches = function (attributeProperty, node) {
        var nodeDefinition = node.definition();
        if (nodeDefinition == null) {
            return false;
        }
        return (universeHelpers.isTypeDeclarationSibling(nodeDefinition)
            || nodeDefinition.isAssignableFrom(universes.Universe08.Parameter.name)
            || universeHelpers.isResourceType(nodeDefinition))
            && universeHelpers.isDisplayNameProperty(attributeProperty);
    };
    DisplayNamePropertyCalculator.prototype.kind = function (node, attributeProperty) {
        return InsertionKind.CALCULATED;
    };
    return DisplayNamePropertyCalculator;
}());
var TypePropertyCalculator = /** @class */ (function () {
    function TypePropertyCalculator() {
    }
    TypePropertyCalculator.prototype.calculate = function (attributeProperty, node) {
        if (node.attr("schema") && node.attr("schema").value() != null) {
            return null; //node.attr("schema").plainValue();
        }
        else if (node.lowLevel().children().filter(function (x) { return x.key() == "properties"; }).length) {
            return "object";
        }
        else if (node.property() && node.property().nameId() == "body") {
            return "any";
        }
        return "string";
    };
    TypePropertyCalculator.prototype.matches = function (attributeProperty, node) {
        return universeHelpers.isTypeProperty(attributeProperty)
            && node.definition() != null
            && universeHelpers.isTypeDeclarationSibling(node.definition());
    };
    TypePropertyCalculator.prototype.kind = function (node, attributeProperty) {
        var schemaAttr = node.attr("schema");
        if (schemaAttr && schemaAttr.value()) {
            return null;
        }
        return InsertionKind.BY_DEFAULT;
    };
    return TypePropertyCalculator;
}());
var RequiredPropertyCalculator = /** @class */ (function () {
    function RequiredPropertyCalculator() {
    }
    RequiredPropertyCalculator.prototype.calculate = function (attributeProperty, node) {
        var nodeDefinition = node.definition();
        var nodeProperty = node.property();
        if (nodeDefinition == null) {
            return null;
        }
        //if node key is ending with question mark, it optional, thus its "required" == false
        var adapter = nodeDefinition.getAdapter(services.RAMLService);
        var keyProperty = adapter.getKeyProp();
        if (keyProperty != null) {
            var attribute = node.attr(keyProperty.nameId());
            if (attribute != null && attribute.optional()) {
                return false;
            }
        }
        if (nodeProperty != null) {
            //the spec is unclear with regard to this parameter, but for now it looks like:
            //for query string parameters, form parameters, and request and response headers the default is false
            //for URI parameters the default is true
            //for base URI parameters - unclear, but according to old JS parser behavior it looks like the default is true
            //for all other entities we back drop to what definition system states
            if (universeHelpers.isHeadersProperty(nodeProperty) ||
                universeHelpers.isFormParametersProperty(nodeProperty) ||
                universeHelpers.isQueryParametersProperty(nodeProperty)) {
                if (attributeProperty.domain().universe().version() == "RAML08") {
                    return false;
                }
                return true;
            }
            else if (universeHelpers.isUriParametersProperty(nodeProperty) ||
                universeHelpers.isBaseUriParametersProperty(nodeProperty)) {
                return true;
            }
        }
        if (attributeProperty.defaultValue() != null) {
            return attributeProperty.defaultValue();
        }
        return null;
    };
    RequiredPropertyCalculator.prototype.matches = function (attributeProperty, node) {
        return universeHelpers.isRequiredProperty(attributeProperty);
    };
    RequiredPropertyCalculator.prototype.kind = function (node, attributeProperty) {
        if (universeHelpers.isRequiredProperty(attributeProperty)) {
            var nodeProp = node.property();
            if (universeHelpers.isPropertiesProperty(nodeProp)
                || universeHelpers.isHeadersProperty(nodeProp)
                || universeHelpers.isBaseUriParametersProperty(nodeProp)
                || universeHelpers.isUriParametersProperty(nodeProp)
                || universeHelpers.isQueryParametersProperty(nodeProp)) {
                if (node.lowLevel().optional()) {
                    return null;
                }
            }
        }
        return InsertionKind.BY_DEFAULT;
    };
    return RequiredPropertyCalculator;
}());
var SecuredByPropertyCalculator = /** @class */ (function () {
    function SecuredByPropertyCalculator(toHighLevel) {
        if (toHighLevel === void 0) { toHighLevel = false; }
        this.toHighLevel = toHighLevel;
    }
    SecuredByPropertyCalculator.prototype.calculate = function (attributeProperty, node) {
        if (universeHelpers.isApiSibling(node.definition())) {
            return null;
        }
        var values;
        //instanceof, but have to avoid direct usage of instanceof in JS.
        var definition = node.definition();
        if (universeHelpers.isMethodType(definition)) {
            var resource = node.parent();
            if (resource) {
                var resourceSlave = resource.getLastSlaveCounterPart();
                if (resourceSlave)
                    resource = resourceSlave;
                values = this.toHighLevel
                    ? resource.attributes("securedBy")
                    : resource.wrapperNode().securedBy();
            }
        }
        if (!values || values.length == 0) {
            while (node != null && !universeHelpers.isApiSibling(node.definition())) {
                node = node.parent();
            }
            if (node) {
                var nodeSlave = node.getLastSlaveCounterPart();
                if (nodeSlave)
                    node = nodeSlave;
                values = this.toHighLevel
                    ? node.attributes("securedBy")
                    : node.wrapperNode().securedBy();
            }
        }
        if (values && values.length > 0) {
            return values;
        }
        return null;
    };
    SecuredByPropertyCalculator.prototype.matches = function (attributeProperty, node) {
        var nodeDefinition = node.definition();
        if (nodeDefinition == null) {
            return false;
        }
        return universeHelpers.isSecuredByProperty(attributeProperty);
    };
    SecuredByPropertyCalculator.prototype.kind = function (node, attributeProperty) {
        return InsertionKind.CALCULATED;
    };
    return SecuredByPropertyCalculator;
}());
var ProtocolsPropertyCalculator = /** @class */ (function () {
    function ProtocolsPropertyCalculator() {
    }
    ProtocolsPropertyCalculator.prototype.calculate = function (attributeProperty, node) {
        while (node != null && !universeHelpers.isApiSibling(node.definition())) {
            node = node.parent();
        }
        var result;
        var baseUriAttr = node.attr(universes.Universe10.Api.properties.baseUri.name);
        if (baseUriAttr) {
            var baseUri = baseUriAttr.value();
            if (baseUri && typeof baseUri == "string") {
                var ind = baseUri.indexOf('://');
                if (ind >= 0) {
                    result = [baseUri.substring(0, ind).toUpperCase()];
                }
                if (!result) {
                    result = ['HTTP'];
                }
            }
        }
        return result;
    };
    ProtocolsPropertyCalculator.prototype.matches = function (attributeProperty, node) {
        if (!universeHelpers.isProtocolsProperty(attributeProperty)) {
            return false;
        }
        var nodeDefinition = node.definition();
        var hasAppropriateLocation = false;
        if (universeHelpers.isApiSibling(nodeDefinition)) {
            hasAppropriateLocation = true;
        }
        else if (universeHelpers.isResourceType(nodeDefinition)) {
            hasAppropriateLocation = true;
        }
        else if (universeHelpers.isMethodType(nodeDefinition)) {
            var parentNode = node.parent();
            hasAppropriateLocation = parentNode && universeHelpers.isResourceType(parentNode.definition());
        }
        return hasAppropriateLocation;
    };
    ProtocolsPropertyCalculator.prototype.kind = function (node, attributeProperty) {
        return InsertionKind.CALCULATED;
    };
    return ProtocolsPropertyCalculator;
}());
var VersionParamEnumCalculator = /** @class */ (function () {
    function VersionParamEnumCalculator() {
    }
    VersionParamEnumCalculator.prototype.calculate = function (attributeProperty, node) {
        while (node != null && !universeHelpers.isApiSibling(node.definition())) {
            node = node.parent();
        }
        var versionAttr = node.attr(universes.Universe10.Api.properties.version.name);
        if (versionAttr) {
            var versionValue = versionAttr.value();
            if (versionValue && versionValue.trim()) {
                return [versionValue];
            }
        }
        return null;
    };
    VersionParamEnumCalculator.prototype.matches = function (attributeProperty, node) {
        if (!universeHelpers.isEnumProperty(attributeProperty)) {
            return false;
        }
        var nodeProperty = node.property();
        if (!nodeProperty) {
            return false;
        }
        if (!universeHelpers.isBaseUriParametersProperty(nodeProperty)) {
            return false;
        }
        var nameAttr = node.attr(universes.Universe10.TypeDeclaration.properties.name.name);
        var paramName = nameAttr && nameAttr.value();
        if (paramName != 'version') {
            return false;
        }
        return true;
    };
    VersionParamEnumCalculator.prototype.kind = function (node, attributeProperty) {
        return InsertionKind.CALCULATED;
    };
    return VersionParamEnumCalculator;
}());
/**
 * This calculator inserts "required=false" if the key property ends with question mark.
 * All other cases are handled in the regular RequiredPropertyCalculator
 */
var UnconditionalRequiredPropertyCalculator = /** @class */ (function () {
    function UnconditionalRequiredPropertyCalculator() {
    }
    UnconditionalRequiredPropertyCalculator.prototype.calculate = function (attributeProperty, node) {
        var nodeDefinition = node.definition();
        if (nodeDefinition == null)
            return null;
        //if node key is ending with question mark, it optional, thus its "required" == false
        var adapter = nodeDefinition.getAdapter(services.RAMLService);
        if (adapter == null)
            return null;
        var keyProperty = adapter.getKeyProp();
        if (keyProperty == null)
            return null;
        var attribute = node.attr(keyProperty.nameId());
        if (attribute == null)
            return null;
        if (attribute.optional())
            return false;
        return null;
    };
    UnconditionalRequiredPropertyCalculator.prototype.matches = function (attributeProperty, node) {
        return universeHelpers.isRequiredProperty(attributeProperty);
    };
    UnconditionalRequiredPropertyCalculator.prototype.kind = function (node, attributeProperty) {
        return InsertionKind.BY_DEFAULT;
    };
    return UnconditionalRequiredPropertyCalculator;
}());
//# sourceMappingURL=defaultCalculator.js.map