#import <React/RCTViewManager.h>
#import <React/RCTUIManager.h>
#import <React/RCTLog.h>
#import "RCTElementsPlayer.h"
#import "CNConnatixPlayer+CNEventTypeMapper.h"
#import "connatix_player_sdk_react_native-Swift.h"

@interface ElementsPlayerViewManager : RCTViewManager
@end

@implementation ElementsPlayerViewManager

const NSString* ERROR_KEY_ELEMENTS = @"error";
const NSString* RESULT_KEY_ELEMENTS = @"result";

RCT_EXPORT_MODULE(RCTElementsPlayer)

RCT_EXPORT_VIEW_PROPERTY(onSetElementsConfig, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(onPlay, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPause, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetVolume, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetQuality, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetQuality, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onDisableAdvertising, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onEnableAdvertising, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetMacros, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetPreRollBreak, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetPostRollBreak, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(onGetVideoDetails, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetVideoCurrentPosition, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetVideoDuration, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetVideoIndex, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetVideoIndex, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetAutoQuality, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetAvailableQualities, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onToggleFullscreen, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onToggleSubtitles, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGetSubtitles, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onSetSubtitle, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(onListenFor, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onListenForMore, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onRemove, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPlayerEvent, RCTBubblingEventBlock)

- (UIView *)view {
    return [[RCTElementsPlayer alloc] initWithFrame:CGRectZero];
}

- (RCTElementsPlayer *)getPlayerView:(NSNumber *) reactTag {
    if (self.bridge == nil) {
        return nil;
    }

    RCTElementsPlayer * view = (RCTElementsPlayer *)[self.bridge.uiManager viewForReactTag:reactTag];
    if (view != nil) {
        return view;
    }

    NSDictionary<NSNumber *, UIView *>* viewRegistry = [self.bridge.uiManager valueForKey:@"viewRegistry"];
    if (viewRegistry != nil) {
        RCTElementsPlayer * viewRegistryView = (RCTElementsPlayer *)viewRegistry[reactTag];
        if (viewRegistryView != nil) {
            return viewRegistryView;
        } else {
            return nil;
        }
    } else {
        return nil;
    }
}

- (UIView *)getObstructionView:(NSNumber *) reactTag {
    if (self.bridge == nil) {
        return nil;
    }

    UIView * view = (UIView *)[self.bridge.uiManager viewForReactTag:reactTag];
    if (view != nil) {
        return view;
    }

    NSDictionary<NSNumber *, UIView *>* viewRegistry = [self.bridge.uiManager valueForKey:@"viewRegistry"];
    if (viewRegistry != nil) {
        UIView * viewRegistryView = (UIView *)viewRegistry[reactTag];
        if (viewRegistryView != nil) {
            return viewRegistryView;
        } else {
            return nil;
        }
    } else {
        return nil;
    }
}

RCT_EXPORT_METHOD(setElementsConfig:(nonnull NSNumber *) reactTag :(NSString *)config) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        NSData *data = [config dataUsingEncoding:NSUTF8StringEncoding];
        NSError *error = nil;
        CNElementsConfig* playerConfig = [ObjcHelper decodeDataToElementsConfigWithData:data error:&error];
        playerConfig._appSettings.reactNativeSdkVersion = @"2.4.10";
        if (error) {
            view.onSetElementsConfig(@{ERROR_KEY_ELEMENTS: error.userInfo[NSDebugDescriptionErrorKey]});
        } else {
            [view.elementsPlayer setElementsConfig:playerConfig];
            view.onSetElementsConfig(@{RESULT_KEY_ELEMENTS: @YES});
        }
    }];
}

RCT_EXPORT_METHOD(stopPlayer:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        [view.elementsPlayer stopPlayer];
    }];
}

// Base API

RCT_EXPORT_METHOD(play:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer playOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onPlay(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(pause:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer pauseOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onPause(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setVolume:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)volume) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer setVolume:volume.floatValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetVolume(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setQuality:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)videoQuality) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer setQuality:videoQuality.intValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetQuality(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getQuality:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer getQualityOnResult:^(enum CNVideoQuality videoQuality) {
            NSNumber* result = [ObjcHelper getVideoQualityNumber:videoQuality];
            weakView.onGetQuality(@{RESULT_KEY_ELEMENTS: result});
        } onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onGetQuality(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(disableAdvertising:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer disableAdvertisingOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onDisableAdvertising(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(enableAdvertising:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer enableAdvertisingOnError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onEnableAdvertising(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setMacros:(nonnull NSNumber *) reactTag :(NSString * _Nonnull)macrosJsonString) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        NSData *macros = [macrosJsonString dataUsingEncoding:NSUTF8StringEncoding];
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer setMacros:macros onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetMacros(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setPreRollBreak:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)seconds) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer setPreRollBreakWithSeconds:seconds.intValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetPreRollBreak(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setPostRollBreak:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)seconds) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer setPostRollBreakWithSeconds:seconds.intValue onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onSetPostRollBreak(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(addFriendlyObstruction:(nonnull NSNumber *) reactTag :(nonnull NSNumber *) obstructionReactTag :(nonnull NSString *) purpose :(nonnull NSString *) detailedReason) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        UIView *obstructionView = [weakSelf getObstructionView:obstructionReactTag];
        CNObstructionPurpose obstructionPurpose = [ObjcHelper toObstructionPurpose:purpose];
        if (!obstructionView) {
            RCTLogError(@"Cannot find UIView with tag #%@", obstructionReactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer addFriendlyObstructionWithView:obstructionView purpose:obstructionPurpose detailedReason:detailedReason onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onAddFriendlyObstruction(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(removeFriendlyObstruction:(nonnull NSNumber *) reactTag :(nonnull NSNumber *) obstructionReactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        UIView *obstructionView = [weakSelf getObstructionView:obstructionReactTag];
        if (!obstructionView) {
            RCTLogError(@"Cannot find UIView with tag #%@", obstructionReactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer removeFriendlyObstructionWithView:obstructionView onError:^(CNBaseAPIError * _Nonnull error) {
            weakView.onRemoveFriendlyObstruction(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

// Elements API

RCT_EXPORT_METHOD(getVideoDetails:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer getVideoDetailsOnResult:^(CNVideoDescription * _Nonnull videoDescription) {
            NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
            dictionary[@"description"] = videoDescription.videoDescription;
            dictionary[@"duration"] = [NSNumber numberWithDouble:videoDescription.duration];
            dictionary[@"id"] = videoDescription.videoId;
            dictionary[@"keywords"] = videoDescription.keywords;
            dictionary[@"title"] = videoDescription.title;
            dictionary[@"url"] = videoDescription.url;
            weakView.onGetVideoDetails(@{RESULT_KEY_ELEMENTS: dictionary});
        } onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onGetVideoDetails(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getVideoCurrentPosition:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer getVideoCurrentPositionOnResult:^(double position) {
            weakView.onGetVideoCurrentPosition(@{RESULT_KEY_ELEMENTS: [NSNumber numberWithDouble:position]});
        } onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onGetVideoCurrentPosition(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getVideoDuration:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer getVideoDurationOnResult:^(double duration) {
            weakView.onGetVideoDuration(@{RESULT_KEY_ELEMENTS: [NSNumber numberWithDouble:duration]});
        } onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onGetVideoDuration(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getVideoIndex:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer getVideoIndexOnResult:^(NSInteger index) {
            weakView.onGetVideoIndex(@{RESULT_KEY_ELEMENTS: [NSNumber numberWithLong:index]});
        } onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onGetVideoIndex(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setVideoIndex:(nonnull NSNumber *) reactTag :(NSNumber * _Nonnull)index) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer setVideoIndex:index.intValue onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onSetVideoIndex(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setAutoQuality:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer setAutoQualityOnError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onSetAutoQuality(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getAvailableQualities:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer getAvailableQualitiesOnResult:^(NSArray<NSNumber *> * _Nonnull qualities) {
            weakView.onGetAvailableQualities(@{RESULT_KEY_ELEMENTS: qualities});
        } onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onGetAvailableQualities(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(toggleFullscreen:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer toggleFullScreenOnError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onToggleFullscreen(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(toggleSubtitles:(nonnull NSNumber *) reactTag :(BOOL)shouldShow) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer toggleSubtitles:shouldShow onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onToggleSubtitles(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(getSubtitles:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        __weak typeof(RCTElementsPlayer *) weakView = view;
        [weakView.elementsPlayer getSubtitlesOnResult:^(NSArray<CNTrack *> * _Nonnull tracks) {
            NSMutableArray *array = [[NSMutableArray alloc] init];
            for (int index = 0; index < tracks.count; index++) {
                NSMutableDictionary * dictionary = [[NSMutableDictionary alloc] init];
                dictionary[@"file"] = tracks[index].file;
                dictionary[@"type"] = [NSNumber numberWithLong:tracks[index].type];
                dictionary[@"code"] = tracks[index].code;
                dictionary[@"title"] = tracks[index].title;
                [array addObject:dictionary];
            }
            weakView.onGetSubtitles(@{RESULT_KEY_ELEMENTS: array});
        } onError:^(CNElementsAPIError * _Nonnull error) {
            weakView.onGetSubtitles(@{ERROR_KEY_ELEMENTS: error.message});
        }];
    }];
}

RCT_EXPORT_METHOD(setSubtitle:(nonnull NSNumber *) reactTag :(NSString * _Nonnull)trackJsonString) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        NSData *data = [trackJsonString dataUsingEncoding:NSUTF8StringEncoding];
        NSError *error = nil;
        CNTrack *track = [ObjcHelper decodeDataToTrackWithData:data error:&error];
        if (error) {
            view.onSetSubtitle(@{ERROR_KEY_ELEMENTS: error.userInfo[NSDebugDescriptionErrorKey]});
        } else {
            __weak typeof(RCTElementsPlayer *) weakView = view;
            [weakView.elementsPlayer setSubtitle:track onError:^(CNElementsAPIError * _Nonnull error) {
                weakView.onSetSubtitle(@{ERROR_KEY_ELEMENTS: error.message});
            }];
        }
    }];
}

// Events API

RCT_EXPORT_METHOD(listenFor:(nonnull NSNumber *) reactTag :(NSString *)event :(BOOL)once) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        @try {
            CNEventType eventType = [CNConnatixPlayer mapStringToEventType:event];
            [view.elementsPlayer listenFor:eventType :once];
        }
        @catch (NSException *exception) {
            view.onListenFor(@{ERROR_KEY_ELEMENTS: exception.reason});
        }
    }];
}

RCT_EXPORT_METHOD(listenForMore:(nonnull NSNumber *) reactTag :(NSArray *)events) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        NSMutableArray* eventsToListenTo = [[NSMutableArray alloc] init];
        for (int index = 0; index < events.count; index++) {
            @try {
                CNEventType eventType = [CNConnatixPlayer mapStringToEventType:events[index]];
                [eventsToListenTo addObject:events[index]];
            }
            @catch (NSException *exception) {
                view.onListenForMore(@{ERROR_KEY_ELEMENTS: exception.reason});
                return;
            }
        }
        [view.elementsPlayer listenForMore:eventsToListenTo];
    }];
}

RCT_EXPORT_METHOD(listenForAllEvents:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        [view.elementsPlayer listenForAllEvents];
    }];
}

RCT_EXPORT_METHOD(remove:(nonnull NSNumber *) reactTag :(NSString *)event) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        @try {
            CNEventType eventType = [CNConnatixPlayer mapStringToEventType:event];
            [view.elementsPlayer remove:eventType];
        }
        @catch (NSException *exception) {
            view.onRemove(@{ERROR_KEY_ELEMENTS: exception.reason});
        }
    }];
}

RCT_EXPORT_METHOD(removeAllEvents:(nonnull NSNumber *) reactTag) {
    __weak typeof(self) weakSelf = self;
    [weakSelf.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
        RCTElementsPlayer *view = [weakSelf getPlayerView:reactTag];
        if (!view || ![view isKindOfClass:[RCTElementsPlayer class]]) {
            RCTLogError(@"Cannot find RCTElementsPlayer with tag #%@", reactTag);
            return;
        }
        [view.elementsPlayer removeAllEvents];
    }];
}

@end
