"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var RamlWrapperImpl = require("../artifacts/raml08parser");
var factory = require("../artifacts/raml08factory");
var core = require("./parserCore");
var hl = require("../highLevelAST");
var hlimpl = require("../highLevelImpl");
var universes = require("../tools/universe");
var expanderLL = require("../ast.core/expanderLL");
var lowLevelProxy = require("../ast.core/LowLevelASTProxy");
var linter = require("../ast.core/linter");
var util = require("../../util/index");
var search = require("../../search/search-interface");
var universeHelpers = require("../tools/universeHelpers");
var jsyaml = require("../jsyaml/jsyaml2lowLevel");
var path = require("path");
var _ = require("underscore");
//export function resolveType(p:RamlWrapper.TypeDeclaration):hl.ITypeDefinition{
//    var tpe=typeexpression.typeFromNode(p.highLevel());
//    return tpe.toRuntime();
//}
var messageRegistry = require("../../../resources/errorMessages");
function load(pth) {
    var m = new jsyaml.Project(path.dirname(pth));
    var unit = m.unit(path.basename(pth));
    if (unit) {
        if (unit.isRAMLUnit()) {
            return hlimpl.fromUnit(unit).wrapperNode();
        }
    }
    return null;
}
exports.load = load;
/**
 * __$helperMethod__
 * Equivalent API with traits and resource types expanded
 * __$meta__={"name":"expand"}
 **/
function expandTraitsAndResourceTypes(api) {
    var lowLevelNode = api.highLevel().lowLevel();
    if (lowLevelProxy.LowLevelProxyNode.isInstance(lowLevelNode)) {
        return api;
    }
    return expanderLL.expandTraitsAndResourceTypes(api);
}
exports.expandTraitsAndResourceTypes = expandTraitsAndResourceTypes;
//__$helperMethod__ Path relative to API root
function completeRelativeUri(res) {
    var uri = '';
    var parent = res;
    do {
        res = parent; //(parent instanceof RamlWrapper.ResourceImpl) ? <RamlWrapper.Resource>parent : null;
        uri = res.relativeUri().value() + uri;
        parent = res.parent();
    } while (parent.definition().key().name == universes.Universe08.Resource.name);
    return uri;
}
exports.completeRelativeUri = completeRelativeUri;
//__$helperMethod__ baseUri of owning Api concatenated with completeRelativeUri
function absoluteUri(res) {
    var uri = '';
    var parent = res;
    do {
        res = parent; //(parent instanceof RamlWrapper.ResourceImpl) ? <RamlWrapper.Resource>parent : null;
        uri = res.relativeUri().value() + uri;
        parent = res.parent();
    } while (parent.definition().key().name == universes.Universe08.Resource.name);
    var buri = parent.baseUri();
    var base = buri ? buri.value() : "";
    base = base ? base : '';
    if (res) {
        base = base.replace(/\/+$/, "");
    }
    uri = base + uri;
    return uri;
}
exports.absoluteUri = absoluteUri;
function qName(c) {
    return hlimpl.qName(c.highLevel(), c.highLevel().root());
}
exports.qName = qName;
/**
 * __$helperMethod__
 * __$meta__{"name":"traits","override":true}
 **/
function traitsPrimary(a) {
    return allTraits(a);
}
exports.traitsPrimary = traitsPrimary;
/**
 * __$helperMethod__ Retrieve all traits including those defined in libraries *
 * __$meta__{"deprecated":true}
 */
function allTraits(a) {
    return findTemplates(a, function (d) { return universeHelpers.isTraitType(d); });
}
exports.allTraits = allTraits;
/**
 *__$helperMethod__
 *__$meta__{"name":"resourceTypes","override":true}
 **/
function resourceTypesPrimary(a) {
    return allResourceTypes(a);
}
exports.resourceTypesPrimary = resourceTypesPrimary;
/**
 * __$helperMethod__ Retrieve all resource types including those defined in libraries
 * __$meta__{"deprecated":true}
 */
function allResourceTypes(a) {
    return findTemplates(a, function (d) { return universeHelpers.isResourceTypeType(d); });
}
exports.allResourceTypes = allResourceTypes;
function findTemplates(a, filter) {
    var arr = search.globalDeclarations(a.highLevel()).filter(function (x) { return filter(x.definition()); });
    var ll = a.highLevel().lowLevel();
    if (!ll) {
        return [];
    }
    var nodePath = ll.includePath();
    if (!nodePath) {
        nodePath = ll.unit().path();
    }
    var topLevelArr = arr.map(function (x) {
        var topLevelNode;
        var p = x.lowLevel().unit().path();
        if (p != nodePath) {
            topLevelNode = factory.buildWrapperNode(x, false);
            topLevelNode.meta().setCalculated();
        }
        else {
            topLevelNode = x.wrapperNode();
        }
        return topLevelNode;
    });
    return topLevelArr;
}
;
function relativeUriSegments(res) {
    var result = [];
    var parent = res;
    do {
        res = parent; //(parent instanceof RamlWrapper.ResourceImpl) ? <RamlWrapper.Resource>parent : null;
        result.push(res.relativeUri().value());
        parent = res.parent();
    } while (parent.definition().key().name == universes.Universe08.Resource.name);
    return result.reverse();
}
exports.relativeUriSegments = relativeUriSegments;
//__$helperMethod__ For methods of Resources returns parent resource. For methods of ResourceTypes returns null.
function parentResource(method) {
    if (RamlWrapperImpl.ResourceImpl.isInstance(method.parent())) {
        return method.parent();
    }
    return null;
}
exports.parentResource = parentResource;
//__$helperMethod__ Parent resource for non top level resources __$meta__={"name":"parentResource"}
function parent(resource) {
    var parent = resource.parent();
    if (parent.definition().key().name == universes.Universe08.Resource.name) {
        parent;
    }
    return null;
}
exports.parent = parent;
//__$helperMethod__ Get child resource by its relative path
function childResource(container, relPath) {
    if (container == null) {
        return null;
    }
    var resources = container.resources();
    if (!resources) {
        return null;
    }
    resources = resources.filter(function (x) { return x.relativeUri().value() == relPath; });
    if (resources.length == 0) {
        return null;
    }
    return resources[0];
}
exports.childResource = childResource;
function getResource(container, path) {
    if (!container) {
        return null;
    }
    var res = null;
    for (var i = 0; i < path.length; i++) {
        res = childResource(container, path[i]);
        if (!res) {
            return null;
        }
        container = res;
    }
    return res;
}
exports.getResource = getResource;
//__$helperMethod__ Get child method by its name
function childMethod(resource, method) {
    if (!resource) {
        return null;
    }
    return resource.methods().filter(function (x) { return x.method() == method; });
}
exports.childMethod = childMethod;
function getMethod(container, path, method) {
    var resource = getResource(container, path);
    if (resource == null) {
        return null;
    }
    return childMethod(resource, method);
}
exports.getMethod = getMethod;
function isApi(obj) {
    return (obj.definition().key().name == universes.Universe08.Api.name);
}
;
//__$helperMethod__ Api owning the resource as a sibling
function ownerApi(method) {
    var obj = method;
    while (!isApi(obj)) {
        obj = obj.parent();
    }
    return obj;
}
exports.ownerApi = ownerApi;
/**
 * __$helperMethod__
 * For methods of Resources: `{parent Resource relative path} {methodName}`.
 * For methods of ResourceTypes: `{parent ResourceType name} {methodName}`.
 * For other methods throws Exception.
 **/
function methodId(method) {
    var parent = method.parent();
    if (RamlWrapperImpl.ResourceImpl.isInstance(parent)) {
        return completeRelativeUri(parent) + ' ' + method.method().toLowerCase();
    }
    else if (RamlWrapperImpl.ResourceTypeImpl.isInstance(parent)) {
        return parent.name() + ' ' + method.method().toLowerCase();
    }
    throw new Error(linter.applyTemplate(messageRegistry.METHOD_OWNED_BY, { owner: method.definition().key().name }));
}
exports.methodId = methodId;
//__$helperMethod__ true for codes < 400 and false otherwise
function isOkRange(response) {
    var str = response.code().value();
    var err = linter.validateResponseString(str);
    if (err != null) {
        return false;
    }
    try {
        if (parseInt(str.charAt(0)) < 4) {
            return true;
        }
    }
    catch (e) { }
    return false;
}
exports.isOkRange = isOkRange;
//__$helperMethod__  Retrieve all resources of the Api
function allResources(api) {
    var resources = [];
    var visitor = function (res) {
        resources.push(res);
        res.resources().forEach(function (x) { return visitor(x); });
    };
    api.resources().forEach(function (x) { return visitor(x); });
    return resources;
}
exports.allResources = allResources;
//export function matchUri(apiRootRelativeUri:string, resource:RamlWrapper.Resource):Opt<ParamValue[]>{
//
//    var allParameters:Raml08Parser.NamedParameterMap = {}
//    var opt:Opt<RamlWrapper.Resource> = new Opt<RamlWrapper.Resource>(resource);
//    while(opt.isDefined()){
//        var res:RamlWrapper.Resource = opt.getOrThrow();
//        uriParameters(res).forEach(x=>allParameters[x.name()]=new ParamWrapper(x));
//        opt = parent(res);
//    }
//    var result = ramlPathMatch(completeRelativeUri(resource), allParameters, {})(apiRootRelativeUri);
//    if (result) {
//        return new Opt<ParamValue[]>(Object.keys((<any>result).params)
//            .map(x=>new ParamValue(x, result['params'][x])));
//    }
//    return Opt.empty<ParamValue[]>();
//}
var schemaContentChars = ['{', '<'];
//export function schema(body:RamlWrapper.TypeDeclaration, api:RamlWrapper.Api):Opt<SchemaDef>{
//
//    var schemaNode = body.schema();
//    if(!schemaNode){
//        return Opt.empty<SchemaDef>();
//    }
//    var schemaString = schemaNode;
//    var isContent:boolean = false;
//    schemaContentChars.forEach(x=>{try{ isContent = isContent||schemaString.indexOf(x)>=0}catch(e){}});
//    var schDef:SchemaDef;
//    if(isContent) {
//        schDef = new SchemaDef(schemaString);
//    }
//    else{
//        var globalSchemes = api.schemas().filter(x=>x.key()==schemaString);
//        if(globalSchemes.length>0){
//            schDef = new SchemaDef(globalSchemes[0].value().value(),globalSchemes[0].key());
//        }
//        else{
//            return Opt.empty<SchemaDef>();
//        }
//    }
//    return new Opt<SchemaDef>(schDef);
//}
/**
 * __$helperMethod__
 * Retrieve an ordered list of all uri parameters including those which are not described in the `uriParameters` node.
 * Consider a fragment of RAML specification:
 * ```yaml
 * /resource/{objectId}/{propertyId}:
 *   uriParameters:
 *     objectId:
 * ```
 * Here `propertyId` uri parameter is not described in the `uriParameters` node,
 * but it is among Resource.uriParameters().
 * __$meta__={"name":"uriParameters","override": true}
 **/
function uriParametersPrimary(resource) {
    return uriParameters(resource);
}
exports.uriParametersPrimary = uriParametersPrimary;
/**
 * __$helperMethod__
 * Retrieve an ordered list of all uri parameters including those which are not described in the `uriParameters` node.
 * Consider a fragment of RAML specification:
 * ```yaml
 * /resource/{objectId}/{propertyId}:
 *   uriParameters:
 *     objectId:
 * ```
 * Here `propertyId` uri parameter is not described in the `uriParameters` node,
 * Thus, it is not among Resource.uriParameters(), but it is among Resource.allUriParameters().
 * __$meta__={"name":"allUriParameters","deprecated":true}
 **/
function uriParameters(resource) {
    var uriNode = resource.relativeUri();
    var uri = uriNode.value();
    var params = resource.uriParameters_original();
    var propName = universes.Universe08.Resource.properties.uriParameters.name;
    var llNode = uriNode.highLevel().lowLevel();
    return extractParams(params, uri, resource, propName, llNode);
}
exports.uriParameters = uriParameters;
/**
 * __$helperMethod__
 * Retrieve an ordered list of all base uri parameters regardless of whether they are described in `baseUriParameters` or not
 * Consider a fragment of RAML specification:
 * ```yaml
 * version: v1
 * baseUri: https://{organization}.example.com/{version}/{service}
 * baseUriParameters:
 *   service:
 * ```
 * Here `version` and `organization` are base uri parameters which are not described in the `baseUriParameters` node,
 * but they are among `Api.baseUriParameters()`.
 * __$meta__={"name":"baseUriParameters","override":true}
 **/
function baseUriParametersPrimary(api) {
    return baseUriParameters(api);
}
exports.baseUriParametersPrimary = baseUriParametersPrimary;
/**
 * __$helperMethod__
 * Retrieve an ordered list of all base uri parameters regardless of whether they are described in `baseUriParameters` or not
 * Consider a fragment of RAML specification:
 * ```yaml
 * version: v1
 * baseUri: https://{organization}.example.com/{version}/{service}
 * baseUriParameters:
 *   service:
 * ```
 * Here `version` and `organization` are base uri parameters which are not described in the `baseUriParameters` node,
 * Thus, they are not among `Api.baseUriParameters()`, but they are among `Api.allBaseUriParameters()`.
 * __$meta__={"name":"allBaseUriParameters","deprecated":true}
 **/
function baseUriParameters(api) {
    var uriNode = api.baseUri();
    var uri = uriNode ? uriNode.value() : '';
    var params = api.baseUriParameters_original();
    var propName = universes.Universe08.Api.properties.baseUriParameters.name;
    var llNode = uriNode && uriNode.highLevel().lowLevel();
    return extractParams(params, uri, api, propName, llNode);
}
exports.baseUriParameters = baseUriParameters;
/**
 * __$helperMethod__
 * Retrieve an ordered list of all absolute uri parameters. Returns a union of `Api.allBaseUriParameters()`
 * for `Api` owning the `Resource` and `Resource.allUriParameters()`.
 **/
function absoluteUriParameters(res) {
    var params = [];
    var parent = res;
    do {
        res = parent;
        params = uriParameters(res).concat(params);
        parent = res.parent();
    } while (parent.definition().key().name == universes.Universe10.Resource.name);
    var api = parent;
    var baseUriParams = api.baseUriParameters();
    params = baseUriParameters(api).concat(params);
    return params;
}
exports.absoluteUriParameters = absoluteUriParameters;
/**
 * _//_$helperMethod__
 * Protocols used by the API. Returns the `protocols` property value if it is specified.
 * Otherwise, returns protocol, specified in the base URI.
 * __$meta__={"name":"protocols","override":true}
 **/
function protocolsPrimary(api) {
    return allProtocols(api);
}
exports.protocolsPrimary = protocolsPrimary;
/**
 * __$helperMethod__
 * Protocols used by the API. Returns the `protocols` property value if it is specified.
 * Otherwise, returns protocol, specified in the base URI.
 * __$meta__{"deprecated":true}
 **/
function allProtocols(api) {
    return api.protocols().map(function (x) { return x.toUpperCase(); });
    //var attributeDefaults = (<RamlWrapper.ApiImpl>api).attributeDefaults();
    //var result = (<RamlWrapper.ApiImpl>api).protocols_original();
    //if(result.length!=0||!attributeDefaults){
    //    return result;
    //}
    //var baseUriAttr = api.baseUri();
    //if(baseUriAttr) {
    //    var baseUri = baseUriAttr.value();
    //    if (baseUri) {
    //        var ind = baseUri.indexOf('://');
    //        if (ind >= 0) {
    //            result = [baseUri.substring(0, ind)];
    //        }
    //        if(result.length==0){
    //            result = [ 'HTTP' ];
    //        }
    //    }
    //}
    //return result;
}
exports.allProtocols = allProtocols;
/**
 * _//_$helperMethod__
 * Returns security schemes, resource or method is secured with. If no security schemes are set at resource or method level,
 * returns schemes defined with `securedBy` at API level.
 * __$meta__={"name":"securedBy","override":true}
 **/
function securedByPrimary(resourceOrMethod) {
    return allSecuredBy(resourceOrMethod);
}
exports.securedByPrimary = securedByPrimary;
/**
 * __$helperMethod__
 * Returns security schemes, resource or method is secured with. If no security schemes are set at resource or method level,
 * returns schemes defined with `securedBy` at API level.
 * __$meta__{"deprecated":true}
 **/
function allSecuredBy(resourceOrMethod) {
    //var currentSecuredBy = (<RamlWrapper.ResourceImpl|RamlWrapper.MethodImpl>resourceOrMethod).securedBy_original();
    //if (currentSecuredBy && currentSecuredBy.length > 0) {
    //    return currentSecuredBy;
    //}
    //
    ////instanceof, but have to avoid direct usage of instanceof in JS.
    //if (resourceOrMethod.highLevel().definition().key() == universes.Universe08.Method) {
    //    var resource = <RamlWrapper.ResourceImpl>(<RamlWrapper.Method>resourceOrMethod).parentResource();
    //    if (resource && resource.securedBy_original() && resource.securedBy_original().length > 0) {
    //        return resource.securedBy();
    //    }
    //}
    return resourceOrMethod.securedBy();
}
exports.allSecuredBy = allSecuredBy;
/**
 * __$helperMethod__
 * __$meta__={"primary":true}
 **/
function securitySchemeName(schemeReference) {
    var highLevel = schemeReference.highLevel();
    if (!highLevel)
        return "";
    var attributeValue = highLevel.value();
    if (!attributeValue)
        return "";
    return attributeValue.toString();
}
exports.securitySchemeName = securitySchemeName;
/**
 * __$helperMethod__
 * __$meta__={"primary":true}
 **/
function securityScheme(schemeReference) {
    var highLevel = schemeReference.highLevel();
    if (!highLevel)
        return null;
    var declaration = search.findDeclarationByNode(highLevel, search.LocationKind.VALUE_COMPLETION);
    if (!declaration)
        return null;
    if (!declaration.getKind || declaration.getKind() != hl.NodeKind.NODE) {
        return null;
    }
    var result = declaration.wrapperNode();
    if (!(RamlWrapperImpl.AbstractSecuritySchemeImpl.isInstance(result))) {
        //I do not see how to avoid instanceof here
        return null;
    }
    return result;
}
exports.securityScheme = securityScheme;
/**
 * __$helperMethod__
 * __$meta__={"primary":true}
 **/
function RAMLVersion(api) {
    return api.highLevel().definition().universe().version();
}
exports.RAMLVersion = RAMLVersion;
/**
 * __$helperMethod__
 * __$meta__={"primary":true}
 **/
function structuredValue(reference) {
    var hl = reference.value().lowLevel();
    return new core.TypeInstanceImpl(hl);
}
exports.structuredValue = structuredValue;
/**
 * __$helperMethod__
 * __$meta__={"name":"name","primary":true}
 **/
function referenceName(reference) {
    var val = reference.highLevel().value();
    return (typeof val == 'string') || val == null ? val : val.valueName();
}
exports.referenceName = referenceName;
/**
 * __$helperMethod__
 * __$meta__={"name":"trait","primary":true}
 **/
function referencedTrait(ref) {
    return referencedObject(ref);
}
exports.referencedTrait = referencedTrait;
/**
 * __$helperMethod__
 * __$meta__={"name":"resourceType","primary":true}
 **/
function referencedResourceType(ref) {
    return referencedObject(ref);
}
exports.referencedResourceType = referencedResourceType;
function referencedObject(ref) {
    var attr = ref.highLevel();
    var parent = attr.parent();
    var vn = ref.name();
    var cands = search.referenceTargets(attr.property(), parent).filter(function (x) { return hlimpl.qName(x, parent) == vn; });
    if (cands.length == 0) {
        return null;
    }
    return cands[0].wrapperNode();
}
function extractParams(params, uri, owner, propName, propNode) {
    var ownerHl = owner.highLevel();
    var prop = ownerHl.definition().property(propName);
    if (typeof (uri) != 'string') {
        uri = "";
    }
    var describedParams = {};
    params.forEach(function (x) {
        var arr = describedParams[x.name()];
        if (!arr) {
            arr = [];
            describedParams[x.name()] = arr;
        }
        arr.push(x);
    });
    var allParams = [];
    var prev = 0;
    var mentionedParams = {};
    for (var i = uri.indexOf('{'); i >= 0; i = uri.indexOf('{', prev)) {
        prev = uri.indexOf('}', ++i);
        if (prev < 0) {
            break;
        }
        var paramName = uri.substring(i, prev);
        mentionedParams[paramName] = true;
        if (describedParams[paramName]) {
            describedParams[paramName].forEach(function (x) { return allParams.push(x); });
        }
        else {
            var propUnit = propNode && hlimpl.actualUnit(propNode);
            var uriParameter = new RamlWrapperImpl.StringTypeDeclarationImpl(paramName);
            uriParameter.setName(paramName);
            var hlNode = uriParameter.highLevel();
            hlNode.lowLevel().setUnit(propUnit);
            hlNode.setParent(ownerHl);
            uriParameter.meta().setCalculated();
            hlNode.patchProp(prop);
            allParams.push(uriParameter);
        }
    }
    Object.keys(describedParams).filter(function (x) { return !mentionedParams[x]; })
        .forEach(function (x) { return describedParams[x].forEach(function (y) { return allParams.push(y); }); });
    return allParams;
}
/**
 * __$helperMethod__
 * __$meta__={"primary":true}
 **/
function schemaContent(bodyDeclaration) {
    var schemaDecl = bodyDeclaration.schema();
    if (schemaDecl == null) {
        return null;
    }
    var schemaString = schemaDecl.value();
    if (!schemaString) {
        return null;
    }
    if (util.stringStartsWith(schemaString, "{")
        || util.stringStartsWith(schemaString, "[")
        || util.stringStartsWith(schemaString, "<")) {
        return schemaString;
    }
    var hlNode = bodyDeclaration.highLevel();
    var root = hlNode.root();
    var globalSchemas = root.elementsOfKind(universes.Universe08.Api.properties.schemas.name);
    var declaration = _.find(globalSchemas, function (x) { return x.name() == schemaString; });
    if (!declaration)
        return schemaString;
    if (!declaration.getKind || declaration.getKind() != hl.NodeKind.NODE) {
        return schemaString;
    }
    //we found the schema declaration and should get its contents
    if (declaration.definition().key() != universes.Universe08.GlobalSchema) {
        return schemaString;
    }
    var valueAttribute = declaration.attr(universes.Universe08.GlobalSchema.properties.value.name);
    if (valueAttribute == null) {
        return null;
    }
    return valueAttribute.value();
}
exports.schemaContent = schemaContent;
/**
 * __$helperMethod__
 * __$meta__={"name":"parametrizedProperties","primary":true}
 **/
function getTemplateParametrizedProperties(node) {
    var highLevelNode = node.highLevel();
    if (highLevelNode == null) {
        return null;
    }
    var lowLevelNode = highLevelNode.lowLevel();
    if (lowLevelNode == null) {
        return null;
    }
    var children = lowLevelNode.children().filter(function (x) { return x.key().indexOf("<<") >= 0; });
    if (children.length == 0) {
        return null;
    }
    var result = new core.TypeInstanceImpl(children);
    return result;
}
exports.getTemplateParametrizedProperties = getTemplateParametrizedProperties;
//
//export class SchemaDef{
//
//    constructor(private _content:string, private _name?:string){}
//
//    name():string{return this._name}
//
//    content(): string{return this._content}
//}
//
//
//export class ParamValue{
//    key:string
//    value:any
//
//    constructor(key:string, value:any) {
//        this.key = key;
//        this.value = value;
//    }
//}
//
//
//class ParamWrapper implements Raml08Parser.BasicNamedParameter{
//
//    constructor(private _param:RamlWrapper.TypeDeclaration){
//
//        this.description = _param.description() ? _param.description().value() : this.description;
//
//        this.displayName = _param.displayName();
//
////        this.enum = _param.enum();
//
//        this.type = _param.type().length > 0 ? _param.type()[0] : "string";
//
//        this.example = _param.example();
//
//        this.repeat = _param.repeat();
//
//        this.required = _param.required();
//
//        this.default = _param.default();
//    }
//
//    description: Raml08Parser.MarkdownString
//
//    displayName: string
//
//    'enum': any[]
//
//    type: string
//
//    example: any
//
//    repeat: boolean
//
//    required: boolean
//
//    'default': any
//
//}
//# sourceMappingURL=wrapperHelper08.js.map