#import "TGEmbedVinePlayerView.h"
#import "TGEmbedPlayerState.h"

#import <AVFoundation/AVFoundation.h>

#import "TGModernGalleryVideoView.h"

#import "TGTimerTarget.h"

NSString *const TGVinePlayerCallbackOnPlayback = @"onPlayback";

@interface TGEmbedVinePlayerView ()
{
    NSString *_videoId;
    bool _started;
    
    AVPlayer *_player;
    TGModernGalleryVideoView *_videoView;
    UIImageView *_watermarkView;
    
    id _playerStartedObserver;
    id _playerEndedObserver;
}
@end

@implementation TGEmbedVinePlayerView

- (instancetype)initWithWebPageAttachment:(TGWebPageMediaAttachment *)webPage thumbnailSignal:(SSignal *)thumbnailSignal
{
    self = [super initWithWebPageAttachment:webPage thumbnailSignal:thumbnailSignal];
    if (self != nil)
    {
        _videoId = [TGEmbedVinePlayerView _vineVideoIdFromText:webPage.embedUrl];
        
        self.controlsView.watermarkImage = [UIImage imageNamed:@"VineWatermark"];
        self.controlsView.watermarkPrerenderedOpacity = true;
        self.controlsView.watermarkOffset = CGPointMake(12.0f, 12.0f);
    }
    return self;
}

- (void)_watermarkAction
{
    [super _watermarkAction];
    
    NSString *videoId =  _videoId;
    
    NSURL *appUrl = [[NSURL alloc] initWithString:[[NSString alloc] initWithFormat:@"vine://post/%@", videoId]];
    
    if ([[UIApplication sharedApplication] canOpenURL:appUrl])
    {
        [[UIApplication sharedApplication] openURL:appUrl];
        return;
    }
    
    NSURL *webUrl = [NSURL URLWithString:[NSString stringWithFormat:@"https://vine.co/v/%@", videoId]];
    [[UIApplication sharedApplication] openURL:webUrl];
}

- (void)playVideo
{
    [_player play];
    
    TGEmbedPlayerState *state = [TGEmbedPlayerState stateWithPlaying:true];
    [self updateState:state];
}

- (void)pauseVideo:(bool)manually
{
    [super pauseVideo:manually];
    [_player pause];
    
    TGEmbedPlayerState *state = [TGEmbedPlayerState stateWithPlaying:false];
    [self updateState:state];
}

- (TGEmbedPlayerControlsType)_controlsType
{
    return TGEmbedPlayerControlsTypeSimple;
}

- (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)
    {
        NSArray *components = [query componentsSeparatedByString:@"="];
        if (components.count > 1)
            data = [query substringFromIndex:[components.firstObject length] + 1];
    }
    if ([action isEqual:TGVinePlayerCallbackOnPlayback])
    {
        if (!_started)
        {
            _started = true;
            [self _didBeginPlayback];
        }
    }
    else if ([action isEqualToString:@"onSrc"])
    {
        [self _setupCustomPlayerWithURL:[NSURL URLWithString:data]];
    }
}

- (void)_setupCustomPlayerWithURL:(NSURL *)url
{
    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
    AVPlayer *player = [AVPlayer playerWithPlayerItem:item];
    _player = player;
    
    UIView *currentView = [self _webView];
    TGModernGalleryVideoView *videoView = [[TGModernGalleryVideoView alloc] initWithFrame:currentView.frame player:player];
    [currentView.superview insertSubview:videoView aboveSubview:currentView];
    
    [self _cleanWebView];
    _videoView = videoView;
    
    __weak TGEmbedVinePlayerView *weakSelf = self;
    _playerStartedObserver = [player addBoundaryTimeObserverForTimes:@[[NSValue valueWithCMTime:CMTimeMake(10, 100)]] queue:NULL usingBlock:^
    {
        __strong TGEmbedVinePlayerView *strongSelf = weakSelf;
        if (strongSelf != nil)
        {
            [strongSelf _didBeginPlayback];
            
            TGEmbedPlayerState *state = [TGEmbedPlayerState stateWithPlaying:true];
            [strongSelf updateState:state];
            
            [strongSelf->_player removeTimeObserver:strongSelf->_playerStartedObserver];
            strongSelf->_playerStartedObserver = nil;
            
            if (CMTimeGetSeconds(strongSelf->_player.currentItem.duration) > 0)
                [strongSelf _setupEndedObserver];
        }
    }];
    
    [player play];
}

- (void)_setupEndedObserver
{
     __weak TGEmbedVinePlayerView *weakSelf = self;
    _playerEndedObserver = [_player addBoundaryTimeObserverForTimes:@[[NSValue valueWithCMTime:CMTimeSubtract(_player.currentItem.duration, CMTimeMake(10, 100))]] queue:NULL usingBlock:^
    {
        __strong TGEmbedVinePlayerView *strongSelf = weakSelf;
        if (strongSelf != nil)
            [strongSelf->_player seekToTime:CMTimeMake(5, 100)];
    }];
}

- (UIView *)_webView
{
    if (_videoView != nil)
        return _videoView;
    
    return [super _webView];
}

- (NSString *)_embedHTML
{
    NSError *error = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"VinePlayer" ofType:@"html"];
    
    NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
    if (error != nil)
    {
        TGLog(@"[VineEmbedPlayer]: Received error rendering template: %@", error);
        return nil;
    }
    
    NSString *embedHTML = [NSString stringWithFormat:embedHTMLTemplate, _videoId];
    return embedHTML;
}

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

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

+ (NSString *)_vineVideoIdFromText:(NSString *)text
{
    if ([text hasPrefix:@"http://vine.co/v/"] || [text hasPrefix:@"https://vine.co/v/"])
    {
        NSString *suffix = @"";
        
        NSMutableArray *prefixes = [NSMutableArray arrayWithArray:@
        [
            @"http://vine.co/v/",
            @"https://vine.co/v/"
        ]];
        
        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];
            }
        }
        
        int end = -1;
        
        for (int i = 0; i < (int)suffix.length; i++)
        {
            unichar c = [suffix characterAtIndex:i];
            if (c == '/')
            {
                end = i;
                break;
            }
            
            if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '=' || c == '&' || c == '#'))
                return nil;
        }
        
        if (end != - 1)
            suffix = [suffix substringToIndex:end];
        
        return suffix;
    }
    
    return nil;
}

+ (NSString *)_vineIdFromPermalink:(NSString *)text
{
    static dispatch_once_t onceToken;
    static NSDictionary *map = nil;
    dispatch_once(&onceToken, ^
    {
        map = @
        {
            @"B" : @"0",
            @"u" : @"1",
            @"z" : @"2",
            @"a" : @"3",
            @"W" : @"4",
            @"7" : @"5",
            @"Z" : @"6",
            @"m" : @"7",
            @"K" : @"8",
            @"A" : @"9",
            @"q" : @"a",
            @"U" : @"A",
            @"b" : @"b",
            @"P" : @"B",
            @"h" : @"c",
            @"x" : @"C",
            @"M" : @"d",
            @"Q" : @"D",
            @"2" : @"E",
            @"O" : @"e",
            @"0" : @"F",
            @"e" : @"f",
            @"E" : @"G",
            @"i" : @"g",
            @"5" : @"h",
            @"9" : @"H",
            @"J" : @"i",
            @"V" : @"I",
            @"1" : @"j",
            @"Y" : @"J",
            @"3" : @"K",
            @"n" : @"k",
            @"L" : @"L",
            @"v" : @"l",
            @"l" : @"M",
            @"r" : @"m",
            @"6" : @"n",
            @"g" : @"o",
            @"X" : @"p",
            @"H" : @"q",
            @"w" : @"r",
            @"d" : @"s",
            @"p" : @"t",
            @"D" : @"u",
            @"j" : @"v",
            @"I" : @"w",
            @"T" : @"x",
            @"t" : @"y",
            @"F" : @"z"
        };
    });
    
    NSMutableString *shiftedString = [text mutableCopy];
    for (NSUInteger i = 0; i < shiftedString.length; i++)
    {
        NSString *charStr = [shiftedString substringWithRange:NSMakeRange(i, 1)];
        NSString *mappedStr = map[charStr];
        
        [shiftedString replaceCharactersInRange:NSMakeRange(i, 1) withString:mappedStr];
    }
    
    return [NSString stringWithFormat:@"%lld", [self _convertString:shiftedString fromBase:49]];
}

+ (int64_t)_convertString:(NSString *)string fromBase:(NSInteger)fromBase
{
    NSString *base = @"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    NSUInteger limit = string.length;
    
    int64_t res = [base rangeOfString:[string substringWithRange:NSMakeRange(0, 1)]].location;
    if (res == NSNotFound)
        return 0;
    
    for (NSUInteger i = 1; i < limit; i++)
    {
        NSInteger a = [base rangeOfString:[string substringWithRange:NSMakeRange(i, 1)]].location;
        if (a == NSNotFound)
            return 0;
        
        res = fromBase * res + a;
    }

    return res;
}

+ (bool)_supportsWebPage:(TGWebPageMediaAttachment *)webPage
{
    NSString *url = webPage.embedUrl;
    return ([url hasPrefix:@"http://vine.co/v/"] || [url hasPrefix:@"https://vine.co/v/"]);
}

@end
