|  | @@ -1,19 +1,26 @@
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  import 'dart:async';
 | 
	
		
			
				|  |  |  import 'dart:convert';
 | 
	
		
			
				|  |  | +import 'dart:math';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import 'package:the_paint/src/generated/client.pbgrpc.dart';
 | 
	
		
			
				|  |  |  import 'package:flutter_webrtc/flutter_webrtc.dart';
 | 
	
		
			
				|  |  |  import 'package:web_socket_channel/web_socket_channel.dart';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +class WebRTCChannels extends Resolver {
 | 
	
		
			
				|  |  | +  WebRTCChannels._internal();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  static final WebRTCChannels _singleton = WebRTCChannels._internal();
 | 
	
		
			
				|  |  | +  static final Map<String, Duplex<RTCDataChannelMessage>> mapf = {};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // RTCPeerConnection? _pc;
 | 
	
		
			
				|  |  | +  static bool connected = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class WebRTCChannels {
 | 
	
		
			
				|  |  | -  bool _inCalling = false;
 | 
	
		
			
				|  |  | -  RTCPeerConnection? _pc;
 | 
	
		
			
				|  |  | -  RTCDataChannel? _dc;
 | 
	
		
			
				|  |  | -  PaintClient? _grpcClient;
 | 
	
		
			
				|  |  | +  Stream<RTCData>? _rtcData;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  WebRTCChannels();
 | 
	
		
			
				|  |  | +  factory WebRTCChannels() {
 | 
	
		
			
				|  |  | +    // _singleton.connect(() {});
 | 
	
		
			
				|  |  | +    return _singleton;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    final Map<String, dynamic> _iceServers = {
 | 
	
		
			
				|  |  |      'iceServers': [
 | 
	
	
		
			
				|  | @@ -42,50 +49,58 @@ class WebRTCChannels {
 | 
	
		
			
				|  |  |      Uri.parse('ws://localhost:8081/ws'),
 | 
	
		
			
				|  |  |    );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Future<void> connect(Function callback) async {
 | 
	
		
			
				|  |  | +  Future<void> connect() async {
 | 
	
		
			
				|  |  | +    if (connected) {
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    connected = true;
 | 
	
		
			
				|  |  |      print("Connecting WEBSOCKET...");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    _pc = await createPeerConnection(
 | 
	
		
			
				|  |  | +    _rtcData = _channel.stream
 | 
	
		
			
				|  |  | +        .map((event) => RTCData.fromBuffer(event))
 | 
	
		
			
				|  |  | +        .asBroadcastStream();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // await waitForData();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Future<RTCData> waitForData(String event /*add client id*/) async {
 | 
	
		
			
				|  |  | +    await connect();
 | 
	
		
			
				|  |  | +    return await _rtcData!.firstWhere((msg) => msg.event == event);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Future<Duplex<RTCDataChannelMessage>> createAndSubscribePeer(
 | 
	
		
			
				|  |  | +      RTCSessionDescription session) async {
 | 
	
		
			
				|  |  | +    Duplex<RTCDataChannelMessage> dup = Duplex();
 | 
	
		
			
				|  |  | +    var key = session.type!;
 | 
	
		
			
				|  |  | +    if (mapf.containsKey(key)) {
 | 
	
		
			
				|  |  | +      return mapf[key]!;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    mapf[key] = dup;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var pc = await createPeerConnection(
 | 
	
		
			
				|  |  |        _iceServers,
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    _pc!.onIceCandidate = (candidate) async {
 | 
	
		
			
				|  |  | -      print('pc2: onIceCandidate: ${candidate.candidate}');
 | 
	
		
			
				|  |  | -      await sendCandidate(candidate);
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | +    await pc.setRemoteDescription(session);
 | 
	
		
			
				|  |  | +    var answer = await pc.createAnswer();
 | 
	
		
			
				|  |  | +    await pc.setLocalDescription(answer);
 | 
	
		
			
				|  |  | +    await sendLocalDescription(pc);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    _pc!.onDataChannel = (channel) {
 | 
	
		
			
				|  |  | -      RTCDataChannel dc = channel;
 | 
	
		
			
				|  |  | -      print('\nnew channel');
 | 
	
		
			
				|  |  | -      dc.onDataChannelState = (state) {
 | 
	
		
			
				|  |  | +    pc.onDataChannel = (channel) {
 | 
	
		
			
				|  |  | +      print('\nnew channel ${channel.id} ${channel.label}');
 | 
	
		
			
				|  |  | +      channel.onDataChannelState = (state) {
 | 
	
		
			
				|  |  |          print('\ndc2: change state: ${state.toString()}');
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  | -      dc.onMessage = (data) {
 | 
	
		
			
				|  |  | -        callback(MonitorReply.fromBuffer(data.binary));
 | 
	
		
			
				|  |  | -        // dc.send(
 | 
	
		
			
				|  |  | -        // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
 | 
	
		
			
				|  |  | +      channel.onMessage = (data) {
 | 
	
		
			
				|  |  | +        dup.streamToUI.add(data);
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  | +      // add future to send message to channel!!!!
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    final rtcData = _channel.stream.map((event) => RTCData.fromBuffer(event));
 | 
	
		
			
				|  |  | -    await for (var msg in rtcData) {
 | 
	
		
			
				|  |  | -      print(msg.event);
 | 
	
		
			
				|  |  | -      switch (msg.event) {
 | 
	
		
			
				|  |  | -        case "offer":
 | 
	
		
			
				|  |  | -          await _pc!
 | 
	
		
			
				|  |  | -              .setRemoteDescription(RTCSessionDescription(msg.data, msg.event));
 | 
	
		
			
				|  |  | -          var answer = await _pc!.createAnswer();
 | 
	
		
			
				|  |  | -          await _pc!.setLocalDescription(answer);
 | 
	
		
			
				|  |  | -          await sendLocalDescription();
 | 
	
		
			
				|  |  | -          break;
 | 
	
		
			
				|  |  | -        default:
 | 
	
		
			
				|  |  | -          print("invalid event");
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    return dup;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  sendLocalDescription() async {
 | 
	
		
			
				|  |  | -    RTCSessionDescription? answer = await _pc!.getLocalDescription();
 | 
	
		
			
				|  |  | +  sendLocalDescription(RTCPeerConnection pc) async {
 | 
	
		
			
				|  |  | +    RTCSessionDescription? answer = await pc.getLocalDescription();
 | 
	
		
			
				|  |  |      if (answer == null) {
 | 
	
		
			
				|  |  |        return;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -94,10 +109,31 @@ class WebRTCChannels {
 | 
	
		
			
				|  |  |      _channel.sink.add(encodedData);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  sendCandidate(RTCIceCandidate candidate) async {
 | 
	
		
			
				|  |  | +  @override
 | 
	
		
			
				|  |  | +  Future<Duplex<RTCDataChannelMessage>> dataChannel(Point<int> point) async {
 | 
	
		
			
				|  |  |      final encodedData =
 | 
	
		
			
				|  |  | -        RTCData(data: jsonEncode(candidate.toMap()), event: "candidate")
 | 
	
		
			
				|  |  | -            .writeToBuffer();
 | 
	
		
			
				|  |  | +        RTCData(data: point.toString(), event: "req_p").writeToBuffer();
 | 
	
		
			
				|  |  |      _channel.sink.add(encodedData);
 | 
	
		
			
				|  |  | +    var msg = await waitForData("offer");
 | 
	
		
			
				|  |  | +    var session = RTCSessionDescription(msg.data, msg.event);
 | 
	
		
			
				|  |  | +    return await createAndSubscribePeer(session);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class Duplex<T> {
 | 
	
		
			
				|  |  | +  StreamController<T> streamToUI = StreamController.broadcast();
 | 
	
		
			
				|  |  | +  StreamController<T> streamFromUI = StreamController.broadcast();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void send(T serialize) {
 | 
	
		
			
				|  |  | +    streamFromUI.add(serialize);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Stream<T> getStream() {
 | 
	
		
			
				|  |  | +    return streamToUI.stream;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Return to who we need to connect*/
 | 
	
		
			
				|  |  | +abstract class Resolver {
 | 
	
		
			
				|  |  | +  Future<Duplex<RTCDataChannelMessage>> dataChannel(Point<int> point);
 | 
	
		
			
				|  |  | +}
 |