// Copyright (c) 2014, 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.

/// @docImport 'package:analysis_server/src/analysis_server.dart';
library;

import 'dart:convert';

import 'package:analysis_server/protocol/protocol.dart';

/// Instances of the class [ChannelChunkSink] uses a [Converter] to translate
/// chunks.
class ChannelChunkSink<S, T> implements ChunkedConversionSink<S> {
  /// The converter used to translate chunks.
  final Converter<S, T> converter;

  /// The sink to which the converted chunks are added.
  final Sink<T> sink;

  /// A flag indicating whether the sink has been closed.
  bool closed = false;

  /// Initialize a newly create sink to use the given [converter] to convert
  /// chunks before adding them to the given [sink].
  new(this.converter, this.sink);

  @override
  void add(S chunk) {
    if (!closed) {
      var convertedChunk = converter.convert(chunk);
      if (convertedChunk != null) {
        sink.add(convertedChunk);
      }
    }
  }

  @override
  void close() {
    closed = true;
    sink.close();
  }
}

/// The abstract class [ClientCommunicationChannel] defines the behavior of
/// objects that allow a client to send [Request]s to an [AnalysisServer] and to
/// receive both [Response]s and [Notification]s.
abstract class ClientCommunicationChannel {
  /// The stream of notifications from the server.
  Stream<Notification> get notificationStream;

  /// The stream of responses from the server.
  Stream<Response> get responseStream;

  /// Close the channel to the server. Once called, all future communication
  /// with the server via [sendRequest] will silently be ignored.
  Future<void> close();

  /// Send the given [request] to the server
  /// and return a future with the associated [Response].
  Future<Response> sendRequest(Request request);
}

/// Instances of the class [JsonStreamDecoder] convert JSON strings to values.
class JsonStreamDecoder extends Converter<String, Object?> {
  @override
  Object? convert(String input) => json.decode(input);

  @override
  ChunkedConversionSink<String> startChunkedConversion(Sink<Object?> sink) =>
      ChannelChunkSink<String, Object?>(this, sink);
}

/// Instances of the class [NotificationConverter] convert JSON maps to
/// [Notification]s.
class NotificationConverter
    extends Converter<Map<String, Object?>, Notification> {
  @override
  Notification convert(Map<Object?, Object?> input) =>
      Notification.fromJson(input);

  @override
  ChunkedConversionSink<Map<String, Object?>> startChunkedConversion(
    Sink<Notification> sink,
  ) => ChannelChunkSink<Map<String, Object?>, Notification>(this, sink);
}

/// Instances of the class [ResponseConverter] convert JSON maps to [Response]s.
class ResponseConverter extends Converter<Map<String, Object?>, Response?> {
  @override
  Response? convert(Map<String, Object?> input) => Response.fromJson(input);

  @override
  ChunkedConversionSink<Map<String, Object?>> startChunkedConversion(
    Sink<Response?> sink,
  ) {
    return ChannelChunkSink<Map<String, Object?>, Response?>(this, sink);
  }
}

/// The abstract class [ServerCommunicationChannel] defines the behavior of
/// objects that allow an [AnalysisServer] to receive [Request]s and to return
/// both [Response]s and [Notification]s.
abstract class ServerCommunicationChannel {
  /// The single-subscription stream of requests and responses.
  Stream<RequestOrResponse> get requests;

  /// Close the communication channel.
  void close();

  /// Send the given [notification] to the client.
  void sendNotification(Notification notification);

  /// Send the given [request] to the client.
  void sendRequest(Request request);

  /// Send the given [response] to the client.
  void sendResponse(Response response);
}
