//
//  Conference.m
//  CocoaAsyncSocket
//
//  Created by Doha Tubaileh on 25/5/2021.
//

#import <Foundation/Foundation.h>
#import "WebRTCConference.h"
#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>
#import <Rainbow/Rainbow.h>
#import "IEventEmmiter.h"
#import "Navigation.h"
#import <Rainbow/ConferencesManagerService.h>
#import "RTCCall+DictRepresentation.h"
#import "ConferenceEventsNames.h"
#import "ConferenceParticipant+DictRepresentation.h"
#import "BuubleEventsNames.h"
#import "Contact+DictRepresentation.h"

@interface WebRTCConference ()
@property (nonatomic,strong) RoomsService *roomService;
@property (nonatomic,strong) ConferencesManagerService *conferenceService;
@property (nonatomic,strong) MyUser *myUser;
@property (nonatomic,strong) RTCService *rtcService;
@property (nonatomic,strong) RTCCall *currentCall;
@property (nonatomic,strong) VideoPublisher *addedPublisher;
@property (strong, nonatomic) RTCVideoTrack *localVideoTrack;
@property (strong, nonatomic) Room *activeConferenceRoom;
@property (strong, nonatomic) RTCVideoTrack *sharingVideoTrack;
@property (strong, nonatomic) IEventEmmiter *eventEmitter;
@property (nonatomic, weak) RCTBridge *bridge;
@end

@implementation WebRTCConference
RCT_EXPORT_MODULE();

- (instancetype)init {
    self = [super init];
    if (self) {
        _roomService = [ServicesManager sharedInstance].roomsService;
        _conferenceService = [ServicesManager sharedInstance].conferencesManagerService;
        _myUser = [ServicesManager sharedInstance].myUser;
        _rtcService = [ServicesManager sharedInstance].rtcService;
        [self registerConferenceEvents];
    }
    return self;
}

- (void)dealloc {
    [self unRegisterConferenceEvents];
}

#pragma mark - Conference React Action Methodes

RCT_EXPORT_METHOD(startConference: (NSString*) bubbleId) {
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    if (foundedRoom) {
        _activeConferenceRoom = foundedRoom;
        NSLog(@"[WebRTCConference] %@ with Room: %@",NSStringFromSelector(_cmd),foundedRoom.displayName);
        [_conferenceService startOrJoin:foundedRoom forceLocalVideo:NO completionHandler:^(NSError * _Nullable error) {
            if (error) {
                NSLog(@"[WebRTCConference] startConference: ERROR: %@",error.localizedDescription);
                if (error.errorDetailsCode == 403060) {
                    // This means that Conference is locked and can't join
                    [self sendEventWithName:GetConferenceLockState eventBody: @YES];
                }
            }
            else {
                NSLog(@"[WebRTCConference] startConference Success");
            }
        }];
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find Room with id : %@",NSStringFromSelector(_cmd),bubbleId);
    }
}

RCT_EXPORT_METHOD(addVideoToConference) {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    [self.rtcService addVideoMediaToCall:_currentCall];
}

RCT_EXPORT_METHOD(removeVideoFromConference) {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    [self.rtcService removeVideoMediaFromCall:_currentCall];
}

RCT_EXPORT_METHOD(declineCall: (NSString*) bubbleId) {
    NSLog(@"[WebRTCConference] %@ with bubbleId: %@",NSStringFromSelector(_cmd), bubbleId);
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    if (foundedRoom && foundedRoom.conference) {
        [self declineCancelOrHangupConference:foundedRoom.conference];
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find room with id: %@",NSStringFromSelector(_cmd),bubbleId);
    }
}

RCT_EXPORT_METHOD(endCall: (NSString*) bubbleId) {
    NSLog(@"[WebRTCConference] %@ with bubbleId: %@",NSStringFromSelector(_cmd),bubbleId);
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    if (foundedRoom && foundedRoom.conference) {
        [self declineCancelOrHangupConference:foundedRoom.conference];
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find room with id: %@",NSStringFromSelector(_cmd),bubbleId);
    }
}

RCT_EXPORT_METHOD(joinConference: (NSString*) bubbleId) {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    [self startConference:bubbleId];
    
}

#pragma mark - Conference Participant React Action Methodes

RCT_EXPORT_METHOD(CheckConferenceAvailability:(NSString*) bubbleId) {
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    if (foundedRoom) {
        BOOL isAdmin = foundedRoom.isAdmin;
        BOOL canStartConference = isAdmin && _myUser.isAllowedToParticipateInWebRTCMobile && _myUser.isAllowedToUseWebRTCConference;
        BOOL isConferenceJoinable = foundedRoom.isAbleToJoinConference;
        BOOL conferenceAvailability = isConferenceJoinable || canStartConference;
        NSLog(@"[WebRTCConference] %@: %d",NSStringFromSelector(_cmd),conferenceAvailability);
        [self sendEventWithName:ConferenceCapability eventBody:[NSNumber numberWithBool:conferenceAvailability]];
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find Room with id : %@",NSStringFromSelector(_cmd),bubbleId);
    }

}

RCT_EXPORT_METHOD(areAllUserMuted: (NSString*) bubbleId) {
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    if (foundedRoom) {
        Conference *conference = foundedRoom.conference;
        if (conference) {
            BOOL areAllUserMuted = conference.allParticipantsAreMuted;
            NSLog(@"[WebRTCConference] %@: %d",NSStringFromSelector(_cmd),areAllUserMuted);
            [self sendEventWithName:AreAllConfUserMuted eventBody:[NSNumber numberWithBool:areAllUserMuted]];
        }
        else {
            NSLog(@"[WebRTCConference] %@ ERROR: Could not find conference with room id : %@",NSStringFromSelector(_cmd),bubbleId);
        }
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find Room with id : %@",NSStringFromSelector(_cmd),bubbleId);
    }
}

RCT_EXPORT_METHOD(unMuteAllParticipants: (NSString*) bubbleId) {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    if (foundedRoom) {
        Conference *conference = foundedRoom.conference;
        if (conference) {
            [_conferenceService changeMuteAllParticipantsConference:foundedRoom allMuted:NO completionHandler:^(NSError * _Nullable error) {
                NSLog(@"[WebRTCConference] ERROR: Coudnot unMuteAllParticipants");
            }];
        }
        [self areAllUserMuted:foundedRoom.jid];
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find Room with id : %@",NSStringFromSelector(_cmd),bubbleId);
    }
}

RCT_EXPORT_METHOD(muteAllParticipants: (NSString*) bubbleId) {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    if (foundedRoom) {
        Conference *conference = foundedRoom.conference;
        if (conference) {
            [_conferenceService changeMuteAllParticipantsConference:foundedRoom allMuted:YES completionHandler:^(NSError * _Nullable error) {
                NSLog(@"[WebRTCConference] ERROR: Coudnot muteAllParticipants");
            }];
        }
        [self areAllUserMuted:foundedRoom.jid];
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find Room with id : %@",NSStringFromSelector(_cmd),bubbleId);
    }
}

RCT_EXPORT_METHOD(delegateConference: (NSString*) bubbleId participantId:(NSString*)participantId) {
    NSLog(@"[WebRTCConference] %@ with conferenceRoomId: %@, participantId: %@",NSStringFromSelector(_cmd),bubbleId,participantId);
    Room *foundedRoom = [_roomService getRoomByRainbowID:bubbleId];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.participantId == %@",participantId];
    if (foundedRoom) {
        Conference *conference = foundedRoom.conference;
        if (conference) {
            RoomConfParticipant *contactParticipant = (RoomConfParticipant*)[conference.participants filteredArrayUsingPredicate:predicate].firstObject;
            [_conferenceService delegate:foundedRoom participant:contactParticipant completionHandler:^(NSError * _Nullable error) {
                if (error) {
                    NSLog(@"[WebRTCConference] %@ ERROR with error msd: %@",NSStringFromSelector(_cmd),error);
                }
                else {
                    NSLog(@"[WebRTCConference] %@ SUCCESS",NSStringFromSelector(_cmd));
                    [self endCall];
                }
            }];
        }
        else {
            NSLog(@"[WebRTCConference] %@ ERROR: Could not find Room with id : %@",NSStringFromSelector(_cmd),bubbleId);
        }
    }
}


RCT_EXPORT_METHOD(lockConference: (NSString*) conferenceRoomId) {
    NSLog(@"[WebRTCConference] %@ with conferenceRoomId: %@",NSStringFromSelector(_cmd),conferenceRoomId);
    Room *foundedRoom = [_roomService getRoomByRainbowID:conferenceRoomId];
    if (foundedRoom) {
        Conference *conference = foundedRoom.conference;
        if (conference) {
            if (conference.isLocked) {
                [_conferenceService changeLockState:foundedRoom locked:NO completionHandler:^(NSError * _Nullable error) {
                    NSLog(@"[WebRTCConference] unlockConference ERROR: %@",error);

                }];
            }
            else {
                [_conferenceService changeLockState:foundedRoom locked:YES completionHandler:^(NSError * _Nullable error) {
                    NSLog(@"[WebRTCConference] lockConference ERROR: %@",error);

                }];
            }
        }
    }
    else {
        NSLog(@"[WebRTCConference] %@ ERROR: Could not find Room with id : %@",NSStringFromSelector(_cmd),conferenceRoomId);
    }
}


#pragma mark - Calculated methods

-(void) endCall {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    if (_currentCall.status == CallStatusRinging) {
        if (_currentCall.isIncoming) {
            [_rtcService declineIncomingCall:_currentCall];
        } else {
            [_rtcService cancelOutgoingCall:_currentCall];
        }
    } else {
        [_rtcService hangupCall:_currentCall];
    }
}

-(void) declineCancelOrHangupConference: (Conference*)conference {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    [self endCall];
    if (conference) {
        if (conference.isOwner) {
            [_conferenceService hangup:_activeConferenceRoom completionHandler:^(NSError * _Nullable error) {
                if (error) {
                    NSLog(@"[WebRTCConference] declineCancelOrHangupAction Error %@",error);
                }
            }];
        }
    }
    else if (conference.getMyParticipant){
        [_conferenceService hangup:conference.getMyParticipant room:_activeConferenceRoom completionHandler:^(NSError * _Nullable error) {
            if (error) {
            }
        }];
     
    }
}

- (RTCVideoTrack*)getStreamFromPublisherId :(NSString *) publisherId {
    NSLog(@"[WebRTCConference]  getStreamForReactTag: %@",publisherId);
    RTCVideoTrack *vedioTrack = [_rtcService remoteVideoTrackForPublisherRainbowID:publisherId];
    return vedioTrack;
}

#pragma mark - Conference Call Notifications

-(void)didCallSuccess:(NSNotification *) notification {
    if ([notification.object class] == [RTCCall class]) {
        NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
        RTCCall *rtcCall = (RTCCall*)notification.object;
        if ([rtcCall.peer class] == [Room class]) {
            _currentCall = rtcCall;
            [[Navigation sharedManager] moveViewToCall];
            NSDictionary *conferenceDic = [rtcCall conferenceCallToDictionaryRepresentation];
            [self sendEventWithName:CurrentConferenceUpdated eventBody: conferenceDic];
        }
    }
}

-(void)didUpdateCall:(NSNotification *) notification {
    if ([notification.object class] == [RTCCall class]) {
        NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
        RTCCall *rtcCall = (RTCCall*)notification.object;
        if ([rtcCall.peer class] == [Room class]) {
            [self CheckConferenceAvailability:rtcCall.peer.jid];
            _currentCall = rtcCall;
            [[Navigation sharedManager] moveViewToCall];
            NSDictionary *conferenceDic = [rtcCall conferenceCallToDictionaryRepresentation];
            [self sendEventWithName:CurrentConferenceUpdated eventBody: conferenceDic];

        }
    }
}

-(void)didRemoveCall:(NSNotification *) notification {
    if ([notification.object class] == [RTCCall class]) {
        NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
        RTCCall *rtcCall = (RTCCall*)notification.object;
        if ([rtcCall.peer class] == [Room class]) {
            _currentCall = rtcCall;
            Room *removedBubble = (Room *)rtcCall.peer;
            // TODO: handle if we need at this stage to send bubble has active
            [self sendEventWithName:BubbleHasActiveConference eventBody:@{@"bubbleId":removedBubble.jid,@"hasActiveConference":@NO}];

            [self sendEventWithName:CurrentConferenceUpdated eventBody: NULL];
            [[Navigation sharedManager] moveViewToRainbow];
        }
    }
}

#pragma mark - Conference Participant Notification

-(void)didParticipantHasJoined:(NSNotification *) notification {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    Room *room = (Room*)notification.object[kRoomKey];
    Conference *activeConference = room.conference;
    [self sendParticipantArrayToReactForConference:activeConference.participants];
}

-(void)didParticipantHasBeenUpdated:(NSNotification *) notification {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    ConferenceParticipant *updatedParticipant = (ConferenceParticipant*)notification.object[kConferenceParticipantKey];
    [self sendEventWithName:ConferenceParticipantStatusUpdate eventBody: updatedParticipant.conferenceParticipantDictionaryRepresentation];
}

-(void)didParticipantHasLeft:(NSNotification *) notification {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    Room *room = (Room*)notification.object[kRoomKey];
    Conference *activeConference = room.conference;
    ConferenceParticipant *leftedParticipant = (ConferenceParticipant*)notification.object[kConferenceParticipantKey];
    NSMutableArray *conferenceParticipants = activeConference.participants.mutableCopy;
    [conferenceParticipants removeObject:leftedParticipant];
    [self sendParticipantArrayToReactForConference:conferenceParticipants];
}

-(void) sendParticipantArrayToReactForConference: (NSArray<ConferenceParticipant*> *) conferenceAttendee {
    NSMutableArray *participants = [[NSMutableArray alloc] init];
    for (ConferenceParticipant *participant in conferenceAttendee) {
        [participants addObject:participant.conferenceParticipantDictionaryRepresentation];
    }
    [self sendEventWithName:ConferenceAttendeesUpdates eventBody: participants];
}

#pragma mark - Conference Publisher

-(void)didAddPublisher:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didAddPublisher:notification];
        });
        return;
    }
    //get trigger when the one of the user start publishing a vedio stream.
    if ([notification.object isKindOfClass:[NSDictionary class]]) {
          NSDictionary *objectDict = (NSDictionary *)notification.object;

          // Extract the conference participant object using "conferenceParticipantKey"
          RoomConfParticipant *roomConfParticipant = objectDict[@"conferenceParticipantKey"];
          Room *room = objectDict[@"room"];

          if (roomConfParticipant) {
              NSLog(@"[WebRTCConference] Participant: %@", roomConfParticipant);
              Participant *roomParticipant = roomConfParticipant.roomParticipant;
              RainbowContact *rainbowContact = (RainbowContact*)roomParticipant.contact;
              if (roomParticipant && room) {
         

                  [_conferenceService updateVideoSubscriptionForPublisher:roomConfParticipant room:room streamLevel:PublisherStreamLevelMedium completionHandler:^(NSError * _Nullable error) {
                      if (error) {
                          NSLog(@"[WebRTCConference] %@ updateVideoSubscriptionForPublisher ERROR: %@",NSStringFromSelector(_cmd),error);
                      }else {
                        //  NSMutableDictionary *callInfo = self->_currentCall.conferenceCallToDictionaryRepresentation.mutableCopy;

                          NSLog(@"[WebRTCConference] %@ updateVideoSubscriptionForPublisher: SUCCESS",NSStringFromSelector(_cmd));
                      }
                  }];
              }
              VideoPublisher *videoPublisher = roomConfParticipant.videoPublisher;  // Extract video publisher
              if (videoPublisher) {
                  NSLog(@"Video Publisher: %@", videoPublisher);
                  _addedPublisher = videoPublisher;
              }
          } else {
              NSLog(@"No participant data found.");
          }
      } else {
          NSLog(@"Notification object is not a dictionary");
      }
   
}

-(void)didRemovePublisher:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didRemovePublisher:notification];
        });
        return;
    }
    NSDictionary *objectDict = (NSDictionary *)notification.object;

    // Extract the conference participant object using "conferenceParticipantKey"
    RoomConfParticipant *roomConfParticipant = objectDict[@"conferenceParticipantKey"];
    Room *room = objectDict[@"room"];
    NSLog(@"[WebRTCConference] didRemovePublisher  %@, %@ ",roomConfParticipant , room);
    VideoPublisher *removedPublisher = (VideoPublisher*)notification.object[kConferencesManagerDidRemovePublisher];

    [_conferenceService releaseVideoSubscriptionForPublisher:roomConfParticipant room:room];
    NSDictionary *conferenceDic = [_currentCall conferenceCallToDictionaryRepresentation];
    [self sendEventWithName:CurrentConferenceUpdated eventBody: conferenceDic];
//    [self sendEventWithName:HasConferenceParticipantJoinedVideoCall eventBody: @{@"participantId":roomParticipant.getRainbowId,@"isPublishingVideo": @NO}];

}

#pragma mark - Local Video Methods

-(void) didUpdateRemoteVideoTrack:(NSNotification *) notification {
    //Fired when you add remote video to call]
    NSLog(@"[WebRTCConference] didUpdateRemoteVideoTrack %@",notification);

//    if(![NSThread isMainThread]){
//        dispatch_async(dispatch_get_main_queue(), ^{
//            [self didUpdateRemoteVideoTrack:notification];
//        });
//        return;
//    }
//
//    Room *room = (Room*)notification.object[kRoomKey];
//    Conference *activeConference = room.conference;
//    NSLog(@"[WebRTCConference] didUpdateRemoteVideoTrack %@",notification);
//        if (activeConference) {
//            // Screen Sharing for conference call
//            _sharingVideoTrack = [self.rtcService remoteSharingTrackForCall:_currentCall];
//
//            NSDictionary *conferenceDic = [_currentCall conferenceCallToDictionaryRepresentation];
//            [self sendEventWithName:CurrentConferenceUpdated eventBody: conferenceDic];
//
//            // Video from Conference Attendees
//            if (_addedPublisher != nil) {
////                [self sendEventWithName:HasConferenceParticipantJoinedVideoCall eventBody: @{@"participantId":_addedPublisher.getRainbowId,@"isPublishingVideo": [NSNumber numberWithBool:_addedPublisher.isSimulcastActive]}];
//            }
//        }

}

-(void)didAddLocalVideoTrack:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didAddLocalVideoTrack:notification];
        });
        return;
    }
    NSLog(@"[WebRTCConference] didAddLocalVideoTrack %@",NSStringFromSelector(_cmd));
    RTCVideoTrack *localVideoTrack = (RTCVideoTrack *) notification.object;
    if(self.localVideoTrack == localVideoTrack)
        return;

    self.localVideoTrack = nil;
    self.localVideoTrack = localVideoTrack;
}

-(void) didRemoveLocalVideoTrack:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didRemoveLocalVideoTrack:notification];
        });
        return;
    }
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    self.localVideoTrack = nil;
}

#pragma mark - Conference Change Listener
/**
 * Notification sent when we receive a conference state
 * The `object` in the notification is a dictionnary with:
 *      - the conference for the key `kConferenceKey`
 *      - the conference state dictionary for the key `kConferenceStateKey` (lock, locked-by, unlocked-by, active, recording-started, talker-active)
 */
-(void) didReceiveConferenceState:(NSNotification *) notification {
    NSLog(@"[WebRTCConference] %@",NSStringFromSelector(_cmd));
    NSDictionary *updatedInfo = (NSDictionary *) notification.object;
    Room *room = (Room*) updatedInfo[kRoomKey];
    Conference *activeConference = room.conference;
    NSMutableDictionary *callInfo = _currentCall.conferenceCallToDictionaryRepresentation.mutableCopy;
    [callInfo setValue:[NSNumber numberWithBool:[activeConference isLocked]] forKey:@"isLocked"];
    [self sendEventWithName:CurrentConferenceUpdated eventBody: callInfo];
}


-(void) didSpeakingParticipantsStateHasBeenUpdated:(NSNotification *) notification {
    NSDictionary *updatedInfo = (NSDictionary *) notification.object;
    NSArray<ConferenceParticipant*> *updatedSpeakers = (NSArray<ConferenceParticipant*> *) updatedInfo[kConferenceParticipantsKey];
    NSMutableDictionary *callInfo = _currentCall.conferenceCallToDictionaryRepresentation.mutableCopy;
    Contact *activeTalker = [_currentCall getConferenceActiveTalkerContact:updatedSpeakers];
    [callInfo setValue:activeTalker.contactDictionaryRepresentation forKey:@"activeSpeaker"];
    [self sendEventWithName:CurrentConferenceUpdated eventBody: callInfo];
    NSLog(@"[WebRTCConference] %@ with name: %@",NSStringFromSelector(_cmd),activeTalker.displayName);
}

- (RTCVideoTrack*)getMyUserVideoConference {
    NSLog(@"getMyUserVedioConference");
    return self.localVideoTrack;
}

- (RTCVideoTrack*)getScreenShareVideoConference {
    NSLog(@"getMyUserVedioConference");
    return self.sharingVideoTrack;
}

#pragma mark - Register Notifications

-(void)registerConferenceEvents{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didCallSuccess:) name:kTelephonyServiceDidAddCall object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateCall:) name:kTelephonyServiceDidUpdateCall object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemoveCall:) name:kTelephonyServiceDidRemoveCall object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddPublisher:) name:kConferencesManagerDidAddPublisher object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemovePublisher:) name:kConferencesManagerDidRemovePublisher object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateRemoteVideoTrack:) name:kRTCServiceDidAddRemoteVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateRemoteVideoTrack:) name:kRTCServiceDidRemoveRemoteVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didParticipantHasJoined:) name:kConferencesManagerParticipantHasJoined object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didParticipantHasLeft:) name:kConferencesManagerParticipantHasLeft object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didParticipantHasBeenUpdated:) name:kConferencesManagerParticipantHasBeenUpdated object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddLocalVideoTrack:) name:kRTCServiceDidAddLocalVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemoveLocalVideoTrack:) name:kRTCServiceDidRemoveLocalVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveConferenceState:) name:kConferencesManagerDidDelegateConference object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didSpeakingParticipantsStateHasBeenUpdated:) name:kConferencesManagerSpeakingParticipantsStateHasBeenUpdated object:nil];
}

-(void)unRegisterConferenceEvents{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kTelephonyServiceDidAddCall object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kTelephonyServiceDidUpdateCall object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kTelephonyServiceDidRemoveCall object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kConferencesManagerDidAddPublisher object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kConferencesManagerDidAddPublisher object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidAddRemoteVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kConferencesManagerDidRemovePublisher object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kConferencesManagerDidDelegateConference object:nil];
}
- ( IEventEmmiter *)eventEmitter {
    if (!_eventEmitter) {
        _eventEmitter = [self.bridge moduleForClass:[IEventEmmiter class]];
    }
    return _eventEmitter;
}

- (void)sendEventWithName:(NSString *)eventName eventBody:(id)eventBody {
    IEventEmmiter *eventEmitter = [self eventEmitter];
    if (eventEmitter) {
        NSLog(@" [WebRTCConference] sendEventWithName: %@", eventName);
        [eventEmitter sendEvent:eventName eventBody:eventBody];
    } else {
        NSLog(@"[WebRTCConference] sendEventWithName:  Bridge is not ready. Cannot send event: %@", eventName);
    }
}
@end
