"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    }
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var defs = require("raml-definition-system");
var ramlTypes = defs.rt;
var _ = require("underscore");
var universes = require("../tools/universe");
var hlimpl = require("../highLevelImpl");
var services = defs;
var linter = require("./linter");
var universeHelpers = require("../tools/universeHelpers");
ramlTypes.setPropertyConstructor(function (x) {
    var v = new defs.Property(x);
    v.unmerge();
    return v;
});
function templateFields(node, d) {
    var u = node.root().definition().universe();
    var key = node.lowLevel().key();
    if (key) {
        var prop = hlimpl.ASTPropImpl.isInstance(node) ? node : null;
        handleValue(key, d, prop, true, u, true);
    }
    node.children().forEach(function (x) { return templateFields(x, d); });
    if (hlimpl.ASTPropImpl.isInstance(node)) {
        var prop_1 = node;
        //TODO RECURSIVE PARAMETERS
        var v = prop_1.value();
        if (typeof v == 'string') {
            var strV = v;
            handleValue(strV, d, prop_1, false, u);
        }
        else {
            node.lowLevel().visit(function (x) {
                if (x.value()) {
                    var strV = x.value() + "";
                    handleValue(strV, d, prop_1, true, u);
                }
                return true;
            });
        }
    }
    else if (hlimpl.BasicASTNode.isInstance(node)) {
        var v = node.lowLevel().value();
        if (typeof v == 'string') {
            var strV = v;
            handleValue(strV, d, null, false, u);
        }
        else {
            node.lowLevel().visit(function (x) {
                if (x.value()) {
                    var strV = x.value() + "";
                    handleValue(strV, d, null, true, u);
                }
                return true;
            });
        }
    }
}
var extractUsedParamNames = function (strV, isKey) {
    if (isKey === void 0) { isKey = false; }
    if (isKey && strV.charAt(0) == "(" && strV.charAt(strV.length - 1) == ")") {
        strV = strV.substring(1, strV.length - 1);
    }
    var parameterUsages = [];
    var ps = 0;
    while (true) {
        var pos = strV.indexOf("<<", ps);
        if (pos == -1) {
            break;
        }
        var end = strV.indexOf(">>", pos);
        var isFull = pos == 0 && end == strV.length - 2;
        var parameterUsage = strV.substring(pos + 2, end);
        ps = pos + 2;
        var directiveIndex = parameterUsage.indexOf("|");
        if (directiveIndex != -1) {
            isFull = false;
            parameterUsage = parameterUsage.substring(0, directiveIndex);
        }
        parameterUsage = parameterUsage.trim();
        parameterUsages.push(parameterUsage);
    }
    return { parameterUsages: parameterUsages, isFull: isFull };
};
var handleValue = function (strV, d, prop, allwaysString, u, isKey) {
    if (isKey === void 0) { isKey = false; }
    var __ret = extractUsedParamNames(strV, isKey);
    var parameterUsages = __ret.parameterUsages;
    var isFull = __ret.isFull;
    var r = (prop) ? prop.property().range() : null;
    if (isKey && strV.charAt(0) == "(" && strV.charAt(strV.length - 1) == ")") {
        r = u.type(universes.Universe10.SchemaString.name);
        allwaysString = false;
    }
    else if (prop) {
        if (prop.property().nameId() == universes.Universe10.TypeDeclaration.properties.type.name ||
            prop.property().nameId() == universes.Universe10.TypeDeclaration.properties.schema.name) {
            if (prop.property().domain().key() == universes.Universe10.TypeDeclaration) {
                r = u.type(universes.Universe10.SchemaString.name);
            }
        }
    }
    if (!isFull || allwaysString) {
        r = u.type(universes.Universe10.StringType.name);
    }
    else if (r == null && u.version() == "RAML10") {
        r = u.type(universes.Universe10.AnyType.name);
    }
    for (var _i = 0, parameterUsages_1 = parameterUsages; _i < parameterUsages_1.length; _i++) {
        var parameterUsage = parameterUsages_1[_i];
        if (linter.RESERVED_TEMPLATE_PARAMETERS[parameterUsage] != null) {
            //Handling reserved parameter names;
            continue;
        }
        var q = d[parameterUsage];
        //FIX ME NOT WHOLE TEMPLATES
        if (q) {
            q.push({
                tp: r,
                attr: prop
            });
        }
        else {
            d[parameterUsage] = [{
                    tp: r,
                    attr: prop
                }];
        }
    }
};
function fillParamPaths(node, paramPaths, path) {
    if (path === void 0) { path = []; }
    if (node.optional()) {
        path = path.concat("/");
    }
    var v = node.value();
    if (typeof v == 'string') {
        var strV = v;
        var __ret = extractUsedParamNames(strV);
        var parameterUsages = __ret.parameterUsages;
        for (var _i = 0, parameterUsages_2 = parameterUsages; _i < parameterUsages_2.length; _i++) {
            var pu = parameterUsages_2[_i];
            var paths = paramPaths[pu];
            if (paths == null) {
                paths = [];
                paramPaths[pu] = paths;
            }
            paths.push(path);
        }
    }
    else {
        for (var _a = 0, _b = node.children(); _a < _b.length; _a++) {
            var ch = _b[_a];
            fillParamPaths(ch, paramPaths, path.concat(ch.key()));
        }
    }
}
function fillTemplateType(result, node) {
    var usages = {};
    var paramPaths = {};
    templateFields(node, usages);
    fillParamPaths(node.lowLevel(), paramPaths);
    for (var _i = 0, _a = Object.keys(paramPaths); _i < _a.length; _i++) {
        var pu = _a[_i];
        paramPaths[pu] = _.unique(paramPaths[pu]);
    }
    result.getAdapter(services.RAMLService).setInlinedTemplates(true);
    Object.keys(usages).forEach(function (x) {
        var propSource = null;
        if (usages[x].length > 0) {
            propSource = usages[x][0].attr;
        }
        var prop = new defs.UserDefinedProp(x, propSource);
        //prop._node=node;
        prop.withDomain(result);
        var paths = paramPaths[x];
        prop.getAdapter(services.RAMLPropertyService).putMeta("templatePaths", paths);
        var defaultType;
        if (node.definition().universe().version() == "RAML10") {
            var hasString = usages[x].filter(function (y) { return universeHelpers.isStringTypeType(y.tp); }).length > 0;
            defaultType = hasString ? universes.Universe10.StringType.name : universes.Universe10.AnyType.name;
        }
        else {
            defaultType = universes.Universe08.StringType.name;
        }
        var tp = _.unique(usages[x].map(function (x) { return x.tp; })).filter(function (x) { return x && x.nameId() != defaultType; });
        prop.withRange(tp.length == 1 ? tp[0] : node.definition().universe().type(defaultType));
        prop.withRequired(true);
        if (tp.length == 1 && node.definition().universe().version() == 'RAML10') {
            if (tp[0].key() == universes.Universe10.SchemaString) {
                prop.getAdapter(services.RAMLPropertyService).setTypeExpression(true);
            }
        }
        prop.unmerge();
    });
    var keyProp = new defs.UserDefinedProp("____key", node);
    //prop._node=node;
    keyProp.withDomain(result);
    keyProp.withKey(true);
    keyProp.withFromParentKey(true);
    keyProp.withRange(node.definition().universe().type(universes.Universe08.StringType.name));
    return result;
}
function fillReferenceType(result, def) {
    if (def.universe().version() == "RAML08") {
        result.getAdapter(services.RAMLService).withAllowAny();
    }
    var p = def.property(def.getAdapter(services.RAMLService).getReferenceIs());
    if (p) {
        p.range().properties().forEach(function (x) {
            var prop = new defs.Property(x.nameId());
            prop.unmerge();
            prop.withDomain(result);
            prop.withRange(x.range());
            prop.withMultiValue(x.isMultiValue());
        });
    }
    return result;
}
var AnnotationType = /** @class */ (function (_super) {
    __extends(AnnotationType, _super);
    function AnnotationType() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    AnnotationType.prototype.allProperties = function (ps) {
        if (ps === void 0) { ps = {}; }
        var rs = this.superTypes()[0].allProperties();
        if (rs.length == 1 && rs[0].nameId() == "annotations") {
            var propertyNode = this.getAdapter(defs.RAMLService).getDeclaringNode();
            var property = new defs.UserDefinedProp("value", propertyNode);
            property.withDomain(this);
            property.withCanBeValue();
            property.withRequired(false);
            var tp = this.superTypes()[0];
            rs = [];
            property.withRange(property.node().asElement().definition().universe().type("string"));
            rs.push(property);
        }
        return rs;
    };
    AnnotationType.prototype.isAnnotationType = function () {
        return true;
    };
    return AnnotationType;
}(defs.UserDefinedClass));
function typeFromNode(node) {
    if (!node) {
        return null;
    }
    if (node.associatedType()) {
        return node.associatedType();
    }
    var root = node.root();
    var isApi = root.definition() && universeHelpers.isApiSibling(root.definition());
    var isInLibExpandModel = node.root().lowLevel()['libProcessed'];
    if ((!isApi || isInLibExpandModel) && node.definition().isAssignableFrom(universes.Universe10.TypeDeclaration.name)) {
        var parent_1 = node.parent();
        if (parent_1 && parent_1.definition().isAssignableFrom(universes.Universe10.TypeDeclaration.name)) {
            var parentType = parent_1.localType();
            if (parentType != null) {
                if (universeHelpers.isPropertiesProperty(node.property())) {
                    var name_1 = node.name();
                    var prop = parentType.properties().find(function (p) { return p.nameId() == name_1; });
                    if (prop) {
                        var range = prop.range();
                        if (range) {
                            return range;
                        }
                    }
                }
                else if (universeHelpers.isItemsProperty(node.property()) && parentType.isArray()) {
                    var ct = parentType.array().componentType();
                    if (ct) {
                        return ct;
                    }
                }
                else if (universeHelpers.isTypeProperty(node.property())) {
                    var superTypes = parentType.superTypes();
                    var ct = superTypes && superTypes.length == 1 && superTypes[0];
                    if (ct) {
                        return ct;
                    }
                }
            }
        }
    }
    var u = node.lowLevel().unit();
    var upath = u ? u.path() : "";
    ramlTypes.setPropertyConstructor(function (x) {
        var propertySource = null;
        var rs = node.elementsOfKind("properties").filter(function (y) { return y.name() == x; });
        if (rs) {
            propertySource = rs[0];
        }
        var v = new defs.UserDefinedProp(x, propertySource);
        v.unmerge();
        return v;
    });
    try {
        var def = node.definition();
        if (node.property() && node.property().nameId() == universes.Universe10.LibraryBase.properties.annotationTypes.name) {
            //var st=node.definition().getAdapter(services.RAMLService).toRuntime();
            var result = new AnnotationType(node.name(), node.definition().universe(), node, upath, "");
            var st = getSimpleType(node);
            result._superTypes.push(st);
            if (node.elementsOfKind(universes.Universe10.ObjectTypeDeclaration.properties.properties.name).length == 0) {
                result.getAdapter(services.RAMLService).withAllowAny();
            }
            var extType = def.getAdapter(services.RAMLService).getExtendedType();
            if (extType) {
                result._superTypes.push(extType);
            }
            return result;
        }
        else {
            var result = new defs.UserDefinedClass(node.name(), node.definition().universe(), node, upath, "");
        }
        node.setAssociatedType(result);
        //result.setDeclaringNode(node);
        if (def.getAdapter(services.RAMLService).isInlinedTemplates()) {
            return fillTemplateType(result, node);
        }
        else if (def.getAdapter(services.RAMLService).getReferenceIs()) {
            return fillReferenceType(result, def);
        }
        var rs = getSimpleType(node);
        rs.getAdapter(services.RAMLService).setDeclaringNode(node);
        node.setAssociatedType(rs);
    }
    finally {
        ramlTypes.setPropertyConstructor(null);
    }
    return rs;
}
exports.typeFromNode = typeFromNode;
function getSimpleType(node) {
    return ramlTypes.toNominal(node.parsedType(), function (x) {
        var m = node.definition().universe().type(x);
        if (!m) {
            var ut = new defs.UserDefinedClass("", node.definition().universe(), node, "", "");
        }
        return m;
    });
}
function transform(u) {
    return function (x) {
        var m = u.type(x);
        if (!m) {
            var ut = new defs.UserDefinedClass("", u, null, "", "");
        }
        return m;
    };
}
function convertType(root, t) {
    var node = _.find(root.elementsOfKind("types"), function (x) { return x.name() == t.name(); });
    if (node) {
        ramlTypes.setPropertyConstructor(function (x) {
            var rs = node.elementsOfKind("properties").filter(function (y) { return y.name() == x; });
            var propertySource = null;
            if (rs && rs.length > 0) {
                propertySource = rs[0];
            }
            else {
                var rs = node.elementsOfKind("facets").filter(function (y) { return y.name() == x; });
                if (rs && rs.length > 0) {
                    propertySource = rs[0];
                }
            }
            var v = new defs.UserDefinedProp(x, propertySource);
            v.unmerge();
            return v;
        });
    }
    var u = transform(root.definition().universe());
    return ramlTypes.toNominal(t, u);
}
exports.convertType = convertType;
//# sourceMappingURL=typeBuilder.js.map