"use strict";
var __extends = (this && this.__extends) || (function () {
    var 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 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 });
exports.tsutil = require("./tsutil");
var _ = require("./underscore");
exports.MODEL_CLASS_MODEL_ELEMENT = '$resource-model-element';
exports.MODEL_CLASS_TYPE_DECLARATION = '$type-declaration';
exports.MODEL_CLASS_INTERFACE = '$interface-declaration';
exports.MODEL_CLASS_CLASS_DECLARATION = '$class-declaration';
exports.MODEL_CLASS_ANNOTATION_DECLARATION = '$annotation-declaration';
exports.MODEL_CLASS_ENUM_DECLARATION = '$enum-declaration';
exports.MODEL_CLASS_TYPE_ASSERTION = '$type-assertion';
exports.MODEL_CLASS_API_MODULE = '$api-module';
exports.MODEL_CLASS_UNIVERSE = '$universe';
exports.MODEL_CLASS_MEMBER = '$member';
exports.MODEL_CLASS_UNION_TYPE_REFERENCE = '$union-type-reference';
exports.MODEL_CLASS_SIMPLE_TYPE_REFERENCE = '$simple-type-reference';
exports.MODEL_CLASS_FUNCTION_REFERENCE = '$function-reference';
exports.MODEL_CLASS_ARRAY_REFERENCE = '$array-reference';
exports.MODEL_CLASS_DECLARED_INTERFACE_REFERENCE = '$declared-interface-reference';
exports.MODEL_CLASS_ANY_TYPE_REFERENCE = '$any-type-reference';
exports.MODEL_CLASS_STRUCTURAL_TYPE_REFERENCE = '$structural-type-reference';
exports.MODEL_CLASS_PARAM = '$param';
exports.MODEL_CLASS_STRING_VALUE = '$string-value';
exports.MODEL_CLASS_ARRAY_VALUE = '$array-value';
exports.MODEL_CLASS_API_ELEMENT_DECLARATION = '$api-element-declaration';
exports.MODEL_CLASS_CONSTRUCTOR = '$constructor';
//TODO HIDE Fields from unmanagable modification
//TODO Refine type decl type ref hieararchy a bit more
//TODO add classes, generics, metadata
var TSModelElement = /** @class */ (function () {
    function TSModelElement(parent, config) {
        if (parent === void 0) { parent = exports.Universe; }
        this.meta = {};
        this._annotations = [];
        this.setParent(parent);
        if (config)
            this._config = config;
        this._children = [];
    }
    TSModelElement.prototype.annotations = function () {
        return this._annotations;
    };
    TSModelElement.prototype.patchParent = function (parent) {
        this._parent = parent; //FIXME
    };
    TSModelElement.prototype.isEmpty = function () {
        return this._children.length == 0;
    };
    TSModelElement.prototype.parent = function () { return this._parent; };
    TSModelElement.prototype.children = function () {
        return this._children;
    };
    TSModelElement.prototype.comment = function () { return this._comment; };
    TSModelElement.prototype.root = function () {
        if (this._parent == exports.Universe) {
            return this;
        }
        return this._parent.root();
    };
    TSModelElement.prototype.setParent = function (parent) {
        if (!parent)
            return;
        this._parent = parent;
        this._config = parent._config;
        this._parent.addChild(this);
    };
    TSModelElement.prototype.removeChild = function (child) {
        if (child.parent() == this) {
            this._children = this._children.filter(function (x) { return x != child; });
        }
        child.patchParent(exports.Universe);
    };
    TSModelElement.prototype.addChild = function (child) {
        if (child.parent()) {
            child.parent().removeChild(child);
        }
        child.patchParent(this);
        this._children.push(child);
    };
    TSModelElement.prototype.serializeToString = function (isImpl) {
        if (isImpl === void 0) { isImpl = false; }
        throw new Error("You should override serialize to string always");
    };
    TSModelElement.prototype.modelClass = function () {
        return exports.MODEL_CLASS_MODEL_ELEMENT;
    };
    return TSModelElement;
}());
exports.TSModelElement = TSModelElement;
//TODO It should become an interface
var TSTypeDeclaration = /** @class */ (function (_super) {
    __extends(TSTypeDeclaration, _super);
    function TSTypeDeclaration(parent) {
        if (parent === void 0) { parent = null; }
        var _this = _super.call(this, parent) || this;
        _this.canBeOmmited = function () { return _this.locked ? false : _this.children().every(function (x) { return x.optional; }); };
        _this.locked = false;
        _this.extras = [""];
        return _this;
    }
    TSTypeDeclaration.prototype.addCode = function (code) {
        this.extras.push(code);
    };
    TSTypeDeclaration.prototype.toReference = function () { throw new Error("Implement in subclasses"); };
    TSTypeDeclaration.prototype.hash = function () { return this.serializeToString(); };
    TSTypeDeclaration.prototype.isFunctor = function () {
        return this.children().some(function (x) { return x.isAnonymousFunction(); });
    };
    TSTypeDeclaration.prototype.getFunctor = function () {
        return _.find(this.children(), function (x) { return x.isAnonymousFunction(); });
    };
    TSTypeDeclaration.prototype.visit = function (v) {
        if (v.startTypeDeclaration(this)) {
            this.children().forEach(function (x, i, arr) {
                x.visit(v);
                if (i != arr.length - 1)
                    v.betweenElements();
            });
            v.endTypeDeclaration(this);
        }
    };
    TSTypeDeclaration.prototype.modelClass = function () {
        return exports.MODEL_CLASS_TYPE_DECLARATION;
    };
    return TSTypeDeclaration;
}(TSModelElement));
exports.TSTypeDeclaration = TSTypeDeclaration;
var TSInterface = /** @class */ (function (_super) {
    __extends(TSInterface, _super);
    function TSInterface(p, name) {
        var _this = _super.call(this, p) || this;
        _this.extends = [];
        _this.implements = [];
        _this.name = name;
        return _this;
    }
    TSInterface.prototype.typeSignature = function () {
        var modelName = this.name;
        if (this.typeParameters && this.typeParameters.length > 0) {
            modelName += "<" + this.typeParameters.join(',') + ">";
        }
        return modelName;
    };
    TSInterface.prototype.hash = function () {
        var _this = this;
        return this.children().filter(function (x) { return !x.isPrivate; }).map(function (x) { return "\n" + x.serializeToString(_this.isImpl()) + "\n"; }).join('');
    };
    TSInterface.prototype.toReference = function () { return new TSDeclaredInterfaceReference(exports.Universe, this.name, this); };
    TSInterface.prototype.decl = function () {
        return "interface";
    };
    TSInterface.prototype.serializeToString = function () {
        var body = this.hash();
        return this.insertComment() + "export " + this.decl() + " " + this.name.concat(this.extendsString() + this.implementsString()) +
            "{" + this.extras.join("\n") + body + "}\n";
    };
    TSInterface.prototype.extendsString = function () {
        if (this.extends.length > 0) {
            return " extends " + this.extends.map(function (x) { return x.serializeToString(); }).join(",");
        }
        return "";
    };
    TSInterface.prototype.implementsString = function () {
        if (this.implements.length > 0) {
            return " implements " + this.implements.map(function (x) { return x.serializeToString(); }).join(",");
        }
        return "";
    };
    TSInterface.prototype.insertComment = function () {
        if (!this._comment) {
            return '';
        }
        return formattedComment(this._comment, 0);
    };
    TSInterface.prototype.modelClass = function () {
        return exports.MODEL_CLASS_INTERFACE;
    };
    TSInterface.prototype.isImpl = function () {
        return false;
    };
    return TSInterface;
}(TSTypeDeclaration));
exports.TSInterface = TSInterface;
//TODO INCORRECT INHERITANCE CHAIN
var TSClassDecl = /** @class */ (function (_super) {
    __extends(TSClassDecl, _super);
    function TSClassDecl() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TSClassDecl.prototype.decl = function () {
        return "class";
    };
    TSClassDecl.prototype.modelClass = function () {
        return exports.MODEL_CLASS_CLASS_DECLARATION;
    };
    TSClassDecl.prototype.isImpl = function () {
        return true;
    };
    return TSClassDecl;
}(TSInterface));
exports.TSClassDecl = TSClassDecl;
var TSAnnotationDecl = /** @class */ (function (_super) {
    __extends(TSAnnotationDecl, _super);
    function TSAnnotationDecl() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TSAnnotationDecl.prototype.decl = function () {
        return "annotation";
    };
    TSAnnotationDecl.prototype.modelClass = function () {
        return exports.MODEL_CLASS_ANNOTATION_DECLARATION;
    };
    TSAnnotationDecl.prototype.toReference = function () { return new TSDeclaredAnnotationReference(exports.Universe, this.name, this); };
    return TSAnnotationDecl;
}(TSInterface));
exports.TSAnnotationDecl = TSAnnotationDecl;
var TSEnumDecl = /** @class */ (function (_super) {
    __extends(TSEnumDecl, _super);
    function TSEnumDecl() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TSEnumDecl.prototype.decl = function () {
        return "enum";
    };
    TSEnumDecl.prototype.modelClass = function () {
        return exports.MODEL_CLASS_ENUM_DECLARATION;
    };
    return TSEnumDecl;
}(TSInterface));
exports.TSEnumDecl = TSEnumDecl;
var TSTypeAssertion = /** @class */ (function (_super) {
    __extends(TSTypeAssertion, _super);
    function TSTypeAssertion(p, _name, _ref) {
        var _this = _super.call(this, p) || this;
        _this._name = _name;
        _this._ref = _ref;
        return _this;
    }
    TSTypeAssertion.prototype.toReference = function () { return new TSSimpleTypeReference(exports.Universe, this._name); };
    TSTypeAssertion.prototype.serializeToString = function () {
        return "export type " + this._name + "=" + this._ref.serializeToString() + "\n";
    };
    TSTypeAssertion.prototype.ref = function () { return this._ref; };
    TSTypeAssertion.prototype.name = function () { return this._name; };
    TSTypeAssertion.prototype.modelClass = function () {
        return exports.MODEL_CLASS_TYPE_ASSERTION;
    };
    return TSTypeAssertion;
}(TSTypeDeclaration));
exports.TSTypeAssertion = TSTypeAssertion;
var TSUniverse = /** @class */ (function (_super) {
    __extends(TSUniverse, _super);
    function TSUniverse() {
        // super(this);
        //commented this call out as the new version of TS compiler does not eat this. For a reason.
        var _this = 
        //so now first calling super with a fake parent
        _super.call(this, new TSModelElement()) || this;
        //and now setting a real parent as self
        //(not sure why we do this originally, I would put null here, creating a cycle doesnt look good)
        _this.setParent(_this);
        return _this;
    }
    TSUniverse.prototype.addChild = function (child) {
    };
    TSUniverse.prototype.setConfig = function (cfg) { this._config = cfg; };
    TSUniverse.prototype.getConfig = function () { return this._config; };
    TSUniverse.prototype.modelClass = function () {
        return exports.MODEL_CLASS_UNIVERSE;
    };
    return TSUniverse;
}(TSModelElement));
exports.TSUniverse = TSUniverse;
exports.Universe = new TSUniverse();
var TSAPIModule = /** @class */ (function (_super) {
    __extends(TSAPIModule, _super);
    function TSAPIModule() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TSAPIModule.prototype.getInterface = function (nm) {
        return _.find(this.children(), function (x) { return x.name == nm; });
    };
    TSAPIModule.prototype.serializeToString = function () {
        var typeMap = {};
        this.children().forEach(function (x) { return typeMap[x.name] = x; });
        var covered = {};
        var sorted = [];
        var append = function (t) {
            if (covered[t.name]) {
                return;
            }
            covered[t.name] = true;
            var refs = t.extends;
            refs.forEach(function (ref) {
                if (ref instanceof TSSimpleTypeReference) {
                    var name = ref.name;
                    var st = typeMap[name];
                    if (st) {
                        append(st);
                    }
                }
            });
            sorted.push(t);
        };
        this.children().forEach(function (x) { return append(x); });
        return sorted.map(function (x) { return x.serializeToString(); }).join("\n");
    };
    TSAPIModule.prototype.modelClass = function () {
        return exports.MODEL_CLASS_API_MODULE;
    };
    return TSAPIModule;
}(TSModelElement));
exports.TSAPIModule = TSAPIModule;
var TSMember = /** @class */ (function (_super) {
    __extends(TSMember, _super);
    function TSMember() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    TSMember.prototype.modelClass = function () {
        return exports.MODEL_CLASS_MEMBER;
    };
    return TSMember;
}(TSModelElement));
exports.TSMember = TSMember;
var TSUnionTypeReference = /** @class */ (function (_super) {
    __extends(TSUnionTypeReference, _super);
    function TSUnionTypeReference() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.array = function () { return false; };
        return _this;
    }
    TSUnionTypeReference.prototype.getFunctor = function () {
        return null;
    };
    //TODO FIXIT FIX IT WITH MIX IN
    TSUnionTypeReference.prototype.union = function (q) {
        var map = {};
        this.children().filter(function (x) { return x instanceof TSSimpleTypeReference; })
            .forEach(function (x) { return map[x.name] = true; });
        var gotNew = false;
        var flat = flattenUnionType(q);
        flat.forEach(function (x) {
            if (x instanceof TSSimpleTypeReference) {
                gotNew = gotNew || !map[q.name];
            }
            else {
                gotNew = true;
            }
        });
        if (!gotNew) {
            return this;
        }
        var r = new TSUnionTypeReference();
        this.children().forEach(function (x) { return r.addChild(x); });
        flat.forEach(function (x) { return r.addChild(x); });
        return r;
    };
    TSUnionTypeReference.prototype.isFunctor = function () {
        return false;
    };
    TSUnionTypeReference.prototype.canBeOmmited = function () {
        return false;
    };
    TSUnionTypeReference.prototype.serializeToString = function () {
        return this.children().map(function (x) { return x.serializeToString(); }).join(" | ");
    };
    TSUnionTypeReference.prototype.removeChild = function (child) { };
    TSUnionTypeReference.prototype.addChild = function (child) {
        this.children().push(child);
    };
    TSUnionTypeReference.prototype.modelClass = function () {
        return exports.MODEL_CLASS_UNION_TYPE_REFERENCE;
    };
    return TSUnionTypeReference;
}(TSModelElement));
exports.TSUnionTypeReference = TSUnionTypeReference;
var TSSimpleTypeReference = /** @class */ (function (_super) {
    __extends(TSSimpleTypeReference, _super);
    function TSSimpleTypeReference(p, tn) {
        var _this = _super.call(this, p) || this;
        _this.array = function () { return false; };
        _this.genericStr = function () { return _this.typeParameters && _this.typeParameters.length > 0
            ? '<' + _this.typeParameters.map(function (p) { return p.serializeToString(); }).join(',') + '>'
            : ''; };
        _this.name = tn;
        return _this;
    }
    TSSimpleTypeReference.prototype.isEmpty = function () {
        return false;
    };
    TSSimpleTypeReference.prototype.getFunctor = function () {
        return null;
    };
    TSSimpleTypeReference.prototype.canBeOmmited = function () {
        return false;
    };
    TSSimpleTypeReference.prototype.isFunctor = function () {
        return false;
    };
    TSSimpleTypeReference.prototype.union = function (q) {
        var _this = this;
        var flat = flattenUnionType(q);
        var gotThis = false;
        flat.filter(function (x) { return x instanceof TSSimpleTypeReference; }).map(function (x) { return x; })
            .forEach(function (x) { return gotThis = gotThis || (x.name == _this.name); });
        if (gotThis) {
            return q;
        }
        var r = new TSUnionTypeReference();
        r.addChild(this);
        flat.forEach(function (x) { return r.addChild(x); });
        return r;
    };
    TSSimpleTypeReference.prototype.serializeToString = function () {
        return this.name + this.genericStr();
    };
    TSSimpleTypeReference.prototype.modelClass = function () {
        return exports.MODEL_CLASS_SIMPLE_TYPE_REFERENCE;
    };
    return TSSimpleTypeReference;
}(TSModelElement));
exports.TSSimpleTypeReference = TSSimpleTypeReference;
var TSFunctionReference = /** @class */ (function (_super) {
    __extends(TSFunctionReference, _super);
    function TSFunctionReference(p) {
        var _this = _super.call(this, p) || this;
        _this.rangeType = new AnyType();
        _this.parameters = [];
        _this.array = function () { return false; };
        _this.paramStr = function (appendDefault) {
            if (appendDefault === void 0) { appendDefault = false; }
            return '(' + _this.parameters
                .filter(function (x) { return !x.isEmpty(); })
                .map(function (p) { return p.serializeToString(appendDefault); })
                .join(', ') + ')';
        };
        return _this;
    }
    TSFunctionReference.prototype.isEmpty = function () {
        return false;
    };
    TSFunctionReference.prototype.getFunctor = function () {
        return null;
    };
    TSFunctionReference.prototype.canBeOmmited = function () {
        return false;
    };
    TSFunctionReference.prototype.isFunctor = function () {
        return true;
    };
    TSFunctionReference.prototype.union = function (q) {
        var r = new TSUnionTypeReference();
        r.addChild(this);
        r.addChild(q);
        return r;
    };
    TSFunctionReference.prototype.serializeToString = function () {
        return this.paramStr() + '=>' + this.rangeType.serializeToString();
    };
    TSFunctionReference.prototype.modelClass = function () {
        return exports.MODEL_CLASS_FUNCTION_REFERENCE;
    };
    return TSFunctionReference;
}(TSModelElement));
exports.TSFunctionReference = TSFunctionReference;
var TSArrayReference = /** @class */ (function (_super) {
    __extends(TSArrayReference, _super);
    function TSArrayReference(componentType) {
        if (componentType === void 0) { componentType = new AnyType(); }
        var _this = _super.call(this, exports.Universe) || this;
        _this.array = function () { return true; };
        _this.componentType = componentType;
        return _this;
    }
    TSArrayReference.prototype.isEmpty = function () {
        return this.componentType ? true : false;
    };
    TSArrayReference.prototype.getFunctor = function () {
        return this.componentType.getFunctor();
    };
    TSArrayReference.prototype.canBeOmmited = function () {
        return false;
    };
    TSArrayReference.prototype.isFunctor = function () {
        return this.componentType.isFunctor();
    };
    TSArrayReference.prototype.union = function (q) {
        var r = new TSUnionTypeReference();
        r.addChild(this);
        r.addChild(q);
        return r;
    };
    TSArrayReference.prototype.serializeToString = function () {
        return this.componentType.serializeToString() + '[]';
    };
    TSArrayReference.prototype.modelClass = function () {
        return exports.MODEL_CLASS_ARRAY_REFERENCE;
    };
    return TSArrayReference;
}(TSModelElement));
exports.TSArrayReference = TSArrayReference;
var TSDeclaredInterfaceReference = /** @class */ (function (_super) {
    __extends(TSDeclaredInterfaceReference, _super);
    function TSDeclaredInterfaceReference(p, tn, _data) {
        var _this = _super.call(this, p, tn) || this;
        _this._data = _data;
        return _this;
    }
    TSDeclaredInterfaceReference.prototype.isEmpty = function () {
        return false;
    };
    TSDeclaredInterfaceReference.prototype.getFunctor = function () {
        return null;
    };
    TSDeclaredInterfaceReference.prototype.canBeOmmited = function () {
        return false;
    };
    TSDeclaredInterfaceReference.prototype.getOriginal = function () {
        return this._data;
    };
    TSDeclaredInterfaceReference.prototype.modelClass = function () {
        return exports.MODEL_CLASS_DECLARED_INTERFACE_REFERENCE;
    };
    return TSDeclaredInterfaceReference;
}(TSSimpleTypeReference));
exports.TSDeclaredInterfaceReference = TSDeclaredInterfaceReference;
var TSAnnotationReference = /** @class */ (function (_super) {
    __extends(TSAnnotationReference, _super);
    function TSAnnotationReference(p, tn, values) {
        if (values === void 0) { values = {}; }
        var _this = _super.call(this, p, tn) || this;
        _this.values = values;
        return _this;
    }
    TSAnnotationReference.prototype.value = function (key) {
        if (key === void 0) { key = 'value'; }
        return this.values[key];
    };
    return TSAnnotationReference;
}(TSSimpleTypeReference));
exports.TSAnnotationReference = TSAnnotationReference;
var TSDeclaredAnnotationReference = /** @class */ (function (_super) {
    __extends(TSDeclaredAnnotationReference, _super);
    function TSDeclaredAnnotationReference() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.values = {};
        return _this;
    }
    TSDeclaredAnnotationReference.prototype.value = function (key) {
        if (key === void 0) { key = 'value'; }
        return this.values[key];
    };
    return TSDeclaredAnnotationReference;
}(TSDeclaredInterfaceReference));
exports.TSDeclaredAnnotationReference = TSDeclaredAnnotationReference;
var AnyType = /** @class */ (function (_super) {
    __extends(AnyType, _super);
    function AnyType(nm) {
        if (nm === void 0) { nm = "any"; }
        return _super.call(this, exports.Universe, nm) || this;
    }
    AnyType.prototype.union = function (q) {
        return q;
    };
    AnyType.prototype.modelClass = function () {
        return exports.MODEL_CLASS_ANY_TYPE_REFERENCE;
    };
    return AnyType;
}(TSSimpleTypeReference));
exports.AnyType = AnyType;
var TSStructuralTypeReference = /** @class */ (function (_super) {
    __extends(TSStructuralTypeReference, _super);
    function TSStructuralTypeReference(parent) {
        if (parent === void 0) { parent = exports.Universe; }
        var _this = _super.call(this, parent) || this;
        _this.array = function () { return false; };
        _this.canBeOmmited = function () { return _this.locked ? false : _this.children().every(function (x) { return x.optional; }); };
        return _this;
    }
    TSStructuralTypeReference.prototype.visitReturnType = function (v) {
        //v.visitStructuralReturn(this);
        this.visit(v);
    };
    TSStructuralTypeReference.prototype.toReference = function () { return this; };
    TSStructuralTypeReference.prototype.union = function (q) {
        var r = new TSUnionTypeReference();
        r.addChild(this);
        r.addChild(q);
        return r;
    };
    TSStructuralTypeReference.prototype.serializeToString = function () {
        var body = this.children().map(function (x) { return "\n" + x.serializeToString() + "\n"; }).join('');
        return "{" + body + "}";
    };
    TSStructuralTypeReference.prototype.modelClass = function () {
        return exports.MODEL_CLASS_STRUCTURAL_TYPE_REFERENCE;
    };
    return TSStructuralTypeReference;
}(TSTypeDeclaration));
exports.TSStructuralTypeReference = TSStructuralTypeReference;
var ParamLocation;
(function (ParamLocation) {
    ParamLocation[ParamLocation["URI"] = 0] = "URI";
    ParamLocation[ParamLocation["BODY"] = 1] = "BODY";
    ParamLocation[ParamLocation["OPTIONS"] = 2] = "OPTIONS";
    ParamLocation[ParamLocation["OTHER"] = 3] = "OTHER";
})(ParamLocation = exports.ParamLocation || (exports.ParamLocation = {}));
var Param = /** @class */ (function (_super) {
    __extends(Param, _super);
    function Param(p, nm, location, tp, defaultValue) {
        if (tp === void 0) { tp = new TSSimpleTypeReference(exports.Universe, "string"); }
        var _this = _super.call(this, p) || this;
        _this.name = nm;
        _this.ptype = tp;
        _this.location = location;
        _this.defaultValue = defaultValue;
        return _this;
    }
    Param.prototype.isEmpty = function () {
        return this.ptype.isEmpty();
    };
    Param.prototype.serializeToString = function (appendDefault) {
        if (appendDefault === void 0) { appendDefault = false; }
        //return this.name + (this.optional ? "?" : "") + ":" + this.ptype.serializeToString() + (this.ptype.canBeOmmited() ? "?" : "");
        return this.name + (this.optional || ((this.defaultValue != null) && !appendDefault) ? "?" : "")
            + (":" + this.ptype.serializeToString() + (this.ptype.canBeOmmited() ? "?" : ""))
            + (appendDefault && (this.defaultValue != null) ? '=' + JSON.stringify(this.defaultValue) : '');
    };
    Param.prototype.modelClass = function () {
        return exports.MODEL_CLASS_PARAM;
    };
    return Param;
}(TSModelElement));
exports.Param = Param;
var StringValue = /** @class */ (function (_super) {
    __extends(StringValue, _super);
    function StringValue(_value) {
        var _this = _super.call(this) || this;
        _this._value = _value;
        return _this;
    }
    StringValue.prototype.value = function () {
        return this._value;
    };
    StringValue.prototype.serializeToString = function () {
        return "\"" + this._value + "\"";
    };
    StringValue.prototype.modelClass = function () {
        return exports.MODEL_CLASS_STRING_VALUE;
    };
    return StringValue;
}(TSMember));
exports.StringValue = StringValue;
var ArrayValue = /** @class */ (function (_super) {
    __extends(ArrayValue, _super);
    function ArrayValue(_values) {
        var _this = _super.call(this) || this;
        _this._values = _values;
        return _this;
    }
    ArrayValue.prototype.value = function () {
        return this.serializeToString();
    };
    ArrayValue.prototype.serializeToString = function () {
        return "[ " + this._values.map(function (x) { return x.value(); }).join(', ') + " ]";
    };
    ArrayValue.prototype.values = function () {
        return this._values;
    };
    ArrayValue.prototype.modelClass = function () {
        return exports.MODEL_CLASS_ARRAY_VALUE;
    };
    return ArrayValue;
}(TSMember));
exports.ArrayValue = ArrayValue;
var TSAPIElementDeclaration = /** @class */ (function (_super) {
    __extends(TSAPIElementDeclaration, _super);
    function TSAPIElementDeclaration(p, name) {
        var _this = _super.call(this, p) || this;
        _this.rangeType = new AnyType();
        _this.value = null;
        _this.isStatic = false;
        _this.paramStr = function (appendDefault) {
            if (appendDefault === void 0) { appendDefault = false; }
            return '( ' + _this.parameters
                .filter(function (x) { return !x.isEmpty(); })
                .map(function (p) { return _this.serializeParam(p, appendDefault); })
                .join(',') + ' )';
        };
        _this.serializeParam = function (p, appendDefault) { return p.serializeToString(appendDefault); };
        _this.isFunction = function () { return _this.parameters.length != 0 || _this.isFunc; };
        _this.isAnonymousFunction = function () { return _this.isFunction() && _this.name === ''; };
        _this.returnStr = function () { return _this.rangeType ? ':' + _this.rangeType.serializeToString() : ''; };
        _this.name = name;
        _this.parameters = [];
        _this.rangeType = null;
        _this.optional = false;
        return _this;
    }
    TSAPIElementDeclaration.prototype.visit = function (v) {
        if (v.startVisitElement(this)) {
            if (this.rangeType) {
                if (this.rangeType instanceof TSStructuralTypeReference && !this.isInterfaceMethodWithBody()) {
                    this.rangeType.visitReturnType(v);
                }
            }
            v.endVisitElement(this);
        }
    };
    TSAPIElementDeclaration.prototype.commentCode = function () {
        if (this.comment()) {
            return formattedComment(this._comment, 2);
        }
        return '';
    };
    TSAPIElementDeclaration.prototype.serializeToString = function (isImpl) {
        if (isImpl === void 0) { isImpl = false; }
        var x = (this.isPrivate ? 'private ' : '')
            + (this.isStatic ? 'static ' : '')
            + this.escapeDot(this.name)
            + (this.optional ? "?" : "")
            + (this.isFunction() ? this.paramStr(isImpl) : "")
            + this.returnStr();
        if (this.value) {
            x += '=' + this.value.value();
        }
        return this.commentCode() + x + (this.isFunction() && this.isInterfaceMethodWithBody() ? '' : this.body());
    };
    TSAPIElementDeclaration.prototype.body = function () {
        if (this._body == null)
            return "";
        return "{" + this._body + "}";
    };
    TSAPIElementDeclaration.prototype.escapeDot = function (name) {
        var escaped = exports.tsutil.escapeTypescriptPropertyName(name);
        return escaped;
    };
    TSAPIElementDeclaration.prototype.isInterfaceMethodWithBody = function () { return false; };
    TSAPIElementDeclaration.prototype.modelClass = function () {
        return exports.MODEL_CLASS_API_ELEMENT_DECLARATION;
    };
    return TSAPIElementDeclaration;
}(TSMember));
exports.TSAPIElementDeclaration = TSAPIElementDeclaration;
var TSConstructor = /** @class */ (function (_super) {
    __extends(TSConstructor, _super);
    function TSConstructor(p) {
        var _this = _super.call(this, p, 'constructor') || this;
        _this.serializeParam = function (p, appendDefault) { return 'protected ' + p.serializeToString(appendDefault); };
        return _this;
    }
    TSConstructor.prototype.modelClass = function () {
        return exports.MODEL_CLASS_CONSTRUCTOR;
    };
    return TSConstructor;
}(TSAPIElementDeclaration));
exports.TSConstructor = TSConstructor;
function flattenUnionType(ref) {
    var _this = this;
    if (!(ref instanceof TSUnionTypeReference)) {
        return [ref];
    }
    var map = {};
    var arr = [];
    ref.children().forEach(function (x) { return _this.flattenUnionType(x).forEach(function (y) {
        if (y instanceof TSSimpleTypeReference) {
            var st = y;
            var name = st.name;
            map[name] = st;
        }
        else {
            arr.push(y);
        }
    }); });
    return arr.concat(_.sortBy(Object.keys(map).map(function (x) { return map[x]; }), 'name'));
}
function formattedComment(str, indents) {
    var shift = '';
    for (var i = 0; i < indents; i++) {
        shift += '    ';
    }
    return "\n" + shift + "/**\n" + str.replace(/\*\//g, '* /').replace(/\r\n/g, '\n').split('\n').map(function (x) { return shift + ' * ' + x.trim(); }).join('\n') + "\n" + shift + " **/\n";
}
//# sourceMappingURL=TSDeclModel.js.map