import 'dart:io';
import 'dart:isolate';

import 'package:flutter/widgets.dart';
import 'package:flutter_nsd/flutter_nsd.dart';
import 'package:skillcourt/enum/commands.dart';
import 'package:skillcourt/pair.dart';

///
class Pad {
  int _padNum = -1;
  late NsdServiceInfo _nsdServiceInfo;
  late SendPort _sendPort;
  late ReceivePort _receivePort;
  late SendPort _isolateSendPort;
  late ReceivePort _isolateReceivePort;
  Offset? _location;
  int? _angle;
  bool alive = false;
  late Commands currentStatus;
  late String cuid;

  Pad();

  int? get angle => _angle;

  set angle(int? angle) {
    if (angle == null) {
      _angle = null;
    } else {
      _angle = angle % 360;
    }
  }

  static Future<Pad> init(NsdServiceInfo nsdServiceInfo) async {
    final Pad pad = Pad();
    final rp = ReceivePort();
    await Isolate.spawn(pad._listen, rp.sendPort);
    pad._sendPort = await rp.first as SendPort;
    pad._receivePort = ReceivePort();
    pad._sendPort.send(Pair(nsdServiceInfo, pad._receivePort.sendPort));
    pad._nsdServiceInfo = nsdServiceInfo;
    sleep(const Duration(milliseconds: 100));
    return pad;
  }

  Future<void> _listen(SendPort sp) async {
    _isolateSendPort = sp;
    _isolateReceivePort = ReceivePort();
    _isolateSendPort.send(_isolateReceivePort.sendPort);
    late final Socket socket;
    await for (final message in _isolateReceivePort) {
      if (message is Pair<NsdServiceInfo?, SendPort>) {
        final nsdServiceInfo = message.left;
        _isolateSendPort = message.right;
        // print('connecting socket');

        try {
          // print(nsdServiceInfo!.hostname);
          socket = await Socket.connect(nsdServiceInfo!.hostname, 30000);
        } on Exception catch (e) {
          print(e);
          // TODO
        }
        socket.listen((event) {
          _isolateSendPort.send(event);
        }, onError: (error) {
          _isolateSendPort.send(error);

          Isolate.exit();
        }, onDone: () {
          socket.destroy();
          _isolateSendPort.send(null);
          Isolate.exit();
        });
      } else if (message is Pair<String, SendPort>) {
        socket.write(message.left);
        _isolateSendPort = message.right;
      } else if (message is String) {
        socket.write(message);
      } else if (message == null) {
        break;
      }
    }
    _isolateSendPort.send(null);
    socket.destroy();
    Isolate.exit();
  }

  Offset? get location {
    return _location != null ? Offset(_location!.dx, _location!.dy) : null;
  }

  set location(Offset? offset) {
    _location = offset;
  }

  void send(String? message) {
    _sendPort.send(message);
  }

  void registerListener(Function(dynamic message) func) {
    _receivePort.listen((message) {
      func(message);
    });
  }

  set padNum(int num) {
    if (num >= 0) {
      _padNum = num;
    }
  }

  int get padNum => _padNum;

  NsdServiceInfo get nsdServiceInfo {
    return NsdServiceInfo(_nsdServiceInfo.hostname, _nsdServiceInfo.port,
        _nsdServiceInfo.name, _nsdServiceInfo.txt);
  }
}
