#import "TGEmbedYoutubePlayerView.h"
#import "TGEmbedPlayerState.h"

NSString *const TGYTPlayerCallbackOnReady = @"onReady";
NSString *const TGYTPlayerCallbackOnState = @"onState";
NSString *const TGYTPlayerCallbackOnPlaybackQualityChange = @"onPlaybackQualityChange";
NSString *const TGYTPlayerCallbackOnError = @"onError";

const NSInteger TGYTPlayerStateUnstartedCode = -1;
const NSInteger TGYTPlayerStateEndedCode = 0;
const NSInteger TGYTPlayerStatePlayingCode = 1;
const NSInteger TGYTPlayerStatePausedCode = 2;
const NSInteger TGYTPlayerStateBufferingCode = 3;

@interface TGEmbedYoutubePlayerView ()
{
    NSDictionary *_playerParams;
    bool _started;
    bool _failed;
    
    bool _ready;
    bool _playOnReady;
    NSInteger _playAfterTicks;
    
    NSInteger _ignorePositionUpdates;
}
@end

@implementation TGEmbedYoutubePlayerView

- (instancetype)initWithWebPageAttachment:(TGWebPageMediaAttachment *)webPage thumbnailSignal:(SSignal *)thumbnailSignal
{
    self = [super initWithWebPageAttachment:webPage thumbnailSignal:thumbnailSignal];
    if (self != nil)
    {
        NSTimeInterval start = 0.0;
        NSString *videoId = [TGEmbedYoutubePlayerView _youtubeVideoIdFromText:webPage.embedUrl originalUrl:webPage.url startTime:&start];
        _playerParams = @
        {
            @"videoId": videoId,
            @"playerVars": @
            {
                @"cc_load_policy" : @1,
                @"iv_load_policy" : @3,
                @"controls" : @0,
                @"playsinline" : @1,
                @"autohide" : @1,
                @"showinfo" : @0,
                @"rel" : @0,
                @"modestbranding" : @1,
                @"start" : @((NSInteger)start)
            }
        };
        
        self.controlsView.watermarkImage = [UIImage imageNamed:@"YoutubeWatermark"];
        self.controlsView.watermarkPosition = TGEmbedPlayerWatermarkPositionBottomRight;
        self.controlsView.watermarkOffset = CGPointMake(-12.0f, -12.0f);
    }
    return self;
}

- (void)_watermarkAction
{
    [super _watermarkAction];
    
    NSString *videoId =  _playerParams[@"videoId"];
    
    NSURL *appUrl = [[NSURL alloc] initWithString:[[NSString alloc] initWithFormat:@"youtube-x-callback://watch?v=%@&x-success=telegram://1&x-source=Telegram", videoId]];
    
    if ([[UIApplication sharedApplication] canOpenURL:appUrl])
    {
        [[UIApplication sharedApplication] openURL:appUrl];
        return;
    }
    
    NSURL *webUrl = [NSURL URLWithString:[NSString stringWithFormat:@"https://youtube.com/watch?v=%@", videoId]];
    [[UIApplication sharedApplication] openURL:webUrl];
}

- (void)playVideo
{
    if (!_ready && self.disallowAutoplay)
    {
        _playOnReady = true;
        _playAfterTicks = 2;
        return;
    }
    
    [super playVideo];
    [self _evaluateJS:@"player.playVideo();" completion:nil];
    
    _ignorePositionUpdates = 2;
}

- (void)pauseVideo:(bool)manually
{
    [super pauseVideo:manually];
    [self _evaluateJS:@"player.pauseVideo();" completion:nil];
}

- (void)seekToPosition:(NSTimeInterval)position
{
    NSString *command = [NSString stringWithFormat:@"player.seekTo(%@, true);", @(position)];
    [self _evaluateJS:command completion:nil];
    
    TGEmbedPlayerState *newState = [TGEmbedPlayerState stateWithPlaying:self.state.isPlaying duration:self.state.duration position:position downloadProgress:self.state.downloadProgress];
    [self updateState:newState];
    
    _ignorePositionUpdates = 2;
}

- (TGEmbedPlayerControlsType)_controlsType
{
    return TGEmbedPlayerControlsTypeFull;
}

- (void)_onPageReady
{
    
}

- (void)_didBeginPlayback
{
    [super _didBeginPlayback];
    [self setDimmed:false animated:true shouldDelay:false];
}

- (void)_notifyOfCallbackURL:(NSURL *)url
{
    NSString *action = url.host;
    
    NSString *query = url.query;
    NSString *data;
    if (query != nil)
        data = [query componentsSeparatedByString:@"="][1];
    
    if ([action isEqualToString:TGYTPlayerCallbackOnState])
    {
        if (_failed)
            return;
        
        NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:false];
        NSArray *queryItems = urlComponents.queryItems;
        
        bool failed = _failed;
        bool playing = self.state.playing;
        bool finished = false;
        NSTimeInterval position = self.state.position;
        NSTimeInterval duration = self.state.duration;
        CGFloat downloadProgress = self.state.downloadProgress;
        
        for (NSURLQueryItem *queryItem in queryItems)
        {
            if ([queryItem.name isEqualToString:@"playback"])
            {
                playing = ([queryItem.value integerValue] == TGYTPlayerStatePlayingCode);
                finished = ([queryItem.value integerValue] == TGYTPlayerStateEndedCode);
            }
            else if ([queryItem.name isEqualToString:@"position"])
            {
                if (_ignorePositionUpdates > 0)
                    _ignorePositionUpdates--;
                else
                    position = [queryItem.value doubleValue];
            }
            else if ([queryItem.name isEqualToString:@"duration"])
            {
                duration = [queryItem.value doubleValue];
            }
            else if ([queryItem.name isEqualToString:@"download"])
            {
                downloadProgress = MAX(0.0f, MIN(1.0, [queryItem.value floatValue]));
            }
            else if ([queryItem.name isEqualToString:@"failed"])
            {
                failed = [queryItem.value boolValue];
            }
        }
        
        if (failed && !_failed)
        {
            _failed = true;
            [self setDimmed:false animated:true shouldDelay:false];
            [self.controlsView setDisabled];
        }
        
        if (playing && !_started)
        {
            _started = true;
            [self _didBeginPlayback];
        }
        
        if (finished)
            position = 0.0;
        
        TGEmbedPlayerState *newState = [TGEmbedPlayerState stateWithPlaying:playing duration:duration position:position downloadProgress:downloadProgress];
        [self updateState:newState];
        
        if (_playAfterTicks > 0)
        {
            _playAfterTicks--;
            if (_playAfterTicks == 0)
            {
                _ready = true;
                [self playVideo];
            }
        }
    }
    else if ([action isEqualToString:TGYTPlayerCallbackOnReady])
    {
        _ready = true;
        if (_playOnReady)
        {
            _playAfterTicks = 0;
            _playOnReady = false;
            [self playVideo];
        }
    }
}

- (NSString *)_embedHTML
{
    NSDictionary *playerCallbacks = @
    {
        @"onReady" : @"onReady",
        @"onStateChange" : @"onStateChange",
        @"onPlaybackQualityChange" : @"onPlaybackQualityChange",
        @"onError" : @"onPlayerError"
    };
    
    NSMutableDictionary *playerParams = [[NSMutableDictionary alloc] init];
    [playerParams addEntriesFromDictionary:_playerParams];
    
    if (![playerParams objectForKey:@"height"])
        [playerParams setValue:@"100%" forKey:@"height"];
    if (![playerParams objectForKey:@"width"])
        [playerParams setValue:@"100%" forKey:@"width"];
    
    [playerParams setValue:playerCallbacks forKey:@"events"];
    
    if ([playerParams objectForKey:@"playerVars"])
    {
        NSMutableDictionary *playerVars = [[NSMutableDictionary alloc] init];
        [playerVars addEntriesFromDictionary:[playerParams objectForKey:@"playerVars"]];
    }
    else
    {
        [playerParams setValue:[[NSDictionary alloc] init] forKey:@"playerVars"];
    }
    
    NSError *error = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"YoutubePlayer" ofType:@"html"];
    
    NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
    if (error != nil)
    {
        TGLog(@"[YTEmbedPlayer]: Received error rendering template: %@", error);
        return nil;
    }
    
    NSError *jsonRenderingError = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:playerParams options:NSJSONWritingPrettyPrinted error:&jsonRenderingError];
    if (jsonRenderingError != nil)
    {
        NSLog(@"[YTEmbedPlayer]: Attempted configuration of player with invalid playerVars: %@ \tError: %@", playerParams, jsonRenderingError);
        return nil;
    }
    
    NSString *playerVarsJsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    
    NSString *autoplay = self.disallowAutoplay ? @"false" : @"true";
    NSString *embedHTML = [NSString stringWithFormat:embedHTMLTemplate, playerVarsJsonString, autoplay];
    return embedHTML;
}

- (NSURL *)_baseURL
{
    return [NSURL URLWithString:@"https://youtube.com/"];
}

- (void)_setupUserScripts:(WKUserContentController *)contentController
{
    NSError *error = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"YoutubePlayerInject" ofType:@"js"];
    NSString *scriptText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
    if (error != nil)
        TGLog(@"[YTEmbedPlayer]: Received error loading inject script: %@", error);
    
    WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptText injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:false];
    [contentController addUserScript:script];
}

- (bool)_scaleViewToMaxSize
{
    return true;
}

- (CGFloat)_compensationEdges
{
    return 3.0f;
}

+ (NSString *)_youtubeVideoIdFromText:(NSString *)text originalUrl:(NSString *)originalUrl startTime:(NSTimeInterval *)startTime
{
    if ([text hasPrefix:@"http://www.youtube.com/watch?v="] || [text hasPrefix:@"https://www.youtube.com/watch?v="] || [text hasPrefix:@"http://m.youtube.com/watch?v="] || [text hasPrefix:@"https://m.youtube.com/watch?v="])
    {
        NSRange range1 = [text rangeOfString:@"?v="];
        bool match = true;
        for (NSInteger i = range1.location + range1.length; i < (NSInteger)text.length; i++)
        {
            unichar c = [text characterAtIndex:i];
            if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '=' || c == '&' || c == '#'))
            {
                match = false;
                break;
            }
        }
        
        if (match)
        {
            NSString *videoId = nil;
            NSRange ampRange = [text rangeOfString:@"&"];
            NSRange hashRange = [text rangeOfString:@"#"];
            if (ampRange.location != NSNotFound || hashRange.location != NSNotFound)
            {
                NSInteger location = MIN(ampRange.location, hashRange.location);
                videoId = [text substringWithRange:NSMakeRange(range1.location + range1.length, location - range1.location - range1.length)];
            }
            else
                videoId = [text substringFromIndex:range1.location + range1.length];
            
            if (videoId.length != 0)
                return videoId;
        }
    }
    else if ([text hasPrefix:@"http://youtu.be/"] || [text hasPrefix:@"https://youtu.be/"] || [text hasPrefix:@"http://www.youtube.com/embed/"] || [text hasPrefix:@"https://www.youtube.com/embed/"])
    {
        NSString *suffix = @"";

        NSMutableArray *prefixes = [NSMutableArray arrayWithArray:@
        [
            @"http://youtu.be/",
            @"https://youtu.be/",
            @"http://www.youtube.com/embed/",
            @"https://www.youtube.com/embed/"
        ]];
        
        while (suffix.length == 0 && prefixes.count > 0)
        {
            NSString *prefix = prefixes.firstObject;
            if ([text hasPrefix:prefix])
            {
                suffix = [text substringFromIndex:prefix.length];
                break;
            }
            else
            {
                [prefixes removeObjectAtIndex:0];
            }
        }
        
        NSString *queryString = nil;
        for (int i = 0; i < (int)suffix.length; i++)
        {
            unichar c = [suffix characterAtIndex:i];
            if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '=' || c == '&' || c == '#'))
            {
                if (c == '?')
                {
                    queryString = [suffix substringFromIndex:i + 1];
                    suffix = [suffix substringToIndex:i];
                    break;
                }
                else
                {
                    return nil;
                }
            }
        }
        
        if (startTime != NULL)
        {
            NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
            NSString *queryString = [NSURL URLWithString:originalUrl].query;
            for (NSString *param in [queryString componentsSeparatedByString:@"&"])
            {
                NSArray *components = [param componentsSeparatedByString:@"="];
                if (components.count < 2)
                    continue;
                [params setObject:components.lastObject forKey:components.firstObject];
            }
            
            NSString *timeParam = params[@"t"];
            if (timeParam != nil)
            {
                NSTimeInterval position = 0.0;
                if ([timeParam rangeOfString:@"s"].location != NSNotFound)
                {
                    NSString *value;
                    NSUInteger location = 0;
                    for (NSUInteger i = 0; i < timeParam.length; i++)
                    {
                        unichar c = [timeParam characterAtIndex:i];
                        if ((c < '0' || c > '9'))
                        {
                            value = [timeParam substringWithRange:NSMakeRange(location, i - location)];
                            location = i + 1;
                            switch (c)
                            {
                                case 's':
                                    position += value.doubleValue;
                                    break;
                                    
                                case 'm':
                                    position += value.doubleValue * 60.0;
                                    break;
                                    
                                case 'h':
                                    position += value.doubleValue * 3600.0;
                                    break;
                                    
                                default:
                                    break;
                            }
                        }
                    }
                }
                else
                {
                    position = timeParam.doubleValue;
                }
                
                *startTime = position;
            }
        }
                
        return suffix;
    }
    
    return nil;
}

+ (bool)_supportsWebPage:(TGWebPageMediaAttachment *)webPage
{
    NSString *url = webPage.embedUrl;
    return ([url hasPrefix:@"http://www.youtube.com/watch?v="] || [url hasPrefix:@"https://www.youtube.com/watch?v="] || [url hasPrefix:@"http://m.youtube.com/watch?v="] || [url hasPrefix:@"https://m.youtube.com/watch?v="] || [url hasPrefix:@"http://youtu.be/"] || [url hasPrefix:@"https://youtu.be/"] || [url hasPrefix:@"https://www.youtube.com/embed/"]);
}

@end
