"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var ts = require("./typesystem");
var tsInterfaces = require("./typesystem-interfaces");
var nt = require("./nominal-types");
var parse = require("./parse");
var restrictions = require("./restrictions");
var reg = require("./facetRegistry");
var metainfo = require("./metainfo");
var _ = require("underscore");
var NOMINAL = "nominal";
var pc;
function setPropertyConstructor(p) {
    pc = p;
}
exports.setPropertyConstructor = setPropertyConstructor;
function toNominal(t, callback, customizer) {
    if (customizer === void 0) { customizer = null; }
    var vs = null;
    if (t.getExtra(NOMINAL)) {
        return t.getExtra(NOMINAL);
    }
    //if (t.isEmpty()){
    //    if (t.superTypes().length==1){
    //        return toNominal(t.superTypes()[0],callback);
    //    }
    //}
    if (!t) {
        var res = callback("any");
        if (!res) {
            vs = new nt.StructuredType(t.name());
        }
    }
    if (t.isBuiltin()) {
        var s = (t.name() != "any" && t.name() != "array") ? callback(t.name()) : null;
        if (!s) {
            if (t.isScalar()) {
                vs = new nt.ValueType(t.name(), null);
            }
            else {
                vs = new nt.StructuredType(t.name());
            }
        }
        else {
            vs = s;
        }
    }
    else {
        if (t.isObject()) {
            vs = new nt.StructuredType(t.name(), null);
        }
        else if (t.isArray()) {
            var ar = new nt.Array(t.name(), null);
            vs = ar;
            t.putExtra(NOMINAL, vs);
            var cm = t.oneMeta(restrictions.ComponentShouldBeOfType);
            var r = cm ? cm.value() : ts.ANY;
            ar.setComponent(toNominal(r, callback));
        }
        else if (t instanceof ts.UnionType) {
            var ut = new nt.Union(t.name(), null);
            if (t.superTypes().length == 0) {
                ut._superTypes.push(toNominal(ts.UNION, callback, customizer));
            }
            t.putExtra(NOMINAL, ut);
            t.options().forEach(function (x) {
                if (ut.left == null) {
                    ut.left = toNominal(x, callback);
                }
                else if (ut.right == null) {
                    ut.right = toNominal(x, callback);
                }
                else {
                    var nu = new nt.Union(t.name(), null);
                    nu.left = ut.right;
                    nu.right = toNominal(x, callback);
                    ut.right = nu;
                }
            });
            vs = ut;
        }
        else if (t.isScalar()) {
            vs = new nt.ValueType(t.name(), null);
        }
        else if (t instanceof ts.ExternalType) {
            var e = t;
            var et = new nt.ExternalType(e.name());
            et.schemaString = e.schema();
            vs = et;
        }
    }
    if (!vs) {
        vs = new nt.StructuredType(t.name());
    }
    t.superTypes().forEach(function (x) {
        var mn = toNominal(x, callback);
        if (x.isBuiltin()) {
            vs._superTypes.push(mn);
        }
        else {
            vs.addSuperType(mn);
        }
    });
    if (t.isEmpty()) {
        if (t.isArray() && t.superTypes().length == 1 && t.superTypes()[0].isAnonymous()) {
            var q = vs.superTypes()[0];
            q.setName(t.name());
            q._subTypes = q._subTypes.filter(function (x) { return x != vs; });
            vs = q;
        }
        if (t.isUnion() && t.superTypes().length == 1 && t.superTypes()[0].isAnonymous()) {
            var q = vs.superTypes()[0];
            q.setName(t.name());
            q._subTypes = q._subTypes.filter(function (x) { return x != vs; });
            vs = q;
        }
    }
    t.putExtra(NOMINAL, vs);
    var proto = parse.toProto(t);
    proto.properties.forEach(function (x) {
        var propName = x.regExp ? "/" + x.id + "/" : x.id;
        var prop = pc ? pc(propName) : new nt.Property(propName);
        prop.withDomain(vs);
        prop.withRange(toNominal(x.type, callback));
        if (!x.optional) {
            prop.withRequired(true);
        }
        if (x.regExp) {
            prop.withKeyRegexp(propName);
        }
    });
    proto.facetDeclarations.filter(function (x) { return !x.isBuiltIn(); }).forEach(function (x) {
        var prop = pc ? pc(x.facetName()) : new nt.Property(x.facetName());
        prop.withRange(toNominal(x.type(), callback));
        vs.addFacet(prop);
    });
    t.customFacets().forEach(function (x) {
        vs.fixFacet(x.facetName(), x.value());
    });
    var skipped = {
        "example": true,
        "examples": true
    };
    var basicFacets = t.meta()
        .filter(function (x) {
        if (!(x instanceof metainfo.Discriminator) && !(x instanceof metainfo.DiscriminatorValue)) {
            if (!(x instanceof restrictions.FacetRestriction)
                && !(x instanceof metainfo.MetaInfo)
                && !(x instanceof restrictions.KnownPropertyRestriction)) {
                return false;
            }
            if (x instanceof metainfo.FacetDeclaration || x instanceof metainfo.CustomFacet) {
                return false;
            }
        }
        var rt = x.requiredType();
        var trArr = rt.isUnion() ? rt.allOptions() : [rt];
        if (!_.some(trArr, function (y) { return t.isSubTypeOf(y); })) {
            return false;
        }
        var n = x.facetName();
        if (skipped[n]) {
            return false;
        }
        if (n == "discriminatorValue") {
            return x.isStrict();
        }
        if (n == "allowedTargets") {
            return true;
        }
        return reg.getInstance().facetPrototypeWithName(n) != null;
    });
    for (var _i = 0, basicFacets_1 = basicFacets; _i < basicFacets_1.length; _i++) {
        var x = basicFacets_1[_i];
        var n = x.facetName();
        if (n == "closed") {
            n = "additionalProperties";
        }
        vs.fixFacet(n, x.value(), true);
    }
    vs.addAdapter(t);
    if (!t.isBuiltin()) {
        vs.putExtra(tsInterfaces.USER_DEFINED_EXTRA, true);
    }
    if (t.isEmpty()) {
        vs.addAdapter(new nt.Empty());
    }
    vs._validator = getValidator(t);
    if (t.isBuiltin()) {
        vs.buildIn = true;
    }
    else {
        t.subTypes().forEach(function (x) {
            var ns = toNominal(x, callback, customizer);
        });
    }
    return vs;
}
exports.toNominal = toNominal;
function getValidator(t) {
    return function (arg) {
        return t.validate(arg, false).getErrors();
    };
}
//# sourceMappingURL=nominals.js.map