text: "use strict";
  module.exports = Service;
  
  // extends Namespace
  var Namespace = require("./namespace");
  ((Service.prototype = Object.create(Namespace.prototype)).constructor = Service).className = "Service";
  
  var Method,
      util,
      rpc;
  
  /**
   * Constructs a new service instance.
   * @classdesc Reflected service.
   * @extends NamespaceBase
   * @constructor
   * @param {string} name Service name
   * @param {Object.<string,*>} [options] Service options
   * @throws {TypeError} If arguments are invalid
   */
  function Service(name, options) {
      Namespace.call(this, name, options);
  
      /**
       * Service methods.
       * @type {Object.<string,Method>}
       */
      this.methods = {}; // toJSON, marker
  
      /**
       * Cached methods as an array.
       * @type {Method[]|null}
       * @private
       */
      this._methodsArray = null;
  }
  
  /**
   * Service descriptor.
   * @interface IService
   * @extends INamespace
   * @property {Object.<string,IMethod>} methods Method descriptors
   */
  
  /**
   * Constructs a service from a service descriptor.
   * @param {string} name Service name
   * @param {IService} json Service descriptor
   * @returns {Service} Created service
   * @throws {TypeError} If arguments are invalid
   */
  Service.fromJSON = function fromJSON(name, json) {
      var service = new Service(name, json.options);
      /* istanbul ignore else */
      if (json.methods)
          for (var names = Object.keys(json.methods), i = 0; i < names.length; ++i)
              service.add(Method.fromJSON(names[i], json.methods[names[i]]));
      if (json.nested)
          service.addJSON(json.nested);
      service.comment = json.comment;
      return service;
  };
  
  /**
   * Converts this service to a service descriptor.
   * @param {IToJSONOptions} [toJSONOptions] JSON conversion options
   * @returns {IService} Service descriptor
   */
  Service.prototype.toJSON = function toJSON(toJSONOptions) {
      var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
      var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
      return util.toObject([
          "options" , inherited && inherited.options || undefined,
          "methods" , Namespace.arrayToJSON(this.methodsArray, toJSONOptions) || /* istanbul ignore next */ {},
          "nested"  , inherited && inherited.nested || undefined,
          "comment" , keepComments ? this.comment : undefined
      ]);
  };
  
  /**
   * Methods of this service as an array for iteration.
   * @name Service#methodsArray
   * @type {Method[]}
   * @readonly
   */
  Object.defineProperty(Service.prototype, "methodsArray", {
      get: function() {
          return this._methodsArray || (this._methodsArray = util.toArray(this.methods));
      }
  });
  
  function clearCache(service) {
      service._methodsArray = null;
      return service;
  }
  
  /**
   * @override
   */
  Service.prototype.get = function get(name) {
      return this.methods[name]
          || Namespace.prototype.get.call(this, name);
  };
  
  /**
   * @override
   */
  Service.prototype.resolveAll = function resolveAll() {
      var methods = this.methodsArray;
      for (var i = 0; i < methods.length; ++i)
          methods[i].resolve();
      return Namespace.prototype.resolve.call(this);
  };
  
  /**
   * @override
   */
  Service.prototype.add = function add(object) {
  
      /* istanbul ignore if */
      if (this.get(object.name))
          throw Error("duplicate name '" + object.name + "' in " + this);
  
      if (object instanceof Method) {
          this.methods[object.name] = object;
          object.parent = this;
          return clearCache(this);
      }
      return Namespace.prototype.add.call(this, object);
  };
  
  /**
   * @override
   */
  Service.prototype.remove = function remove(object) {
      if (object instanceof Method) {
  
          /* istanbul ignore if */
          if (this.methods[object.name] !== object)
              throw Error(object + " is not a member of " + this);
  
          delete this.methods[object.name];
          object.parent = null;
          return clearCache(this);
      }
      return Namespace.prototype.remove.call(this, object);
  };
  
  /**
   * Creates a runtime service using the specified rpc implementation.
   * @param {RPCImpl} rpcImpl RPC implementation
   * @param {boolean} [requestDelimited=false] Whether requests are length-delimited
   * @param {boolean} [responseDelimited=false] Whether responses are length-delimited
   * @returns {rpc.Service} RPC service. Useful where requests and/or responses are streamed.
   */
  Service.prototype.create = function create(rpcImpl, requestDelimited, responseDelimited) {
      var rpcService = new rpc.Service(rpcImpl, requestDelimited, responseDelimited);
      for (var i = 0, method; i < /* initializes */ this.methodsArray.length; ++i) {
          var methodName = util.lcFirst((method = this._methodsArray[i]).resolve().name).replace(/[^$\w_]/g, "");
          rpcService[methodName] = util.codegen(["r","c"], util.isReserved(methodName) ? methodName + "_" : methodName)("return this.rpcCall(m,q,s,r,c)")({
              m: method,
              q: method.resolvedRequestType.ctor,
              s: method.resolvedResponseType.ctor
          });
      }
      return rpcService;
  };
  
  Service._configure = function (){
      Method = require("./method");
      util   = require("./util");
      rpc    = require("./rpc/service");
  };