"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 ts = require("./typesystem");
var messageRegistry = ts.messageRegistry;
var tsInterfaces = require("./typesystem-interfaces");
var rs = require("./restrictions");
var typesystem_1 = require("./typesystem");
var typeExpressions = require("./typeExpressions");
var facetR = require("./facetRegistry");
var meta = require("./metainfo");
var metainfo_1 = require("./metainfo");
var typesystem_2 = require("./typesystem");
var restrictions_1 = require("./restrictions");
var _ = require("underscore");
var restrictions_2 = require("./restrictions");
var NodeKind;
(function (NodeKind) {
    NodeKind[NodeKind["SCALAR"] = 0] = "SCALAR";
    NodeKind[NodeKind["ARRAY"] = 1] = "ARRAY";
    NodeKind[NodeKind["MAP"] = 2] = "MAP";
})(NodeKind = exports.NodeKind || (exports.NodeKind = {}));
var JSObjectNode = /** @class */ (function () {
    function JSObjectNode(_key, obj, inArr, provider) {
        if (inArr === void 0) { inArr = false; }
        this._key = _key;
        this.obj = obj;
        this.inArr = inArr;
        this.provider = provider;
    }
    JSObjectNode.prototype.value = function () {
        return this.obj;
    };
    JSObjectNode.prototype.key = function () {
        if (!this._key) {
            if (this.kind() === NodeKind.MAP && this.inArr) {
                var l = Object.keys(this.obj);
                if (l.length === 1) {
                    return l[0];
                }
            }
        }
        return this._key;
    };
    JSObjectNode.prototype.childWithKey = function (k) {
        if (this.obj == null) {
            return null;
        }
        if (this.obj.hasOwnProperty(k)) {
            return new JSObjectNode(k, this.obj[k], false, this.contentProvider());
        }
        return null;
    };
    JSObjectNode.prototype.children = function () {
        var _this = this;
        if (Array.isArray(this.obj)) {
            return this.obj.map(function (x) { return new JSObjectNode(null, x, true, _this.contentProvider()); });
        }
        else if (this.obj && typeof this.obj == "object") {
            return Object.keys(this.obj).map(function (x) { return new JSObjectNode(x, _this.obj[x], false, _this.provider); });
        }
        return [];
    };
    JSObjectNode.prototype.kind = function () {
        if (!this.obj) {
            return NodeKind.SCALAR;
        }
        if (Array.isArray(this.obj)) {
            return NodeKind.ARRAY;
        }
        else if (typeof this.obj === "object") {
            return NodeKind.MAP;
        }
        return NodeKind.SCALAR;
    };
    JSObjectNode.prototype.contentProvider = function () {
        return this.provider;
    };
    ;
    JSObjectNode.prototype.getMeta = function (key) {
        return null;
    };
    JSObjectNode.prototype.path = function () {
        return JSON.stringify(this.obj);
    };
    return JSObjectNode;
}());
function parseJSON(name, n, r, provider) {
    if (r === void 0) { r = ts.builtInRegistry(); }
    return parse(name, new JSObjectNode(null, n, false, provider), r, false, false, true, false, false, []);
}
exports.parseJSON = parseJSON;
function parseJSONTypeCollection(n, r, provider) {
    if (r === void 0) { r = ts.builtInRegistry(); }
    return parseTypeCollection(new JSObjectNode(null, n, false, provider), r);
}
exports.parseJSONTypeCollection = parseJSONTypeCollection;
function endsWithQuestionMark(p) {
    return p.charAt(p.length - 1) == '?';
}
var PropertyBean = /** @class */ (function () {
    function PropertyBean() {
    }
    PropertyBean.prototype.add = function (t, typePath) {
        // const p = typePath.concat(this.id).join(", ");
        // console.log(p);
        if (!this.optional && !this.additonal && !this.regExp && !this.type.isSubTypeOf(ts.NIL)) {
            t.addMeta(new rs.HasProperty(this.id));
        }
        var matchesPropertyFacet;
        if (this.additonal) {
            matchesPropertyFacet = new rs.AdditionalPropertyIs(this.type);
        }
        else if (this.regExp) {
            matchesPropertyFacet = new rs.MapPropertyIs(this.id, this.type, this.optional);
        }
        else {
            matchesPropertyFacet = new rs.PropertyIs(this.id, this.type, this.optional);
        }
        if (matchesPropertyFacet != null) {
            t.addMeta(matchesPropertyFacet);
            if (this.type instanceof ts.InheritedType && this.type.name() == null) {
                //Linking anonymous types with properties declaring them
                this.type.setContextMeta(matchesPropertyFacet);
            }
            if (this.annotations && this.annotations.length) {
                for (var _i = 0, _a = this.annotations; _i < _a.length; _i++) {
                    var a = _a[_i];
                    var a1 = new meta.Annotation(a.name, a.value, a.key);
                    a1.setOwnerFacet(matchesPropertyFacet);
                    matchesPropertyFacet.addAnnotation(a1);
                }
            }
        }
    };
    return PropertyBean;
}());
exports.PropertyBean = PropertyBean;
var TypeCollection = /** @class */ (function () {
    function TypeCollection(_id) {
        this._id = _id;
        this._types = [];
        this._typeMap = {};
        this.uses = {};
        this._annotationTypes = [];
        this._annotationTypeMap = {};
    }
    TypeCollection.prototype.id = function () {
        return this._id;
    };
    TypeCollection.prototype.library = function (n) {
        return this.uses[n];
    };
    TypeCollection.prototype.addLibrary = function (n, t) {
        this.uses[n] = t;
    };
    TypeCollection.prototype.add = function (t) {
        this._types = this._types.filter(function (x) { return x.name() != t.name(); });
        this._types.push(t);
        this._typeMap[t.name()] = t;
    };
    TypeCollection.prototype.getType = function (name) {
        if (this._typeMap.hasOwnProperty(name)) {
            return this._typeMap[name];
        }
        return null;
    };
    TypeCollection.prototype.addAnnotationType = function (t) {
        this._annotationTypes.push(t);
        this._annotationTypeMap[t.name()] = t;
    };
    TypeCollection.prototype.getAnnotationType = function (name) {
        if (this._annotationTypeMap.hasOwnProperty(name)) {
            return this._annotationTypeMap[name];
        }
        return null;
    };
    TypeCollection.prototype.types = function () {
        return this._types;
    };
    TypeCollection.prototype.annotationTypes = function () {
        return this._annotationTypes;
    };
    TypeCollection.prototype.getAnnotationTypeRegistry = function () {
        var _this = this;
        var r = new typesystem_2.TypeRegistry(ts.builtInRegistry(), this, true);
        this.annotationTypes().forEach(function (x) { return r.addType(x); });
        Object.keys(this.uses).forEach(function (x) {
            _this.uses[x].annotationTypes().forEach(function (y) { return r.put(x + "." + y.name(), y); });
        });
        return r;
    };
    TypeCollection.prototype.getTypeRegistry = function () {
        var _this = this;
        var r = new typesystem_2.TypeRegistry(ts.builtInRegistry(), this);
        this.types().forEach(function (x) { return r.addType(x); });
        Object.keys(this.uses).forEach(function (x) {
            _this.uses[x].types().forEach(function (y) { return r.put(x + "." + y.name(), y); });
        });
        return r;
    };
    TypeCollection.prototype.libraries = function () {
        return this.uses;
    };
    return TypeCollection;
}());
exports.TypeCollection = TypeCollection;
var AccumulatingRegistry = /** @class */ (function (_super) {
    __extends(AccumulatingRegistry, _super);
    function AccumulatingRegistry(toParse, schemas, ts, _c) {
        var _this = _super.call(this, ts, _c) || this;
        _this.toParse = toParse;
        _this.schemas = schemas;
        _this.parsing = {};
        return _this;
    }
    AccumulatingRegistry.prototype.getClassIdentifier = function () {
        var superIdentifiers = _super.prototype.getClassIdentifier.call(this);
        return superIdentifiers.concat(AccumulatingRegistry.CLASS_IDENTIFIER_AccumulatingRegistry);
    };
    AccumulatingRegistry.isInstance = function (instance) {
        return instance != null && instance.getClassIdentifier
            && typeof (instance.getClassIdentifier) == "function"
            && _.contains(instance.getClassIdentifier(), AccumulatingRegistry.CLASS_IDENTIFIER_AccumulatingRegistry);
    };
    AccumulatingRegistry.prototype.get = function (name, isPropertyType) {
        if (isPropertyType === void 0) { isPropertyType = false; }
        var result = _super.prototype.get.call(this, name);
        if (isPropertyType && result && result.isSubTypeOf(ts.REFERENCE)) {
            return result;
        }
        if (!result || result.isSubTypeOf(ts.REFERENCE)) {
            var chld = this.toParse ? this.toParse.childWithKey(name) : null;
            if (!chld) {
                chld = this.schemas ? this.schemas.childWithKey(name) : null;
            }
            if (chld) {
                if (this.parsing[name]) {
                    var recurrent = ts.derive(name, [ts.RECURRENT]);
                    if (result && result.isSubTypeOf(ts.REFERENCE)) {
                        result.patch(recurrent);
                    }
                    else {
                        result = recurrent;
                    }
                    return result;
                }
                this.parsing[name] = true;
                try {
                    var tp = parse(name, chld, this, false, false, true, false, false, []);
                }
                finally {
                    delete this.parsing[name];
                }
                return tp;
            }
            else {
                var dt = name.indexOf('.');
                if (dt != -1) {
                    var ln = name.substring(0, dt);
                    var tn = name.substr(dt + 1);
                    var lib = this._c.library(ln);
                    if (lib) {
                        var t = lib.getType(tn);
                        if (t) {
                            return t;
                        }
                    }
                }
            }
        }
        return result;
    };
    AccumulatingRegistry.CLASS_IDENTIFIER_AccumulatingRegistry = "parse.AccumulatingRegistry";
    return AccumulatingRegistry;
}(ts.TypeRegistry));
exports.AccumulatingRegistry = AccumulatingRegistry;
function parseTypes(n, tr) {
    if (tr === void 0) { tr = ts.builtInRegistry(); }
    var provider = n.provider && n.provider();
    return parseTypeCollection(new JSObjectNode(null, n, false, provider), tr);
}
exports.parseTypes = parseTypes;
var WrapArrayNode = /** @class */ (function () {
    function WrapArrayNode(n) {
        this.n = n;
    }
    WrapArrayNode.prototype.key = function () {
        return null;
    };
    WrapArrayNode.prototype.value = function () {
        return null;
    };
    WrapArrayNode.prototype.childWithKey = function (k) {
        var r = this.children();
        for (var i = 0; i < r.length; i++) {
            if (r[i].key() == k) {
                return r[i];
            }
        }
        return null;
    };
    WrapArrayNode.prototype.children = function () {
        return this.n.children().map(function (x) {
            var c = x.children();
            if (c.length == 1) {
                return c[0];
            }
            return x;
        });
    };
    WrapArrayNode.prototype.kind = function () {
        return NodeKind.MAP;
    };
    WrapArrayNode.prototype.getMeta = function (key) {
        return null;
    };
    WrapArrayNode.prototype.path = function () {
        return this.n.path();
    };
    return WrapArrayNode;
}());
function transformToArray(n) {
    return new WrapArrayNode(n);
}
function parseTypeCollection(n, tr, ignoreUses) {
    if (ignoreUses === void 0) { ignoreUses = false; }
    var result = new TypeCollection(n.path());
    if (n.anchor) {
        if (n.anchor().__$$) {
            return n.anchor().__$$;
        }
        n.anchor().__$$ = result;
    }
    var tpes = n.childWithKey("types");
    if (tpes && tpes.kind() === NodeKind.ARRAY) {
        tpes = transformToArray(tpes);
    }
    var schemas = n.childWithKey("schemas");
    if (schemas && schemas.kind() === NodeKind.ARRAY) {
        schemas = transformToArray(schemas);
    }
    var reg = new AccumulatingRegistry(tpes, schemas, tr, result);
    if (tpes && tpes.kind() !== NodeKind.SCALAR) {
        tpes.children().filter(function (x) { return x.key() && true; }).forEach(function (x) {
            var t = ts.derive(x.key(), [ts.REFERENCE]);
            result.add(t);
            reg.addType(t);
        });
    }
    if (schemas && schemas.kind() !== NodeKind.SCALAR) {
        schemas.children().filter(function (x) { return x.key() && true; }).forEach(function (x) {
            var t = ts.derive(x.key(), [ts.REFERENCE]);
            result.add(t);
            reg.addType(t);
        });
    }
    if (!ignoreUses) {
        var uses = n.childWithKey("uses");
        if (uses && uses.kind() === NodeKind.ARRAY) {
            uses = transformToArray(uses);
        }
        if (uses && uses.kind() === NodeKind.MAP) {
            uses.children().forEach(function (c) {
                result.addLibrary(c.key(), parseTypeCollection(c, tr));
            });
        }
    }
    if (tpes && tpes.kind() !== NodeKind.SCALAR) {
        tpes.children().filter(function (x) { return x.key() && true; }).forEach(function (x) {
            reg.get(x.key());
        });
    }
    if (schemas && schemas.kind() !== NodeKind.SCALAR) {
        schemas.children().filter(function (x) { return x.key() && true; }).forEach(function (x) {
            reg.get(x.key());
        });
    }
    reg.types().forEach(function (x) { return result.add(x); });
    var tpes = n.childWithKey("annotationTypes");
    if (tpes && tpes.kind() === NodeKind.ARRAY) {
        tpes = transformToArray(tpes);
    }
    if (tpes != null && tpes.kind() === NodeKind.MAP) {
        tpes.children().forEach(function (x) {
            result.addAnnotationType(parse(x.key(), x, reg, false, true, false, false, false, []));
        });
    }
    return result;
}
exports.parseTypeCollection = parseTypeCollection;
function parsePropertyBean(n, tr, typePath) {
    var result = new PropertyBean();
    var hasRequiredFacet = false;
    var requiredNode = n.childWithKey("required");
    if (requiredNode) {
        var rsValue = requiredNode.value();
        if (typeof rsValue === "boolean") {
            hasRequiredFacet = true;
        }
        else if (rsValue && typeof (rsValue) === "object" && typeof rsValue.value === "boolean") {
            hasRequiredFacet = true;
            var annotations = Object.keys(rsValue).filter(function (x) {
                x = x.trim();
                return x.length > 1 && x.charAt(0) == "(" && x.charAt(x.length - 1) == ")";
            });
            if (annotations.length) {
                result.annotations = annotations.map(function (x) {
                    x = x.trim();
                    return {
                        name: x.substring(1, x.length - 1).trim(),
                        key: x,
                        value: rsValue[x]
                    };
                });
            }
        }
        if (rsValue === false) {
            result.optional = true;
            result.id = n.key();
        }
    }
    var name = n.key();
    if (!hasRequiredFacet && endsWithQuestionMark(n.key())) {
        name = name.substr(0, name.length - 1);
        result.optional = true;
    }
    if (name != null && name.length == 0 || name === '/.*/') {
        result.additonal = true;
    }
    else if (name.charAt(0) == '/' && name.charAt(name.length - 1) == '/') {
        name = name.substring(1, name.length - 1);
        result.regExp = true;
    }
    var tp1 = [].concat(typePath);
    tp1.push(name);
    result.type = parse(null, n, tr, false, false, false, false, true, tp1);
    var chainingData = n.getMeta("chaining");
    if (chainingData && chainingData.length > 0) {
        if (result.type.metaOfType(meta.ImportedByChain).length == 0) {
            var target = result.type;
            if (result.type.isArray()) {
                var compMeta = result.type.metaOfType(rs.ComponentShouldBeOfType);
                if (compMeta && compMeta.length > 0) {
                    var rt = compMeta[0].value();
                    target = ts.derive("", [rt]);
                    compMeta[0].type = target;
                }
            }
            else if (result.type.isUnion()) {
                var opts = result.type.allOptions();
                if (opts.length > 0) {
                    target = ts.derive("", [opts[0]]);
                    opts[0] = target;
                }
            }
            if (target.metaOfType(meta.ImportedByChain).length == 0) {
                if (!target.isSubTypeOf(ts.UNKNOWN) && ts.InheritedType.isInstance(target)) {
                    target.addSuper(ts.UNKNOWN);
                }
                for (var _i = 0, chainingData_1 = chainingData; _i < chainingData_1.length; _i++) {
                    var cd = chainingData_1[_i];
                    target.addMeta(new meta.ImportedByChain(cd.value));
                }
            }
            if (!target.isSubTypeOf(ts.UNKNOWN)) {
                if (ts.InheritedType.isInstance(target)) {
                    target.addSuper(ts.UNKNOWN);
                }
            }
            for (var _a = 0, chainingData_2 = chainingData; _a < chainingData_2.length; _a++) {
                var cd = chainingData_2[_a];
                target.addMeta(new meta.ImportedByChain(cd.value));
            }
        }
    }
    result.id = name;
    return result;
}
exports.parsePropertyBean = parsePropertyBean;
var TypeProto = /** @class */ (function () {
    function TypeProto() {
    }
    TypeProto.prototype.toJSON = function () {
        var result = {};
        if (this.superTypes && this.superTypes.length > 0) {
            if (this.superTypes.length == 1) {
                result['type'] = this.superTypes[0];
            }
            else {
                result['type'] = this.superTypes;
            }
        }
        if (this.customFacets) {
            this.customFacets.forEach(function (x) { return result[x.facetName()] = x.value(); });
        }
        if (this.annotations) {
            this.annotations.forEach(function (x) { return result["(" + x.facetName() + ")"] = x.value(); });
        }
        if (this.facetDeclarations && this.facetDeclarations.length > 0) {
            var facets = {};
            this.facetDeclarations.forEach(function (x) {
                var nm = x.facetName();
                if (x.isOptional()) {
                    nm = nm + "?";
                }
                var vl = null;
                if (x.type().isAnonymous()) {
                    if (x.type().isEmpty()) {
                        vl = typeToSignature(x.type());
                    }
                    else {
                        vl = toProto(x.type()).toJSON();
                    }
                }
                else {
                    vl = typeToSignature(x.type());
                }
                facets[nm] = vl;
            });
            result['facets'] = facets;
        }
        if (this.properties && this.properties.length > 0) {
            var properties = {};
            this.properties.forEach(function (x) {
                var nm = x.id;
                if (x.optional) {
                    nm = nm + "?";
                }
                if (x.additonal) {
                    nm = "/.*/";
                }
                if (x.regExp) {
                    nm = "/" + x.id + "/";
                }
                var vl = null;
                if (x.type.isAnonymous()) {
                    if (x.type.isEmpty()) {
                        vl = typeToSignature(x.type);
                    }
                    else {
                        vl = toProto(x.type).toJSON();
                    }
                }
                else {
                    vl = typeToSignature(x.type);
                }
                properties[nm] = vl;
            });
            result['properties'] = properties;
        }
        if (this.basicFacets) {
            this.basicFacets.forEach(function (x) {
                result[x.facetName()] = x.value();
            });
        }
        if (Object.keys(result).length == 1 && !this.notAScalar) {
            if (result['type']) {
                return result['type'];
            }
        }
        if (this.additionalProperties !== undefined) {
            result["additionalProperties"] = this.additionalProperties;
        }
        return result;
    };
    return TypeProto;
}());
exports.TypeProto = TypeProto;
function toProto(type) {
    var result = new TypeProto();
    result.name = type.name();
    result.superTypes = type.superTypes().map(function (x) { return typeToSignature(x); });
    result.annotations = [];
    result.customFacets = [];
    result.facetDeclarations = [];
    result.basicFacets = [];
    result.properties = [];
    var pmap = {};
    type.declaredMeta().forEach(function (x) {
        if (x instanceof meta.Annotation) {
            result.annotations.push(x);
        }
        else if (x instanceof meta.CustomFacet) {
            result.customFacets.push(x);
        }
        else if (x instanceof meta.NotScalar) {
            result.notAScalar = true;
        }
        else if (x instanceof metainfo_1.FacetDeclaration) {
            result.facetDeclarations.push(x);
        }
        else {
            if (x instanceof rs.HasProperty) {
                if (pmap.hasOwnProperty(x.value())) {
                    pmap[x.value()].optional = false;
                }
                else {
                    var pbean = new PropertyBean();
                    pbean.optional = false;
                    pbean.id = x.value();
                    pbean.type = ts.ANY;
                    pmap[x.value()] = pbean;
                }
            }
            else if (x instanceof rs.AdditionalPropertyIs) {
                var pbean = new PropertyBean();
                pbean.optional = false;
                pbean.id = ".*";
                pbean.additonal = true;
                pbean.type = x.value();
                pbean.optional = x.isOptional();
                pbean.regExp = true;
                pmap['/.*/'] = pbean;
            }
            else if (rs.MapPropertyIs.isInstance(x)) {
                var pbean = new PropertyBean();
                pbean.optional = false;
                pbean.id = x.regexpValue();
                pbean.regExp = true;
                pbean.type = x.value();
                pbean.optional = x.isOptional();
                pmap[x.regexpValue()] = pbean;
            }
            else if (rs.PropertyIs.isInstance(x)) {
                if (pmap.hasOwnProperty(x.propertyName())) {
                    pmap[x.propertyName()].type = x.value();
                }
                else {
                    var pbean = new PropertyBean();
                    pbean.optional = true;
                    pbean.id = x.propertyName();
                    pbean.type = x.value();
                    pmap[x.propertyName()] = pbean;
                }
            }
            else if (rs.KnownPropertyRestriction.isInstance(x)) {
                result.additionalProperties = x.value();
            }
            else if (meta.DiscriminatorValue.isInstance(x)) {
                if (x.isStrict()) {
                    result.basicFacets.push(x);
                }
            }
            else if (!meta.HasPropertiesFacet.isInstance(x)) {
                result.basicFacets.push(x);
            }
        }
    });
    Object.keys(pmap).forEach(function (x) { return result.properties.push(pmap[x]); });
    return result;
}
exports.toProto = toProto;
/***
 * stores a type to JSON structure
 * @param ts
 */
function storeAsJSON(ts) {
    if (ts instanceof typesystem_1.AbstractType) {
        return toProto(ts).toJSON();
    }
    else {
        return storeTypeCollection(ts);
    }
}
exports.storeAsJSON = storeAsJSON;
function storeTypeCollection(tc) {
    var res = {};
    var types = {};
    tc.types().forEach(function (x) {
        types[x.name()] = storeAsJSON(x);
    });
    if (Object.keys(types).length > 0) {
        res["types"] = types;
    }
    var types = {};
    tc.annotationTypes().forEach(function (x) {
        types[x.name()] = storeAsJSON(x);
    });
    if (Object.keys(types).length > 0) {
        res["annotationTypes"] = types;
    }
    return res;
}
function typeToSignature(t) {
    if (t.isAnonymous()) {
        if (t.isArray()) {
            var ci = t.oneMeta(rs.ComponentShouldBeOfType);
            if (ci) {
                var vl = ci.value();
                if (vl.isAnonymous() && vl.isUnion()) {
                    return "(" + typeToSignature(vl) + ")" + "[]";
                }
                return typeToSignature(vl) + "[]";
            }
        }
        if (t.isUnion()) {
            return t.options().map(function (x) { return typeToSignature(x); }).join(" | ");
        }
        return t.superTypes().map(function (x) { return typeToSignature(x); }).join(" , ");
    }
    return t.name();
}
/**
 * Analogue of type.isSubTypeOf(), but also checks through unions
 * @param potentialSubtype
 * @param potentialSupertype
 */
function isSubtypeOf(potentialSubtype, potentialSupertype) {
    //TODO this algorithm should be moved to type.isSubTypeOf() after release (now leaving it here for safety)
    if (potentialSupertype === ts.ANY ||
        potentialSubtype === potentialSupertype ||
        potentialSubtype.superTypes().some(function (currentSuperType) { return isSubtypeOf(currentSuperType, potentialSupertype); })) {
        return true;
    }
    if (potentialSubtype.isUnion() && potentialSubtype.options) {
        var options = potentialSubtype.options();
        if (options.some(function (option) { return isSubtypeOf(option, potentialSupertype); }))
            return true;
    }
    if (potentialSupertype.isUnion() && potentialSupertype.options) {
        var options = potentialSupertype.options();
        if (options.some(function (option) { return potentialSubtype == option; }))
            return true;
    }
    return false;
}
function testFacetAgainstType(facet, type) {
    var requiredType = facet.requiredType();
    var requiredTypes = facet.requiredTypes();
    if (requiredTypes && requiredTypes.length > 0) {
        return requiredTypes.some(function (currentRType) { return isSubtypeOf(type, currentRType); });
    }
    else {
        return isSubtypeOf(type, requiredType);
    }
}
function appendAnnotations(appendedInfo, childNode) {
    var arr = childNode.kind() == NodeKind.ARRAY ? childNode.children() : [childNode];
    for (var i = 0; i < arr.length; i++) {
        var children = arr[i].children();
        for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
            var ch = children_1[_i];
            var key = ch.key();
            if (key && key.charAt(0) == "(" && key.charAt(key.length - 1) == ")") {
                var aName = key.substring(1, key.length - 1);
                var aInstance = new meta.Annotation(aName, ch.value(), key, false, i);
                aInstance.setOwnerFacet(appendedInfo);
                aInstance._owner = appendedInfo._owner;
                appendedInfo.addAnnotation(aInstance);
            }
        }
    }
}
var checkIfSkipValidation = function (sp, n) {
    if (n.getMeta("skipValidation")) {
        sp.addMeta(new meta.SkipValidation());
    }
};
/**
 * parses a type from a JSON structure
 * @param name
 * @param n
 * @param r
 * @returns {any}
 */
function parse(name, n, r, defaultsToAny, annotation, global, ignoreTypeAttr, isPropertyType, typePath) {
    if (r === void 0) { r = ts.builtInRegistry(); }
    if (typePath.length == 0) {
        typePath = [name];
    }
    //mentioning fragment' uses
    var uses = n.childWithKey("uses");
    if (uses) {
        if (uses.kind() === NodeKind.ARRAY) {
            uses = transformToArray(uses);
        }
        if (uses.kind() === NodeKind.MAP) {
            var col = new TypeCollection(n.path());
            uses.children().forEach(function (c) {
                col.addLibrary(c.key(), parseTypeCollection(c, ts.builtInRegistry()));
            });
            r = new AccumulatingRegistry(null, null, r, col);
        }
    }
    if (n.kind() == NodeKind.SCALAR) {
        var valString = n.value();
        var sp;
        if (valString == null || valString == "Null" || valString == "NULL") {
            sp = ts.STRING;
        }
        else {
            sp = typeExpressions.parseToType("" + valString, r, n, null, isPropertyType);
        }
        if (name == null) {
            checkIfSkipValidation(sp, n);
            return sp;
        }
        var res = ts.derive(name, [sp]);
        var chainingData = n.getMeta("chaining");
        if (sp && chainingData && chainingData.length > 0) {
            var target = res;
            if (sp.isArray()) {
                var compMeta = sp.metaOfType(rs.ComponentShouldBeOfType);
                if (compMeta && compMeta.length > 0) {
                    var rt = compMeta[0].value();
                    target = ts.derive("", [rt]);
                    compMeta[0].type = target;
                }
            }
            else if (sp.isUnion()) {
                var opts = sp.allOptions();
                if (opts.length > 0) {
                    target = ts.derive("", [opts[0]]);
                    opts[0] = target;
                }
            }
            if (target.metaOfType(meta.ImportedByChain).length == 0) {
                if (!target.isSubTypeOf(ts.UNKNOWN)) {
                    target.addSuper(ts.UNKNOWN);
                }
                for (var _i = 0, chainingData_3 = chainingData; _i < chainingData_3.length; _i++) {
                    var cd = chainingData_3[_i];
                    target.addMeta(new meta.ImportedByChain(cd.value));
                }
            }
        }
        if (AccumulatingRegistry.isInstance(r)) {
            res = contributeToAccumulatingRegistry(res, r);
        }
        checkIfSkipValidation(res, n);
        return res;
    }
    if (n.kind() == NodeKind.ARRAY) {
        var supers = [];
        n.children().forEach(function (x) {
            supers.push(typeExpressions.parseToType("" + x.value(), r, n, null, isPropertyType));
        });
        var res = ts.derive(name, supers);
        if (AccumulatingRegistry.isInstance(r)) {
            res = contributeToAccumulatingRegistry(res, r);
        }
        checkIfSkipValidation(res, n);
        return res;
    }
    var superTypes = [];
    var tp = n.childWithKey("type");
    var shAndType = false;
    if (!tp) {
        tp = n.childWithKey("schema");
    }
    else {
        if (n.childWithKey("schema")) {
            shAndType = true;
        }
    }
    var typePropAnnotations = [];
    if (!tp || (!tp.children().length && !tp.value()) || ignoreTypeAttr) {
        if (defaultsToAny) {
            if (n.childWithKey("properties")) {
                superTypes = [ts.OBJECT];
            }
            else {
                superTypes = [ts.ANY];
            }
        }
        else {
            if (n.childWithKey("properties")) {
                superTypes = [ts.OBJECT];
            }
            else {
                superTypes = [ts.STRING];
            }
        }
    }
    else {
        var sAnnotations = [];
        var actual = tp.childWithKey("value");
        if (actual && (actual.kind() == NodeKind.SCALAR || actual.kind() == NodeKind.ARRAY)) {
            sAnnotations = [tp.children().filter(function (x) {
                    var key = x.key();
                    if (!key) {
                        return false;
                    }
                    return key.charAt(0) == "(" && key.charAt(key.length - 1) == ")";
                })];
            tp = actual;
        }
        if (tp.kind() == NodeKind.SCALAR) {
            var valString = tp.value();
            if (valString == null || valString == "Null" || valString == "NULL") {
                superTypes = [ts.STRING];
            }
            else {
                var typeAttributeContentProvider = tp.contentProvider ? tp.contentProvider() : null;
                var st = typeExpressions.parseToType("" + valString, r, n, typeAttributeContentProvider, isPropertyType);
                superTypes = [st];
            }
        }
        else if (tp.kind() == NodeKind.ARRAY) {
            superTypes = tp.children().map(function (x) {
                var actual = x.childWithKey("value");
                if (actual && (actual.kind() == NodeKind.SCALAR || actual.kind() == NodeKind.ARRAY)) {
                    sAnnotations.push(x.children().filter(function (x) {
                        var key = x.key();
                        if (!key) {
                            return false;
                        }
                        return key.charAt(0) == "(" && key.charAt(key.length - 1) == ")";
                    }));
                    x = actual;
                }
                else {
                    sAnnotations.push([]);
                }
                return x.value();
            }).map(function (y) { return typeExpressions.parseToType("" + y, r, n, null, isPropertyType); });
        }
        else if (tp.kind() == NodeKind.MAP) {
            var tp1 = [].concat(typePath);
            tp1.push("#super");
            superTypes = [parse("", tp, r, false, false, false, false, false, tp1)];
        }
        if (sAnnotations.length > 0 && sAnnotations.filter(function (x) { return x.length > 0; }).length > 0) {
            for (var _a = 0, sAnnotations_1 = sAnnotations; _a < sAnnotations_1.length; _a++) {
                var aArr = sAnnotations_1[_a];
                var aiArr = [];
                typePropAnnotations.push(aiArr);
                for (var _b = 0, aArr_1 = aArr; _b < aArr_1.length; _b++) {
                    var ann = aArr_1[_b];
                    var key = ann.key();
                    var aName = key.substring(1, key.length - 1);
                    var aInstance = new meta.Annotation(aName, ann.value(), key);
                    aInstance.setNode(ann);
                    aiArr.push(aInstance);
                }
            }
        }
    }
    for (var _d = 0, superTypes_1 = superTypes; _d < superTypes_1.length; _d++) {
        var st = superTypes_1[_d];
        if (st.superTypes().indexOf(ts.REFERENCE) >= 0) {
            r.get(st.name());
        }
    }
    var result = ts.derive(name, superTypes);
    if (ignoreTypeAttr && tp) {
        result.addMeta(new meta.TypeAttributeValue(tp.value()));
    }
    for (var i = 0; i < typePropAnnotations.length; i++) {
        var aArr1 = typePropAnnotations[i];
        result.addSupertypeAnnotation(aArr1, i);
    }
    if (name != null && name.length > 0 && AccumulatingRegistry.isInstance(r)) {
        result = contributeToAccumulatingRegistry(result, r);
    }
    var actualResult = result;
    var hasfacetsOrOtherStuffDoesNotAllowedInExternals = null;
    n.children().forEach(function (childNode) {
        var key = childNode.key();
        var actual = childNode.childWithKey("value");
        var x = childNode;
        if (key != "example" && key != "discriminatorValue" && actual) {
            x = actual;
        }
        if (!key) {
            return;
        }
        if (key === "type") {
            return;
        }
        if (key === "uses") {
            //FIXME this should be handled depending from parse level
            return;
        }
        if (key === "schema") {
            return;
        }
        if (key == "properties" || key == "additionalProperties") {
            if (result.isSubTypeOf(ts.OBJECT)) {
                return;
            }
        }
        var appendedInfo;
        if (key == "items") {
            if (result.isSubTypeOf(ts.ARRAY)) {
                var componentTypes = [];
                var arr = [x];
                if (x.kind() == NodeKind.ARRAY) {
                    arr = x.children();
                    var err_1 = ts.error(messageRegistry.ITEMS_SHOULD_BE_REFERENCE_OR_INLINE_TYPE, actualResult);
                    err_1.setValidationPath({ name: "items" });
                    result.putExtra(tsInterfaces.PARSE_ERROR, err_1);
                }
                var componentTypes = arr.map(function (y) {
                    var actual = y.childWithKey("value");
                    if (actual && (actual.kind() == NodeKind.SCALAR || actual.kind() == NodeKind.MAP)) {
                        y = actual;
                    }
                    if (y.kind() == NodeKind.SCALAR) {
                        var valString = y.value();
                        if (valString == null || valString == "Null" || valString == "NULL") {
                            return ts.STRING;
                        }
                        else {
                            var res_1 = typeExpressions.parseToType("" + valString, r, n);
                            checkIfSkipValidation(res_1, x);
                            return res_1;
                        }
                    }
                    else if (y.kind() == NodeKind.MAP) {
                        var tp1 = [].concat(typePath);
                        tp1.push("#super");
                        return parse("", y, r, false, false, false, false, false, tp1);
                    }
                });
                var tp = componentTypes.length == 1 ? componentTypes[0] :
                    ts.derive("", componentTypes);
                appendedInfo = new restrictions_1.ComponentShouldBeOfType(tp);
                actualResult.addMeta(appendedInfo);
                actualResult.putExtra(tsInterfaces.HAS_ITEMS, true);
                appendAnnotations(appendedInfo, childNode);
                return appendedInfo;
            }
            else if (!ignoreTypeAttr) {
                var err_2 = ts.error(messageRegistry.ITEMS_DEFINED_FOR_NON_ARRAY, actualResult);
                err_2.setValidationPath({ name: "items" });
                result.putExtra(tsInterfaces.PARSE_ERROR, err_2);
            }
        }
        else {
            if (key == "required" && !isPropertyType) {
                return;
            }
            if (key === "facets") {
                hasfacetsOrOtherStuffDoesNotAllowedInExternals = [key];
                return;
            }
            else if (key == "default" || key == "xml" || key == "required") {
                hasfacetsOrOtherStuffDoesNotAllowedInExternals =
                    hasfacetsOrOtherStuffDoesNotAllowedInExternals ?
                        hasfacetsOrOtherStuffDoesNotAllowedInExternals.concat(key) : [key];
            }
            else if (key.charAt(0) == '(' && key.charAt(key.length - 1) == ')') {
                var a = new meta.Annotation(key.substr(1, key.length - 2), x.value(), key);
                a.setNode(x);
                result.addMeta(a);
                return;
            }
            appendedInfo = facetR.getInstance().buildFacet(key, x.value());
            //TODO remove "format" condition and use this check for all facets
            if (appendedInfo && (key != "format" || testFacetAgainstType(appendedInfo, result))) {
                appendedInfo.setNode(x);
                result.addMeta(appendedInfo);
            }
            else {
                // if (annotation && key === "allowedTargets") {
                //     result.addMeta(new meta.AllowedTargets(x.value()));
                // }
                { //else {
                    var customFacet = new meta.CustomFacet(key, x.value());
                    customFacet.setNode(x);
                    result.addMeta(customFacet);
                }
            }
        }
        if (appendedInfo) {
            appendAnnotations(appendedInfo, childNode);
        }
    });
    if (result.metaOfType(meta.DiscriminatorValue).length == 0 && global) {
        result.addMeta(new meta.DiscriminatorValue(result.name(), false));
    }
    if (result.isSubTypeOf(ts.OBJECT)) {
        var props = n.childWithKey("properties");
        var hasProps = false;
        if (props) {
            result.addMeta(new meta.HasPropertiesFacet());
            if (props.kind() == NodeKind.MAP) {
                props.children().forEach(function (x) {
                    hasProps = true;
                    var tp1 = [].concat(typePath);
                    tp1.push("#props");
                    parsePropertyBean(x, r, tp1).add(result, tp1);
                });
            }
            else {
                var err = ts.error(messageRegistry.PROPERTIES_MAP, actualResult);
                err.setValidationPath({ name: "properties" });
                result.putExtra(tsInterfaces.PARSE_ERROR, err);
            }
        }
        var ap = n.childWithKey("additionalProperties");
        if (ap) {
            var actual = ap.childWithKey("value");
            if (actual) {
                ap = actual;
            }
            if (typeof (ap.value()) == "boolean") {
                result.addMeta(new restrictions_2.KnownPropertyRestriction(ap.value()));
            }
            else {
                var err = ts.error(messageRegistry.ADDITIONAL_PROPERTIES_BOOLEAN, actualResult);
                err.setValidationPath({ name: "additionalProperties" });
                result.putExtra(tsInterfaces.PARSE_ERROR, err);
            }
        }
    }
    var props = n.childWithKey("facets");
    if (props) {
        if (props.kind() == NodeKind.MAP) {
            props.children().forEach(function (x) {
                var tp1 = [].concat(typePath);
                tp1.push("#facets");
                var bean = parsePropertyBean(x, r, tp1);
                result.addMeta(new meta.FacetDeclaration(bean.id, bean.type, bean.optional));
            });
        }
        else {
            var err = ts.error(messageRegistry.FACETS_MAP, actualResult);
            err.setValidationPath({ name: "facets" });
            result.putExtra(tsInterfaces.PARSE_ERROR, err);
        }
    }
    if (result.isAnonymous() && result.isEmpty()) {
        if (result.superTypes().length == 1) {
            var res_2 = result.superTypes()[0];
            checkIfSkipValidation(res_2, n);
            return res_2;
        }
    }
    if (n.kind() != NodeKind.SCALAR) {
        result.addMeta(new meta.NotScalar());
    }
    if (shAndType) {
        actualResult.putExtra(ts.SCHEMA_AND_TYPE, true);
    }
    actualResult.putExtra(ts.GLOBAL, global);
    actualResult.putExtra(ts.SOURCE_EXTRA, n);
    if (hasfacetsOrOtherStuffDoesNotAllowedInExternals) {
        actualResult.putExtra(tsInterfaces.HAS_FACETS, hasfacetsOrOtherStuffDoesNotAllowedInExternals);
    }
    checkIfSkipValidation(actualResult, n);
    if (n.getMeta("acceptAllScalarsAsStrings")) {
        actualResult.addMeta(new meta.AcceptAllScalarsAsStrings());
    }
    return actualResult;
}
exports.parse = parse;
function contributeToAccumulatingRegistry(result, r) {
    var existing;
    var _r = r;
    while (_r) {
        existing = _r.typeMap()[result.name()];
        if (existing) {
            break;
        }
        _r = _r.parent();
    }
    if (existing == null || !existing.isSubTypeOf(ts.REFERENCE)) {
        r.addType(result);
    }
    else if (existing != null && existing.isSubTypeOf(ts.REFERENCE)) {
        existing.patch(result);
        result = existing;
    }
    result.putExtra(tsInterfaces.TOP_LEVEL_EXTRA, true);
    return result;
}
;
//# sourceMappingURL=parse.js.map