text: 'use strict';
  
  const crypto = require('crypto');
  
  const commons = require('@sap/odata-commons');
  const EdmProvider = commons.edm.EdmProvider;
  const CsdlJsonProvider = commons.csdl.CsdlJsonProvider;
  const UriParser = commons.uri.UriParser;
  const RepresentationKinds = commons.format.RepresentationKind.Kinds;
  const ContentTypes = commons.format.ContentTypeInfo.ContentTypes;
  const PrimitiveValueEncoder = commons.utils.PrimitiveValueEncoder;
  const Service = require('./core/Service');
  const Components = require('./core/ComponentManager').Components;
  const Dispatcher = require('./core/Dispatcher');
  const MetadataHandler = require('./handler/MetadataHandler');
  const ServiceHandler = require('./handler/ServiceHandler');
  const BatchHandler = require('./handler/BatchHandler');
  const ResponseContentNegotiator = require('./format/ResponseContentNegotiator');
  const SerializerFactory = require('./serializer/SerializerFactory');
  const MetadataXmlSerializer = require('./serializer/MetadataXmlSerializer');
  const ServiceJsonSerializer = require('./serializer/ServiceJsonSerializer');
  const BatchMultipartSerializer = require('./serializer/BatchMultipartSerializer');
  const ErrorJsonSerializer = require('./serializer/ErrorJsonSerializer');
  const ErrorXmlSerializer = require('./serializer/ErrorXmlSerializer');
  const DebugJsonSerializer = require('./serializer/DebugJsonSerializer');
  const DebugHtmlSerializer = require('./serializer/DebugHtmlSerializer');
  const RequestValidator = require('./validator/RequestValidator');
  const DeserializerFactory = require('./deserializer/DeserializerFactory');
  
  /**
   * OData service factory to create a full featured OData service.
   */
  class ServiceFactory {
      /**
       * Creates a full featured OData service.
       * @param {Object} metadata The metadata JSON with which the CSDL and then EDM providers can be created
       * @param {Object} [edmConfiguration={}] A configuration object to configure the behavior of EDM artifacts
       * The example shows the configuration for an EntitySet and a Singleton that are ETAG enabled.
       * const configuration = {
       *   'the.EDM.namespace': {
       *     TheNameOfTheConcurrentEntitySet: { isConcurrent: true },
       *     TheNameOfTheConcurrentSingleton: { isConcurrent: true }
       *   }
       * };
       * @returns {Service} A full featured OData service
       */
      static createService(metadata, edmConfiguration = {}) {
          const edmProvider = new EdmProvider(new CsdlJsonProvider(metadata), edmConfiguration);
  
          return new Service(edmProvider)
              .log(null, entry => entry)
              .on('request', (request, response, next) => {
                  // This is the default listener called on on each request.
                  next(/* error */);
              })
              .on('debug', (request, response, next) => {
                  // We disable the debug mode by default.
                  // Overwrite this listener to enable debug mode.
                  // To get the info if debug mode is requested
                  // call 'request.getContract().isDebug(): true|false'.
                  request.getContract().enableDebugMode(false);
                  next(/* error */);
              })
              .on('error', (request, response, next, error) => {
                  // This listener subscribes to all error events.
                  // The error object is the original error thrown.
                  // Another error-like object with properties like .statusCode or .message could be passed on.
                  next(null, error);
              })
              .setOdataVersion('4.0')
              .setMetadataEtag(crypto.createHash('sha256').update(JSON.stringify(metadata)).digest('base64'))
              .use(Components.URI_PARSER, new UriParser(edmProvider))
              .use(Components.CONTENT_NEGOTIATOR, new ResponseContentNegotiator())
              .use(Components.REQUEST_VALIDATOR, new RequestValidator())
              .use(Components.DISPATCHER, new Dispatcher())
              .use(Components.PRIMITIVE_VALUE_ENCODER, new PrimitiveValueEncoder())
              .use(Components.METADATA_HANDLER, MetadataHandler.read)
              .use(Components.SERVICE_HANDLER, ServiceHandler.read)
              .use(Components.BATCH_EXECUTE_HANDLER, BatchHandler.handle)
              .parse(RepresentationKinds.BATCH, ContentTypes.MULTIPART_MIXED,
                  DeserializerFactory.createMultipartMixedDeserializer())
              .parse(RepresentationKinds.BATCH, ContentTypes.JSON, DeserializerFactory.createBatchJsonDeserializer())
              .parse(RepresentationKinds.ENTITY, ContentTypes.JSON,
                  DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.ENTITY))
              .parse(RepresentationKinds.PRIMITIVE, ContentTypes.JSON,
                  DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.PRIMITIVE))
              .parse(RepresentationKinds.PRIMITIVE_COLLECTION, ContentTypes.JSON,
                  DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.PRIMITIVE_COLLECTION))
              .parse(RepresentationKinds.COMPLEX, ContentTypes.JSON,
                  DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.COMPLEX))
              .parse(RepresentationKinds.COMPLEX_COLLECTION, ContentTypes.JSON,
                  DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.COMPLEX_COLLECTION))
              .parse(RepresentationKinds.REFERENCE, ContentTypes.JSON,
                  DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.REFERENCE))
              .parse(RepresentationKinds.PRIMITIVE_VALUE, ContentTypes.TEXT_PLAIN,
                  DeserializerFactory.createTextValueDeserializer())
              .parse(RepresentationKinds.ACTION_PARAMETERS, ContentTypes.JSON,
                  DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.ACTION_PARAMETERS))
              // Register the binary parser for all content types sent for representation kind BINARY.
              // There is special logic for this in the request content negotiator.
              .parse(RepresentationKinds.BINARY, '', DeserializerFactory.createBinaryDeserializer())
              .format(RepresentationKinds.METADATA, ContentTypes.XML, (context, data, options, next) =>
                  next(null, new MetadataXmlSerializer(edmProvider, context.getLogger()).serialize(data)))
              .format(RepresentationKinds.SERVICE, ContentTypes.JSON,
                  (context, data, options, next) => next(null, new ServiceJsonSerializer(edmProvider).serialize(data)))
              .format(RepresentationKinds.BATCH, ContentTypes.MULTIPART_MIXED, (context, batchContext, options, next) => {
                  new BatchMultipartSerializer(batchContext).execute(context.getResponse());
                  next();
              })
              .format(RepresentationKinds.BATCH, ContentTypes.JSON,
                  SerializerFactory.batch, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.ENTITY, ContentTypes.JSON,
                  SerializerFactory.resourceEntity, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.ENTITY_COLLECTION, ContentTypes.JSON,
                  SerializerFactory.resourceEntityCollection, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.PRIMITIVE, ContentTypes.JSON,
                  SerializerFactory.resourcePrimitive, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.PRIMITIVE_COLLECTION, ContentTypes.JSON,
                  SerializerFactory.resourcePrimitiveCollection, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.COMPLEX, ContentTypes.JSON,
                  SerializerFactory.resourceComplex, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.COMPLEX_COLLECTION, ContentTypes.JSON,
                  SerializerFactory.resourceComplexCollection, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.REFERENCE, ContentTypes.JSON,
                  SerializerFactory.resourceReference, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.REFERENCE_COLLECTION, ContentTypes.JSON,
                  SerializerFactory.resourceReferenceCollection, SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.PRIMITIVE_VALUE, ContentTypes.TEXT_PLAIN, SerializerFactory.value)
              .format(RepresentationKinds.BINARY, ContentTypes.TEXT_PLAIN, SerializerFactory.value)
              .format(RepresentationKinds.COUNT, ContentTypes.TEXT_PLAIN,
                  (context, data, options, next) => next(null, data.value.toString()))
              .format(RepresentationKinds.NO_CONTENT, null, (context, data, options, next) => {
                  context.getLogger().path('Entering ServiceFactory.format(NO_CONTENT, null)');
                  context.getLogger().debug('No payload available');
                  next();
              })
              .format(RepresentationKinds.ERROR, ContentTypes.XML,
                  (error, response, next) => next(null, new ErrorXmlSerializer(error).serialize()))
              .format(RepresentationKinds.ERROR, ContentTypes.JSON,
                  (error, response, next) => next(null, new ErrorJsonSerializer(error).serialize()),
                  SerializerFactory.checkJsonParameters)
              .format(RepresentationKinds.DEBUG, ContentTypes.JSON, (context, buffer, error, next) =>
                  next(null, new DebugJsonSerializer(context, buffer.toString(), error).serialize()))
              .format(RepresentationKinds.DEBUG, ContentTypes.HTML, (context, buffer, error, next) =>
                  next(null, new DebugHtmlSerializer(context, buffer.toString(), error).serialize()));
      }
  }
  
  module.exports = ServiceFactory;
  