import 'dart:async';
import 'dart:isolate';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:logger/logger.dart';

/// Ein PortController mit verschiedenen Funktionen zur Kommunikation zwischen
/// main und anderen Isolates
class PortController {
  /// Gibt eine Instanz dieser Klasse zurück
  factory PortController() => _instance;

  PortController._internal() {
    if (!kIsWeb && ServicesBinding.rootIsolateToken != null) {
      _registerRootIsolateTokenSender();
    }
  }

  static final PortController _instance = PortController._internal();
  final Logger _logger = Logger();

  /// Fügt einen Port mit [name] zum NameServer hinzu
  void addPort(final SendPort sendPort, final String name) {
    IsolateNameServer.removePortNameMapping(name);
    IsolateNameServer.registerPortWithName(sendPort, name);
  }

  /// Gibt einen Port mit [name] vom NameServer zurück, falls gefunden
  SendPort? getPort(final String name) =>
      IsolateNameServer.lookupPortByName(name);

  /// Gibt das [RootIsolateToken] der main-Isolate zurück
  Future<RootIsolateToken?> getRootIsolateToken() async {
    _logger.d('Trying to retrieve RootIsolateToken...');

    final ReceivePort receivePort = ReceivePort();

    final SendPort? rootPort = IsolateNameServer.lookupPortByName(
      'root-isolate-token',
    );

    if (rootPort == null) {
      _logger.e("Couldn't get Port from IsolateNameServer!");
      receivePort.close();
      return null;
    }

    _logger.i('Sending communication attempt...');
    rootPort.send(receivePort.sendPort);

    try {
      final dynamic message = await receivePort.first;

      if (message is RootIsolateToken) {
        _logger.i('Got RootIsolateToken, returning...');
        return message;
      }

      _logger.w("Couldn't get RootIsolateToken!");
      return null;
    } finally {
      _logger.i('Closing receivePort...');
      receivePort.close();
    }
  }

  void _registerRootIsolateTokenSender() {
    final ReceivePort receivePort = ReceivePort()
      ..listen((final value) {
        _logger.d('Received Message with $value');

        if (value is SendPort) {
          value.send(ServicesBinding.rootIsolateToken);
        }
      });

    addPort(receivePort.sendPort, 'root-isolate-token');
  }
}
