'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');

var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');

var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);

var _inherits2 = require('babel-runtime/helpers/inherits');

var _inherits3 = _interopRequireDefault(_inherits2);

var _driver = require('./driver');

var _error = require('./error');

var _connectionProviders = require('./internal/connection-providers');

var _leastConnectedLoadBalancingStrategy = require('./internal/least-connected-load-balancing-strategy');

var _leastConnectedLoadBalancingStrategy2 = _interopRequireDefault(_leastConnectedLoadBalancingStrategy);

var _roundRobinLoadBalancingStrategy = require('./internal/round-robin-load-balancing-strategy');

var _roundRobinLoadBalancingStrategy2 = _interopRequireDefault(_roundRobinLoadBalancingStrategy);

var _connectionErrorHandler = require('./internal/connection-error-handler');

var _connectionErrorHandler2 = _interopRequireDefault(_connectionErrorHandler);

var _configuredHostNameResolver = require('./internal/resolver/configured-host-name-resolver');

var _configuredHostNameResolver2 = _interopRequireDefault(_configuredHostNameResolver);

var _node = require('./internal/node');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * A driver that supports routing in a causal cluster.
 * @private
 */
/**
 * Copyright (c) 2002-2018 "Neo4j,"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var RoutingDriver = function (_Driver) {
  (0, _inherits3.default)(RoutingDriver, _Driver);

  function RoutingDriver(hostPort, routingContext, userAgent) {
    var token = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
    var config = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
    (0, _classCallCheck3.default)(this, RoutingDriver);

    var _this = (0, _possibleConstructorReturn3.default)(this, (RoutingDriver.__proto__ || (0, _getPrototypeOf2.default)(RoutingDriver)).call(this, hostPort, userAgent, token, validateConfig(config)));

    _this._routingContext = routingContext;
    return _this;
  }

  (0, _createClass3.default)(RoutingDriver, [{
    key: '_afterConstruction',
    value: function _afterConstruction() {
      this._log.info('Routing driver ' + this._id + ' created for server address ' + this._hostPort);
    }
  }, {
    key: '_createConnectionProvider',
    value: function _createConnectionProvider(hostPort, connectionPool, driverOnErrorCallback) {
      var loadBalancingStrategy = RoutingDriver._createLoadBalancingStrategy(this._config, connectionPool);
      var resolver = createHostNameResolver(this._config);
      return new _connectionProviders.LoadBalancer(hostPort, this._routingContext, connectionPool, loadBalancingStrategy, resolver, driverOnErrorCallback, this._log);
    }
  }, {
    key: '_createConnectionErrorHandler',
    value: function _createConnectionErrorHandler() {
      var _this2 = this;

      // connection errors mean SERVICE_UNAVAILABLE for direct driver but for routing driver they should only
      // result in SESSION_EXPIRED because there might still exist other servers capable of serving the request
      return new _connectionErrorHandler2.default(_error.SESSION_EXPIRED, function (error, hostPort) {
        return _this2._handleUnavailability(error, hostPort);
      }, function (error, hostPort) {
        return _this2._handleWriteFailure(error, hostPort);
      });
    }
  }, {
    key: '_handleUnavailability',
    value: function _handleUnavailability(error, hostPort) {
      this._log.warn('Routing driver ' + this._id + ' will forget ' + hostPort + ' because of an error ' + error.code + ' \'' + error.message + '\'');
      this._connectionProvider.forget(hostPort);
      return error;
    }
  }, {
    key: '_handleWriteFailure',
    value: function _handleWriteFailure(error, hostPort) {
      this._log.warn('Routing driver ' + this._id + ' will forget writer ' + hostPort + ' because of an error ' + error.code + ' \'' + error.message + '\'');
      this._connectionProvider.forgetWriter(hostPort);
      return (0, _error.newError)('No longer possible to write to server at ' + hostPort, _error.SESSION_EXPIRED);
    }

    /**
     * Create new load balancing strategy based on the config.
     * @param {object} config the user provided config.
     * @param {Pool} connectionPool the connection pool for this driver.
     * @return {LoadBalancingStrategy} new strategy.
     * @private
     */

  }], [{
    key: '_createLoadBalancingStrategy',
    value: function _createLoadBalancingStrategy(config, connectionPool) {
      var configuredValue = config.loadBalancingStrategy;
      if (!configuredValue || configuredValue === _leastConnectedLoadBalancingStrategy.LEAST_CONNECTED_STRATEGY_NAME) {
        return new _leastConnectedLoadBalancingStrategy2.default(connectionPool);
      } else if (configuredValue === _roundRobinLoadBalancingStrategy.ROUND_ROBIN_STRATEGY_NAME) {
        return new _roundRobinLoadBalancingStrategy2.default();
      } else {
        throw (0, _error.newError)('Unknown load balancing strategy: ' + configuredValue);
      }
    }
  }]);
  return RoutingDriver;
}(_driver.Driver);

/**
 * @private
 * @returns {HostNameResolver} new resolver.
 */


function createHostNameResolver(config) {
  if (config.resolver) {
    return new _configuredHostNameResolver2.default(config.resolver);
  }
  return new _node.HostNameResolver();
}

/**
 * @private
 * @returns {object} the given config.
 */
function validateConfig(config) {
  if (config.trust === 'TRUST_ON_FIRST_USE') {
    throw (0, _error.newError)('The chosen trust mode is not compatible with a routing driver');
  }
  var resolver = config.resolver;
  if (resolver && typeof resolver !== 'function') {
    throw new TypeError('Configured resolver should be a function. Got: ' + resolver);
  }
  return config;
}

exports.default = RoutingDriver;