text: // Copyright IBM Corp. 2015,2016. All Rights Reserved.
  // Node module: loopback-swagger
  // This file is licensed under the MIT License.
  // License text available at https://opensource.org/licenses/MIT
  
  'use strict';
  
  // Globalization
  var g = require('strong-globalize')();
  
  var _ = require('lodash');
  
  module.exports = TypeRegistry;
  
  function TypeRegistry() {
    this._definitions = Object.create(null);
    this._referenced = Object.create(null);
    this._loopbackTypeMap = Object.create(null);
    this._routeSecurities = Object.create(null);
  
    this.registerLoopbackType('x-any', {properties: {}});
    this.registerLoopbackType('ObjectID', {type: 'string', pattern: '^[a-fA-F\\d]{24}$'});
    this.registerLoopbackType('GeoPoint', {
      properties: {
        lat: {type: 'number'},
        lng: {type: 'number'},
      },
    });
    this.registerLoopbackType('DateString', {type: 'string', format: 'date-time'});
    this.registerLoopbackType('file', {type: 'file'});
  }
  
  TypeRegistry.prototype.registerLoopbackType = function(typeName, definition) {
    var typeNameLowerCase = typeName.toLowerCase();
    this._loopbackTypeMap[typeNameLowerCase] = typeName;
    this._definitions[typeNameLowerCase] = definition;
  };
  
  TypeRegistry.prototype.registerModel = function(typeName, definitionFn) {
    this._definitions[typeName] = definitionFn;
  };
  
  TypeRegistry.prototype.reference = function(typeName) {
    var refName = typeName;
    var typeNameLowerCase = typeName.toLowerCase();
    if (typeNameLowerCase in this._loopbackTypeMap) {
      refName = this._loopbackTypeMap[typeNameLowerCase];
    }
    this._referenced[refName] = true;
    return '#/definitions/' + refName;
  };
  
  TypeRegistry.prototype._buildDefinitionsFrom = function(definitionObj) {
    var defs = Object.create(null);
    var currentDefCount = 0;
    var newDefCount = 0;
    do {
      currentDefCount = Object.keys(definitionObj).length;
      for (var name in definitionObj) {
        var nameLowerCase = name.toLowerCase();
        if (nameLowerCase in this._loopbackTypeMap) {
          var loopbackTypeName = this._loopbackTypeMap[nameLowerCase];
          if (!defs[loopbackTypeName]) {
            defs[loopbackTypeName] = this._definitions[nameLowerCase];
          }
        } else {
          if (!defs[name] && this._definitions[name]) {
            defs[name] = this._definitions[name]();
          }
        }
      }
      newDefCount = Object.keys(definitionObj).length;
    } while (currentDefCount !== newDefCount);
    return defs;
  };
  
  TypeRegistry.prototype.getDefinitions = function() {
    var defs = this._buildDefinitionsFrom(this._referenced);
  
    for (var ref in this._referenced) {
      if (ref in defs) continue;
      // https://github.com/strongloop/loopback-explorer/issues/71
      g.warn('{{Swagger}}: skipping unknown type %j.', ref);
    }
  
    return defs;
  };
  
  TypeRegistry.prototype.getAllDefinitions = function() {
    return this._buildDefinitionsFrom(this._definitions);
  };
  
  TypeRegistry.prototype.isDefined = function(typeName) {
    var typeNameLowerCase = typeName.toLowerCase();
    return typeName in this._definitions ||
      typeNameLowerCase in this._loopbackTypeMap;
  };
  
  TypeRegistry.prototype.routeSecurity = function(modelName, acl) {
    var key = modelName + "." + acl.principalId;
    this._routeSecurities[key] = this._routeSecurities[key] || [];
    this._routeSecurities[key].push(acl);
  };
  
  TypeRegistry.prototype.checkSecurity = function(key) {
    var security = null;
    if (key in this._routeSecurities) {
      security = [];
      var obj = {};
      this._routeSecurities[key].forEach(function(acl) {
        obj[acl.principalType] = obj[acl.principalType] || [];
        obj[acl.principalType].push(acl.scope);
      });
      security.push(obj);
    }
    return security;
  };
  