// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

import 'package:async/async.dart';

import 'base_client.dart';
import 'base_request.dart';
import 'exception.dart';
import 'io.dart' as io;
import 'streamed_response.dart';

/// A `dart:io`-based HTTP client.
///
/// This is the default client when running on the command line.
class IOClient extends BaseClient {
  /// The underlying `dart:io` HTTP client.
  dynamic _inner;

  /// Creates a new HTTP client.
  ///
  /// [innerClient] must be a `dart:io` HTTP client. If it's not passed, a
  /// default one will be instantiated.
  IOClient([dynamic innerClient]) {
    io.assertSupported("IOClient");
    if (innerClient != null) {
      // TODO(nweiz): remove this assert when we can type [innerClient]
      // properly.
      assert(io.isHttpClient(innerClient));
      _inner = innerClient;
    } else {
      _inner = io.newHttpClient();
    }
  }

  /// Sends an HTTP request and asynchronously returns the response.
  @override
  Future<StreamedResponse> send(BaseRequest request) async {
    dynamic stream = request.finalize();

    try {
      dynamic ioRequest = await _inner.openUrl(request.method, request.url);

      ioRequest
          ..followRedirects = request.followRedirects
          ..maxRedirects = request.maxRedirects
          ..contentLength = request.contentLength == null
              ? -1
              : request.contentLength
          ..persistentConnection = request.persistentConnection;
      request.headers.forEach((String name, String value) {
        ioRequest.headers.set(name, value);
      });

      dynamic response = await stream.pipe(
          DelegatingStreamConsumer.typed(ioRequest));
      Map<String, dynamic> headers = <String, dynamic>{};
      response.headers.forEach((String key, dynamic values) {
        headers[key] = values.join(',');
      });

      return new StreamedResponse(
          DelegatingStream.typed/*<List<int>>*/(response).handleError((dynamic error) =>
              throw new ClientException(error.message, error.uri),
              test: (dynamic error) => io.isHttpException(error)),
          response.statusCode,
          contentLength: response.contentLength == -1
              ? null
              : response.contentLength,
          request: request,
          headers: headers,
          isRedirect: response.isRedirect,
          persistentConnection: response.persistentConnection,
          reasonPhrase: response.reasonPhrase);
    } catch (error) {
      if (!io.isHttpException(error)) rethrow;
      throw new ClientException(error.message, error.uri);
    }
  }

  /// Closes the client. This terminates all active connections. If a client
  /// remains unclosed, the Dart process may not terminate.
  @override
  void close() {
    if (_inner != null) _inner.close(force: true);
    _inner = null;
  }
}