// Copyright 2014 The Flutter Authors. 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' show Completer;
import 'dart:isolate' show ReceivePort;
import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'binary_messenger.dart';
import 'binding.dart';

/// A [BinaryMessenger] for use on background (non-root) isolates.
class BackgroundIsolateBinaryMessenger extends BinaryMessenger {
  BackgroundIsolateBinaryMessenger._();

  final ReceivePort _receivePort = ReceivePort();
  final Map<int, Completer<ByteData?>> _completers = <int, Completer<ByteData?>>{};
  int _messageCount = 0;

  /// The existing instance of this class, if any.
  ///
  /// Throws if [ensureInitialized] has not been called at least once.
  static BinaryMessenger get instance {
    if (_instance == null) {
      throw StateError(
        'The BackgroundIsolateBinaryMessenger.instance value is invalid '
        'until BackgroundIsolateBinaryMessenger.ensureInitialized is '
        'executed.',
      );
    }
    return _instance!;
  }

  static BinaryMessenger? _instance;

  /// Ensures that [BackgroundIsolateBinaryMessenger.instance] has been initialized.
  ///
  /// The argument should be the value obtained from [ServicesBinding.rootIsolateToken]
  /// on the root isolate.
  ///
  /// This function is idempotent (calling it multiple times is harmless but has no effect).
  static void ensureInitialized(ui.RootIsolateToken token) {
    if (_instance == null) {
      ui.PlatformDispatcher.instance.registerBackgroundIsolate(token);
      final portBinaryMessenger = BackgroundIsolateBinaryMessenger._();
      _instance = portBinaryMessenger;
      portBinaryMessenger._receivePort.listen((dynamic message) {
        try {
          final args = message as List<dynamic>;
          final identifier = args[0] as int;
          final bytes = args[1] as Uint8List;
          final byteData = ByteData.sublistView(bytes);
          portBinaryMessenger._completers.remove(identifier)!.complete(byteData);
        } catch (exception, stack) {
          FlutterError.reportError(
            FlutterErrorDetails(
              exception: exception,
              stack: stack,
              library: 'services library',
              context: ErrorDescription('during a platform message response callback'),
            ),
          );
        }
      });
    }
  }

  @override
  Future<void> handlePlatformMessage(
    String channel,
    ByteData? data,
    ui.PlatformMessageResponseCallback? callback,
  ) {
    throw UnimplementedError('handlePlatformMessage is deprecated.');
  }

  @override
  Future<ByteData?>? send(String channel, ByteData? message) {
    final completer = Completer<ByteData?>();
    _messageCount += 1;
    final int messageIdentifier = _messageCount;
    _completers[messageIdentifier] = completer;
    ui.PlatformDispatcher.instance.sendPortPlatformMessage(
      channel,
      message,
      messageIdentifier,
      _receivePort.sendPort,
    );
    return completer.future;
  }

  @override
  void setMessageHandler(String channel, MessageHandler? handler) {
    throw UnsupportedError(
      'Background isolates do not support setMessageHandler(). Messages from the host platform always go to the root isolate.',
    );
  }
}
