//
//  WebrtcP2P.m
//  rainbow
//
//  Created by Doha Tubaileh on 9/16/19.
//  Copyright © 2019 Facebook. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>
#import <Rainbow/Rainbow.h>
#import <WebRTC/WebRTC.h>
#import "WebrtcP2P.h"
#import "RTCCall+DictRepresentation.h"
#import "Navigation.h"
#import "WebRtcEvents.h"
#import "IEventEmmiter.h"
#import "WebRtcEvents.h"
#import <React/RCTRootView.h>
#import "RNRainbowRn.h"

@interface RTCCameraPreviewView (PreviewLayer)
- (AVCaptureVideoPreviewLayer *)previewLayer;
@end


@interface WebrtcP2P ()
@property (nonatomic, weak) IBOutlet RTCCameraPreviewView *localVideoView;
@property (nonatomic, weak) IBOutlet RTCEAGLVideoView *remoteVideoView;
@property (strong, nonatomic) ContactsManagerService *contactManager;
@property (strong, nonatomic) RTCService *rtcService;
@property (strong, nonatomic) RTCCall *currentCall;
@property (strong, nonatomic) RTCVideoTrack *localVideoTrack;
@property (strong, nonatomic) RTCVideoTrack *remoteVideoTrack;
@property (strong, nonatomic) RTCVideoTrack *sharingVideoTrack;
@property (strong, nonatomic) RTCCameraVideoCapturer *cameraVideoCapturer;
@property (strong, nonatomic) TelephonyService *telephonyService;
@property (nonatomic, strong) IEventEmmiter *eventEmitter;
@property (nonatomic, weak) RCTBridge *bridge;
@end

@implementation WebrtcP2P
RCT_EXPORT_MODULE();

- (instancetype)init {
        self = [super init];
        if (self) {
            [self registerTelephonyEvents];
            [self registerWebrtcEvents];
            _localVideoTrack = nil;
            _remoteVideoTrack = nil;
            _contactManager = [ServicesManager sharedInstance].contactsManagerService;
            _rtcService = [ServicesManager sharedInstance].rtcService;
            _telephonyService  = [ServicesManager sharedInstance].telephonyService;
        }
        return self;
}

- (void)dealloc {
    _localVideoTrack = nil;
    _remoteVideoTrack = nil;
    _contactManager = nil;
    _rtcService = nil;
    _currentCall = nil;
    _localVideoTrack = nil;
    _remoteVideoTrack = nil;
    [self unRegisterWebrtcEvents];
    [self unRegisterTelephonyEvents];
}

#pragma mark - Webrct P2P React methodes

RCT_EXPORT_METHOD(startCall:(NSString*)contactId isVideo:(BOOL)isVideo subject:(NSString*)subject) {
    NSLog(@"[WebrtcP2P] startCall called");
    [_contactManager searchRainbowContactWithJid:contactId withCompletionHandler:^(RainbowContact * _Nullable foundContact) {
        if (foundContact == nil) {
            NSLog(@"[WebrtcP2P] startCall: no contact is found with id %@",contactId);
            return ;
        }
        if([self checkMicrophoneAccess]) {
            RTCCallFeatureFlags callFeature = isVideo ? RTCCallFeatureLocalVideo | RTCCallFeatureAudio : RTCCallFeatureAudio;
            self->_currentCall = [self->_rtcService startOutgoingP2PCallTo:foundContact withFeatures:callFeature];

        
            NSLog(@"[WebrtcP2P] startCall WebrtcP2P %@",self->_currentCall);
            if (self->_currentCall != nil) {
                [[Navigation sharedManager] moveViewToCall];
            } else {
                NSLog(@"[WebrtcP2P] error with WebrtcP2P calling");
                //TODO: send msg to the user error msg
                return;
            }
        }
    }];

}


RCT_EXPORT_METHOD(takeCall:(BOOL)isVideo) {
    NSLog(@"[WebrtcP2P] takeCall called isVideo: %@",[NSNumber numberWithBool:isVideo]);
    if (isVideo) {
        [_rtcService acceptIncomingCall:_currentCall withFeatures:RTCCallFeatureRemoteVideo|RTCCallFeatureLocalVideo|RTCCallFeatureAudio];
    }
    else {
        [_rtcService acceptIncomingCall:_currentCall withFeatures:RTCCallFeatureAudio];
    }
}


-(void) endCall {
    RTCCall *call = _rtcService.calls.firstObject;
    if (call.status == CallStatusRinging) {
        if (call.isIncoming) {
            [_rtcService declineIncomingCall:call];
        } else {
            [_rtcService cancelOutgoingCall:call];
        }
    } else {
        [_rtcService hangupCall:call];
    }
}

RCT_EXPORT_METHOD(rejectCall) {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self rejectCall];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] rejectCall called");
    [self endCall];
    [[Navigation sharedManager] moveViewToRainbow];
}

RCT_EXPORT_METHOD(hangupCall) {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self hangupCall];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] hangupCall called");
    [self endCall];
    [[Navigation sharedManager] moveViewToRainbow];
}

RCT_EXPORT_METHOD(switchCamera) {
    NSLog(@"[WebrtcP2P] switchCamera called");
    [_rtcService switchTheVideoCapturerCamera];
}

#pragma mark - Enable/Disable Local Video

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

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

#pragma mark - CaptureSession Settings

-(void)statsUpdated:(NSNotification *)notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self statsUpdated:notification];
        });
        return;
    }
    if([notification.object class] == [RTCCall class]){
        NSLog(@"statsUpdated notification");
    }
}

-(void)didAllowMicrophone:(NSNotification *)notification {
    if([notification.object class] == [RTCCall class]){
        NSLog(@"didAllowMicrophone notification");
    }
}

-(void)didRefuseMicrophone:(NSNotification *)notification {
    if([notification.object class] == [RTCCall class]){
        NSLog(@"didRefuseMicrophone notification");
    }
}

#pragma mark - Local video call notifications

-(void) rotateLocalVideoView {
    AVCaptureConnection *previewLayerConnection = self.localVideoView.previewLayer.connection;
    if ([previewLayerConnection isVideoOrientationSupported]){
        AVCaptureVideoOrientation videoOrientation;
        switch ([[UIApplication sharedApplication] statusBarOrientation]) {
            default:
            case UIInterfaceOrientationPortrait:
                videoOrientation = AVCaptureVideoOrientationPortrait;
                break;
            case UIInterfaceOrientationPortraitUpsideDown:
                videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
                break;
            case UIInterfaceOrientationLandscapeLeft:
                videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
                break;
            case UIInterfaceOrientationLandscapeRight:
                videoOrientation = AVCaptureVideoOrientationLandscapeRight;
                break;
        }
        [previewLayerConnection setVideoOrientation:videoOrientation];
    }
}

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

-(void)didAddLocalVideoTrack:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didAddLocalVideoTrack:notification];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] didAddLocalVideoTrack notification");
    RTCVideoTrack *localVideoTrack = (RTCVideoTrack *) notification.object;
    if(self.localVideoTrack == localVideoTrack)
        return;
    self.localVideoTrack = nil;
    self.localVideoTrack = localVideoTrack;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStartCaptureSession:) name:AVCaptureSessionDidStartRunningNotification object:nil];
}

-(void) didRemoveLocalVideoTrack:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didRemoveLocalVideoTrack:notification];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] didRemoveLocalVideoTrack notification");
    [self removeLocalVideoTrack];
}

-(void) didStartCaptureSession:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self didStartCaptureSession:notification];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] didStartCaptureSession notification");
    [self rotateLocalVideoView];
}

#pragma mark - Remote video call notifications

-(void) didAddRemoteVideoTrack:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didAddRemoteVideoTrack:notification];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] didAddRemoteVideoTrack notification");
    [self updateRemoteVideo];
}

-(void) didRemoveRemoteVideoTrack:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didRemoveRemoteVideoTrack:notification];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] didRemoveRemoteVideoTrack notification");
    [self updateRemoteVideo];
    if( _currentCall.class == RTCCall.class){
        // We had a video and there is no more video, so remove it.
        NSMutableDictionary *callDic = [_currentCall rtcCallToDictionaryRepresentation].mutableCopy;
        if (callDic == nil || callDic.count == 0) {
            // For some reason in PBX the SDK fire up this method
            return;
        }
        [self sendEventWithName:P2PCallUpdated eventBody:callDic];
        return;
    }
}


-(void) removeLocalVideoTrack {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:nil];
    self.localVideoTrack = nil;
    self.localVideoView.captureSession = nil;
    self.localVideoView.hidden = YES;
}

-(void)updateRemoteVideo {
    _remoteVideoTrack = nil;
    _sharingVideoTrack = nil;

    if (_currentCall.isSharingEnabled) {
        self.sharingVideoTrack = [self.rtcService remoteSharingTrackForCall:_currentCall];
        self.remoteVideoTrack = [self.rtcService remoteVideoTrackForCall:_currentCall];
        NSLog(@"[WebrtcP2P] updateRemoteVideo : sharingVideoTrack%@", self.sharingVideoTrack);

    }
    else {
        self.remoteVideoTrack = [self.rtcService remoteVideoTrackForCall:_currentCall];
        NSLog(@"[WebrtcP2P] updateRemoteVideo : remoteVideoTrack %@", self.remoteVideoTrack);

    }
}


#pragma mark - Others

-(BOOL) checkMicrophoneAccess {
    if(self.rtcService.microphoneAccessGranted){
        return YES;
    } else {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Access to microphone" message:@"Without your auhtorisation to use microphone, the callee will not be able to hear you during call." preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
                                                         handler:^(UIAlertAction * action) {
            [alert dismissViewControllerAnimated:YES completion:nil];
        }];
        UIAlertAction* settingsAction = [UIAlertAction actionWithTitle:@"Go to Settings" style:UIAlertActionStyleDefault
                                                               handler:^(UIAlertAction * action) {
            [alert dismissViewControllerAnimated:YES completion:nil];
            if (@available(iOS 10.0, *)) {
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
            } else {
                //TODO: handle this side for ios less that 10
                // Fallback on earlier versions
            }
        }];
        [alert addAction:settingsAction];
        [alert addAction:okAction];
        [[[RNRainbowRn sharedManager] navigationController] presentViewController:alert animated:YES completion:nil];
    }
    return NO;
}

- (RTCVideoTrack*)getStreamForReactTag :(NSString *) streamReactTag {
    NSLog(@"[WebrtcP2P] getStreamForReactTag: %@",streamReactTag);
    if([streamReactTag containsString:@"local"]){
        return _localVideoTrack;
    }
    else if ([streamReactTag containsString:@"share"]) {
        return _sharingVideoTrack;
    }
    else {
        return _remoteVideoTrack;
    }
}

#pragma mark - Telephony-Listerns

-(void) didAddCall:(NSNotification *) notification {
    if(![NSThread isMainThread]){
          dispatch_async(dispatch_get_main_queue(), ^{
              [self didAddCall:notification];
          });
          return;
      }
      
    NSLog(@"[WebrtcP2P] didAddCall");
    if ([notification.object class] == [RTCCall class]) {
        RTCCall *rtcCall = (RTCCall*)notification.object;
        // Handle the Contact WebrtcP2P

        if ([rtcCall.peer class] == [RainbowContact class]) {
            NSLog(@"[WebrtcP2P] didAddCall: rtcCall ");

            _currentCall = rtcCall;//outgoing//incoming just for china
            NSDictionary *rtcCallDic = rtcCall.rtcCallToDictionaryRepresentation;
            [self sendEventWithName:P2PCallUpdated eventBody:rtcCallDic];
        }
    }
}
- (void)didRemoveCall:(NSNotification *)notification {
    NSLog(@"[WebrtcP2P] didRemoveCall ");

    if (![NSThread isMainThread]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didRemoveCall:notification];
        });
        return;
    }

    RTCCall *call = (RTCCall *)notification.object;
    
    if (!call) {
        return;
    }

    [self sendEventWithName:P2PCallUpdated eventBody:NSNull.null];
    [[Navigation sharedManager] moveViewToRainbow];
}
-(void) didUpdateCall:(NSNotification *) notification {
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self didUpdateCall:notification];
        });
        return;
    }
    NSLog(@"[WebrtcP2P] didUpdateCall");
    if ([notification.object class] == [RTCCall class]) {
        RTCCall *rtcCall = (RTCCall*)notification.object;
        _currentCall = rtcCall;
        NSLog(@"[WebrtcP2P] didUpdateCall: rtcCall %@", rtcCall);

        if ([rtcCall.peer class] == [RainbowContact class]) {
            BOOL isCallStillActive = [[Navigation sharedManager] isCallActive];
            if (rtcCall.status == CallStatusEstablished) {
                if (!isCallStillActive) {
                    [[Navigation sharedManager] moveViewToCall];
                }
                //the user opened the PBX call, then send the updated call to the react-native
                NSDictionary *rtcCallDic = rtcCall.rtcCallToDictionaryRepresentation;
                [self sendEventWithName:P2PCallUpdated eventBody:rtcCallDic];
            }
            if ((rtcCall.status == CallStatusCanceled || rtcCall.status == CallStatusHangup || rtcCall.status == CallStatusDeclined ||rtcCall.status == CallStatusTimeout ) && isCallStillActive) {
                if (isCallStillActive) {
                    // This means this called is removed
                    [self sendEventWithName:P2PCallUpdated eventBody:NSNull.null];
                    [[Navigation sharedManager] moveViewToRainbow];
                }
            }
        }
    }
}


-(void) didFailedToCall:(NSNotification *) notification {
    NSLog(@"[WebrtcP2P] didFailedToCall");
    if ([notification.object class] == [RTCCall class]) {
        //TODO: I think it should hanguo the call
    }
}

-(void) didFinishLoadingCallContainer:(NSNotification *) notification {
    NSLog(@"[WebrtcP2P] didFinishLoadingCallContainer");
    RCTRootView *currentView = (RCTRootView*)notification.object;
    if ([currentView.moduleName isEqualToString:@"call_activity"]) {
        // This Event trigger when the `call_activity` is finished loading from the React side.
            NSLog(@"[WebrtcP2P] send event to react side");

        RTCCall *call = _rtcService.calls.firstObject;
        if ([call.peer class] == [Contact class]) {
            NSDictionary *rtcCallDic = call.rtcCallToDictionaryRepresentation;
            NSLog(@"[WebrtcP2P] process call to send event to react side%@",rtcCallDic);

            [self sendEventWithName:P2PCallUpdated eventBody:rtcCallDic];
        }
    }
}


#pragma mark - Register-Notification

- (void) registerTelephonyEvents {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddCall:) 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(didFailedToCall:) name:kTelephonyServiceDidFailToCall object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishLoadingCallContainer:) name:RCTContentDidAppearNotification object:nil];
}

- (void) unRegisterTelephonyEvents {
    [[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:kTelephonyServiceDidFailToCall object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:RCTContentDidAppearNotification object:nil];
}

-(void)registerWebrtcEvents {
    // Register for stats notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statsUpdated:) name:kRTCServiceCallStats object:nil];
    // Local video notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddCaptureSession:) name:kRTCServiceDidAddCaptureSession object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddLocalVideoTrack:) name:kRTCServiceDidAddLocalVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemoveLocalVideoTrack:) name:kRTCServiceDidRemoveLocalVideoTrack object:nil];
    // Remote video notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddRemoteVideoTrack:) name:kRTCServiceDidAddRemoteVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemoveRemoteVideoTrack:) name:kRTCServiceDidRemoveRemoteVideoTrack object:nil];
    // Microphone notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAllowMicrophone:) name:kRTCServiceDidAllowMicrophone object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRefuseMicrophone:) name:kRTCServiceDidRefuseMicrophone object:nil];
}

-(void)unRegisterWebrtcEvents {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceCallStats object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidAddCaptureSession object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidAddLocalVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidAddRemoteVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidRemoveLocalVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidRemoveRemoteVideoTrack object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidAllowMicrophone object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidRefuseMicrophone object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kRTCServiceDidMuteUnmuteCall 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(@" [WebrtcP2P] sendEventWithName: %@", eventName);
        [eventEmitter sendEvent:eventName eventBody:eventBody];
    } else {
        NSLog(@"[WebrtcP2P] sendEventWithName:  Bridge is not ready. Cannot send event: %@", eventName);
    }
}
@end




