// import 'dart:convert';
// import 'dart:async';
// import 'package:flutter/cupertino.dart';
// import 'package:flutter_webrtc/flutter_webrtc.dart';
//
// import '../utils/websocket.dart' if (dart.library.js) '../utils/websocket_web.dart';
// import '../utils/turn.dart' if (dart.library.js) '../utils/turn_web.dart';
//
//
// enum SignalingState { Open, Closed, Error }
// enum CallState { Calling, Ringing, Invite, Connected, Bye }
//
//
// const JsonEncoder _encoder = JsonEncoder();
// const JsonDecoder _decoder = JsonDecoder();
//
// class SessionOneToOne {
//   late String id;
//   late SocketUser local_user;
//   late SocketUser remote_user;
//   SessionOneToOne({required this.id, required this.local_user, required this.remote_user});
//
//
//   late RTCPeerConnection pc;
//   late RTCDataChannel dc;
//   List<RTCIceCandidate> remoteCandidates = [];
// }
//
// class SocketUser{
//   String? id;
//   String? name;
//   String? userAgent;
//   Map? moreInfo;
//
//   SocketUser({required this.id, required this.name, required this.userAgent, required this.moreInfo});
//
//   SocketUser.from(dynamic json){
//     id = json['id'];
//     name = json['name'];
//     userAgent = json['user_agent'];
//     moreInfo = json['more_info'];
//   }
//
//   Map toJson() => {
//     "id": id,
//     "name": name,
//     "user_agent": userAgent,
//     "more_info": moreInfo
//   };
// }
//
// class Signaling {
//   var _host;
//   var _turnCredential;
//
//   SimpleWebSocket? _socket;
//   SessionOneToOne? session;
//
//   Signaling(this._host, {required this.session});
//
//   late MediaStream? localStream;
//   late final List<MediaStream> remoteStreams = <MediaStream>[];
//
//   late Function(SignalingState state) onSignalingStateChange;
//   late Function(SessionOneToOne session, CallState state) onCallStateChange;
//   late  Function(MediaStream stream) onLocalStream;
//   late Function(SessionOneToOne session, MediaStream stream) onAddRemoteStream;
//   late Function(SessionOneToOne session, MediaStream stream) onRemoveRemoteStream;
//   late Function(dynamic event) onPeersUpdate;
//   late Function(dynamic event) onConnected;
//   late Function(dynamic event) onRemoteConnected;
//   late Function(SessionOneToOne session, RTCDataChannel dc, RTCDataChannelMessage data) onDataChannelMessage;
//   late  Function(SessionOneToOne session, RTCDataChannel dc) onDataChannel;
//
//   String? get sdpSemantics => WebRTC.platformIsWindows ? 'plan-b' : 'unified-plan';
//
//   Map<String, dynamic> _iceServers = {
//     'iceServers': [
//       {'url': 'stun:stun.l.google.com:19302'},
//       /*
//        * turn server configuration example.
//       {
//         'url': 'turn:123.45.67.89:3478',
//         'username': 'change_to_real_user',
//         'credential': 'change_to_real_secret'
//       },
//       */
//     ]
//   };
//
//   final Map<String, dynamic> _config = {
//     'mandatory': {},
//     'optional': [
//       {'DtlsSrtpKeyAgreement': true},
//     ]
//   };
//
//   final Map<String, dynamic> _dcConstraints = {
//     'mandatory': {
//       'OfferToReceiveAudio': false,
//       'OfferToReceiveVideo': false,
//     },
//     'optional': [],
//   };
//
//
//   close() async {
//     await finishSessions();
//     _socket?.close();
//   }
//
//   void switchCamera() {
//     if (localStream != null) {
//       Helper.switchCamera(localStream! .getVideoTracks()[0]);
//     }
//   }
//
//   void muteMic() {
//     if (localStream != null) {
//       bool enabled = localStream!.getAudioTracks()[0].enabled;
//       localStream!.getAudioTracks()[0].enabled = !enabled;
//     }
//   }
//
//
//   callAccepted(SessionOneToOne session){
//     _send('call_accepted', {
//       'to': session.remote_user?.id,
//       'from': session.local_user.id,
//       'session_id': session.id,
//     });
//   }
//
//   void offer(String media, bool useScreen) async {
//     if(session == null)
//       return;
//
//     if (media == 'data') {
//       _createDataChannel(session!);
//     }
//     _createOffer(session!, media);
//     onCallStateChange?.call(session!, CallState.Calling);
//   }
//
//   void bye(SessionOneToOne session) {
//     _send('bye', {
//       'session_id': session.id,
//       'from': session.local_user.id,
//     });
//     _closeSession(session);
//   }
//
//   void onMessage(message) async {
//     Map<String, dynamic> mapData = message;
//     var data = mapData['data'];
//
//     switch (mapData['type']) {
//       case 'call_accepted':
//         {
//           onRemoteConnected?.call(data);
//         }
//         break;
//
//       case 'connected':
//         {
//           if (onConnected != null) {
//             onConnected?.call(data);
//           }
//         }
//         break;
//
//       case 'offer':
//         {
//
//           var peerId = data['from'];
//           var description = data['description'];
//           var media = data['media'];
//
//           await _initPeerConnection(session!, media: media, screenSharing: false);
//           await session!.pc?.setRemoteDescription(RTCSessionDescription(description['sdp'], description['type']));
//           await _createAnswer(session!, media);
//
//           if (session!.remoteCandidates!.isNotEmpty) {
//             session!.remoteCandidates!.forEach((candidate) async {
//               await session!.pc?.addCandidate(candidate);
//             });
//             session!.remoteCandidates.clear();
//           }
//           onCallStateChange?.call(session!, CallState.Calling);
//         }
//         break;
//       case 'answer':
//         {
//
//           var description = data['description'];
//           var sessionId = data['session_id'];
//           session!.pc?.setRemoteDescription(
//               RTCSessionDescription(description['sdp'], description['type']));
//         }
//         break;
//       case 'candidate':
//         {
//
//           var peerId = data['from'];
//           var candidateMap = data['candidate'];
//           var sessionId = data['session_id'];
//           RTCIceCandidate candidate = RTCIceCandidate(candidateMap['candidate'],
//               candidateMap['sdpMid'], candidateMap['sdpMLineIndex']);
//
//           if (session != null) {
//             if (session!.pc != null) {
//               await session!.pc?.addCandidate(candidate);
//             } else {
//               session!.remoteCandidates.add(candidate);
//             }
//           } else {
//             // _sessions[sessionId] = SessionOneToOne(pid: peerId, sid: sessionId)
//             //   ..remoteCandidates.add(candidate);
//           }
//         }
//         break;
//       case 'leave':
//         {
//           var peerId = data as String;
//           _closeSessionById(peerId);
//         }
//         break;
//       case 'bye':
//         {
//
//           var sessionId = data['session_id'];
//           print('bye: ' + sessionId);
//           if (session != null) {
//             onCallStateChange?.call(session!, CallState.Bye);
//             _closeSession(session!);
//           }
//         }
//         break;
//       case 'keepalive':
//         {
//           print('keepalive response!');
//         }
//         break;
//       default:
//         break;
//     }
//   }
//
//   Future<void> connect() async {
//     _socket = SimpleWebSocket('$_host/ws');
//
//     if (_turnCredential == null) {
//       try {
//         _turnCredential = await getTurnCredential(_host);
//         /*
//         {
//             "username": "1584195784:mbzrxpgjys",
//             "password": "isyl6FF6nqMTB9/ig5MrMRUXqZg",
//             "ttl": 86400,
//             "uris": ["turn:127.0.0.1:19302?transport=udp"]
//           }
//         */
//         _iceServers = {
//           'iceServers': [
//             {
//               'urls': _turnCredential['uris'][0],
//               'username': _turnCredential['username'],
//               'credential': _turnCredential['password']
//             },
//           ]
//         };
//       } catch (e) {}
//     }
//
//     _socket?.onOpen = () {
//       print('onOpen');
//       onSignalingStateChange?.call(SignalingState.Open);
//       _send('connect', session!.local_user!.toJson());
//     };
//
//     _socket?.onMessage = (message) {
//       print('Received data: ' + message);
//       onMessage(_decoder.convert(message));
//     };
//
//     _socket?.onClose = (int code, String reason) {
//       print('Closed by server [$code => $reason]!');
//       onSignalingStateChange?.call(SignalingState.Closed);
//     };
//
//     await _socket?.connect();
//   }
//
//   Future<MediaStream> createStream(String media, bool userScreen) async {
//     final Map<String, dynamic> mediaConstraints = {
//       'audio': userScreen ? false : true,
//       'video': userScreen
//           ? true
//           : {
//         'mandatory': {
//           'minWidth': '640', // Provide your own width, height and frame rate here
//           'minHeight': '480',
//           'minFrameRate': '30',
//         },
//         'facingMode': 'user',
//         'optional': [],
//       }
//     };
//
//     MediaStream stream = userScreen
//         ? await navigator.mediaDevices.getDisplayMedia(mediaConstraints)
//         : await navigator.mediaDevices.getUserMedia(mediaConstraints);
//     onLocalStream?.call(stream);
//     return stream;
//   }
//
//   Future<SessionOneToOne> _initPeerConnection(SessionOneToOne session, {required String media, required bool screenSharing}) async {
//
//     if (media != 'data')
//       localStream = await createStream(media, screenSharing);
//     print(_iceServers);
//     RTCPeerConnection pc = await createPeerConnection({
//       ..._iceServers,
//       ...{'sdpSemantics': sdpSemantics}
//     }, _config);
//     if (media != 'data') {
//       switch (sdpSemantics) {
//         case 'plan-b':
//           pc.onAddStream = (MediaStream stream) {
//             onAddRemoteStream?.call(session, stream);
//             remoteStreams.add(stream);
//           };
//           await pc.addStream(localStream!);
//           break;
//         case 'unified-plan':
//         // Unified-Plan
//           pc.onTrack = (event) {
//             if (event.track.kind == 'video') {
//               onAddRemoteStream?.call(session, event.streams[0]);
//             }
//           };
//           localStream! .getTracks().forEach((track) {
//             pc.addTrack(track, localStream!);
//           });
//           break;
//       }
//
//       // Unified-Plan: Simuclast
//       /*
//       await pc.addTransceiver(
//         track: _localStream.getAudioTracks()[0],
//         init: RTCRtpTransceiverInit(
//             direction: TransceiverDirection.SendOnly, streams: [_localStream]),
//       );
//
//       await pc.addTransceiver(
//         track: _localStream.getVideoTracks()[0],
//         init: RTCRtpTransceiverInit(
//             direction: TransceiverDirection.SendOnly,
//             streams: [
//               _localStream
//             ],
//             sendEncodings: [
//               RTCRtpEncoding(rid: 'f', active: true),
//               RTCRtpEncoding(
//                 rid: 'h',
//                 active: true,
//                 scaleResolutionDownBy: 2.0,
//                 maxBitrate: 150000,
//               ),
//               RTCRtpEncoding(
//                 rid: 'q',
//                 active: true,
//                 scaleResolutionDownBy: 4.0,
//                 maxBitrate: 100000,
//               ),
//             ]),
//       );*/
//       /*
//         var sender = pc.getSenders().find(s => s.track.kind == "video");
//         var parameters = sender.getParameters();
//         if(!parameters)
//           parameters = {};
//         parameters.encodings = [
//           { rid: "h", active: true, maxBitrate: 900000 },
//           { rid: "m", active: true, maxBitrate: 300000, scaleResolutionDownBy: 2 },
//           { rid: "l", active: true, maxBitrate: 100000, scaleResolutionDownBy: 4 }
//         ];
//         sender.setParameters(parameters);
//       */
//     }
//     pc.onIceCandidate = (candidate) async {
//       if (candidate == null) {
//         print('onIceCandidate: complete!');
//         return;
//       }
//       // This delay is needed to allow enough time to try an ICE candidate
//       // before skipping to the next one. 1 second is just an heuristic value
//       // and should be thoroughly tested in your own environment.
//       await Future.delayed(
//           const Duration(seconds: 1),
//               () => _send('candidate', {
//             'to': session.remote_user?.id,
//             'from': session.local_user.id,
//             'candidate': {
//               'sdpMLineIndex': candidate.sdpMLineIndex, // sdpMlineIndex or sdpMLineIndex
//               'sdpMid': candidate.sdpMid,
//               'candidate': candidate.candidate,
//             },
//             'session_id': session.id,
//           }));
//     };
//
//     pc.onIceConnectionState = (state) {};
//
//     pc.onRemoveStream = (stream) {
//       onRemoveRemoteStream?.call(session, stream);
//       remoteStreams.removeWhere((it) {
//         return (it.id == stream.id);
//       });
//     };
//
//     pc.onDataChannel = (channel) {
//       _addDataChannel(session, channel);
//     };
//
//     session.pc = pc;
//     return session;
//   }
//
//   void _addDataChannel(SessionOneToOne session, RTCDataChannel channel) {
//     channel.onDataChannelState = (e) {};
//     channel.onMessage = (RTCDataChannelMessage data) {
//       onDataChannelMessage?.call(session, channel, data);
//     };
//     session.dc = channel;
//     onDataChannel?.call(session, channel);
//   }
//
//   Future<void> _createDataChannel(SessionOneToOne session, {label= 'fileTransfer'}) async {
//     RTCDataChannelInit dataChannelDict = RTCDataChannelInit()
//       ..maxRetransmits = 30;
//     RTCDataChannel channel =
//     await session.pc .createDataChannel(label, dataChannelDict);
//     _addDataChannel(session, channel);
//   }
//
//   Future<void> _createOffer(SessionOneToOne session, String media) async {
//     try {
//       RTCSessionDescription s =
//       await session.pc .createOffer(media == 'data' ? _dcConstraints : {});
//       await session.pc .setLocalDescription(s);
//       _send('offer', {
//         'to': session.remote_user?.id,
//         'from': session.local_user.id,
//         'description': {'sdp': s.sdp, 'type': s.type},
//         'session_id': session.id,
//         'media': media,
//       });
//     } catch (e) {
//       print(e.toString());
//     }
//   }
//
//   Future<void> _createAnswer(SessionOneToOne session, String media) async {
//     try {
//       RTCSessionDescription s =
//       await session.pc .createAnswer(media == 'data' ? _dcConstraints : {});
//       await session.pc .setLocalDescription(s);
//       _send('answer', {
//         'to': session.remote_user?.id,
//         'from': session.local_user.id,
//         'description': {'sdp': s.sdp, 'type': s.type},
//         'session_id': session.id,
//       });
//     } catch (e) {
//       print(e.toString());
//     }
//   }
//
//   _send(event, data) {
//     var request = Map();
//     request["type"] = event;
//     request["data"] = data;
//     _socket?.send(_encoder.convert(request));
//   }
//
//   Future<void> finishSessions() async {
//     _closeSessionById(session!.id);
//   }
//
//   void _closeSessionById(String sessionId) {
//     if (session != null && session!.id == sessionId) {
//       _closeSession(session!);
//       onCallStateChange?.call(session!, CallState.Bye);
//     }
//   }
//
//   Future<void> _closeSession(SessionOneToOne session) async {
//     localStream?.getTracks()?.forEach((element) async {
//       await element.stop();
//     });
//     await localStream?.dispose();
//     localStream = null;
//
//     await session.pc?.close();
//     await session.dc?.close();
//   }
// }
