/*
 * This is the source code of Telegram for iOS v. 1.1
 * It is licensed under GNU GPL v. 2 or later.
 * You should have received a copy of the license in this archive (see LICENSE).
 *
 * Copyright Peter Iakovlev, 2013.
 */

#import "TGImageView.h"

#import "TGImageManager.h"
#import <MTProtoKit/MTProtoKit.h>

#import "UIImage+TG.h"

NSString *TGImageViewOptionKeepCurrentImageAsPlaceholder = @"TGImageViewOptionKeepCurrentImageAsPlaceholder";
NSString *TGImageViewOptionEmbeddedImage = @"TGImageViewOptionEmbeddedImage";
NSString *TGImageViewOptionSynchronous = @"TGImageViewOptionSynchronous";

@interface TGImageView ()
{
    id _loadToken;
    volatile int _version;
    SMetaDisposable *_disposable;
    
    UIImageView *_transitionOverlayView;
}

@end

@implementation TGImageView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self != nil)
    {
        _disposable = [[SMetaDisposable alloc] init];
        _legacyAutomaticProgress = true;
    }
    return self;
}

- (void)dealloc
{
    [_disposable dispose];
    if (_loadToken != nil)
        [[TGImageManager instance] cancelTaskWithId:_loadToken];
}

- (void)setExpectExtendedEdges:(bool)expectExtendedEdges
{
    if (_expectExtendedEdges != expectExtendedEdges)
    {
        _expectExtendedEdges = expectExtendedEdges;
        
        if (_expectExtendedEdges && _extendedInsetsImageView == nil)
        {
            self.image = nil;
            _extendedInsetsImageView = [[UIImageView alloc] init];
            [self addSubview:_extendedInsetsImageView];
        }
        else if (!_expectExtendedEdges && _extendedInsetsImageView != nil)
        {
            _extendedInsetsImageView.image = nil;
            [_extendedInsetsImageView removeFromSuperview];
            _extendedInsetsImageView = nil;
        }
    }
}

- (void)loadUri:(NSString *)uri withOptions:(NSDictionary *)__unused options
{
    [_disposable setDisposable:nil];
    _version++;
    
    UIImage *image = nil;
    
    bool beganAsyncTask = false;
    
    if (options[TGImageViewOptionEmbeddedImage] != nil)
        image = options[TGImageViewOptionEmbeddedImage];
    else
    {
        __autoreleasing id asyncTaskId = nil;
        __weak TGImageView *weakSelf = self;
        int version = _version;
        CFAbsoluteTime loadStartTime = MTAbsoluteSystemTime();
        bool legacyAutomaticProgress = _legacyAutomaticProgress;
        image = [[TGImageManager instance] loadImageSyncWithUri:uri canWait:[options[TGImageViewOptionSynchronous] boolValue] decode:true acceptPartialData:true asyncTaskId:&asyncTaskId progress:^(float value)
        {
            if (legacyAutomaticProgress)
            {
                TGDispatchOnMainThread(^
                {
                    __strong TGImageView *strongSelf = weakSelf;
                    if (strongSelf != nil && strongSelf->_version == version)
                        [strongSelf _updateProgress:value];
                });
            }
        } partialCompletion:^(UIImage *partialImage)
        {
            TGDispatchOnMainThread(^
            {
                __strong TGImageView *strongSelf = weakSelf;
                if (strongSelf != nil && strongSelf->_version == version)
                    [strongSelf _commitImage:partialImage partial:true loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
                else
                    TGLog(@"[TGImageView _commitImage version mismatch]");
            });
        } completion:^(UIImage *image)
        {
            TGDispatchOnMainThread(^
            {
                __strong TGImageView *strongSelf = weakSelf;
                if (strongSelf != nil && strongSelf->_version == version)
                {
                    if (legacyAutomaticProgress)
                        [strongSelf _updateProgress:1.0f];
                    [strongSelf _commitImage:image partial:false loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
                }
                else
                    TGLog(@"[TGImageView _commitImage version mismatch]");
            });
        }];
        
        if (asyncTaskId != nil)
        {
            beganAsyncTask = true;
            _loadToken = asyncTaskId;
        }
    }
    
    if (image != nil)
        [self _commitImage:image partial:beganAsyncTask loadTime:0.0];
    else
    {
        if (![options[TGImageViewOptionKeepCurrentImageAsPlaceholder] boolValue])
        {
            UIImage *placeholderImage = [[TGImageManager instance] loadAttributeSyncForUri:uri attribute:@"placeholder"];
            if (placeholderImage != nil)
                [self _commitImage:placeholderImage partial:beganAsyncTask loadTime:0.0];
            else
                [self performTransitionToImage:nil partial:true duration:0.0];
        }
        
        CFAbsoluteTime loadStartTime = MTAbsoluteSystemTime();
        
        __weak TGImageView *weakSelf = self;
        int version = _version;
        bool legacyAutomaticProgress = _legacyAutomaticProgress;
        _loadToken = [[TGImageManager instance] beginLoadingImageAsyncWithUri:uri decode:true progress:^(float value)
        {
            if (legacyAutomaticProgress)
            {
                TGDispatchOnMainThread(^
                {
                    __strong TGImageView *strongSelf = weakSelf;
                    if (strongSelf != nil && strongSelf->_version == version)
                        [strongSelf _updateProgress:value];
                });
            }
        } partialCompletion:^(UIImage *partialImage)
        {
            TGDispatchOnMainThread(^
            {
                __strong TGImageView *strongSelf = weakSelf;
                if (strongSelf != nil && strongSelf->_version == version)
                    [strongSelf _commitImage:partialImage partial:true loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
                else
                    TGLog(@"[TGImageView _commitImage version mismatch]");
            });
        } completion:^(UIImage *image)
        {
            TGDispatchOnMainThread(^
            {
                __strong TGImageView *strongSelf = weakSelf;
                if (strongSelf != nil && strongSelf->_version == version)
                {
                    if (legacyAutomaticProgress)
                        [strongSelf _updateProgress:1.0f];
                    [strongSelf _commitImage:image partial:false loadTime:(NSTimeInterval)(MTAbsoluteSystemTime() - loadStartTime)];
                }
                else
                    TGLog(@"[TGImageView _commitImage version mismatch]");
            });
        }];
    }
}

- (void)_updateProgress:(float)value
{
    [self performProgressUpdate:value];
}

- (void)_commitImage:(UIImage *)image partial:(bool)partial loadTime:(NSTimeInterval)loadTime
{
    if (image == self.image)
        return;
    
    NSTimeInterval transitionDuration = 0.0;
    
    if (loadTime > DBL_EPSILON)
        transitionDuration = 0.16;
    
    [self performTransitionToImage:image partial:partial duration:transitionDuration];
}

- (void)reset
{
    _version++;
    [_disposable setDisposable:nil];
    
    if (_loadToken != nil)
    {
        [[TGImageManager instance] cancelTaskWithId:_loadToken];
        _loadToken = nil;
    }
    
    [self _commitImage:nil partial:false loadTime:0.0];
}

- (UIImage *)currentImage
{
    if (_expectExtendedEdges)
        return _extendedInsetsImageView.image;
    return [super image];
}

- (void)performProgressUpdate:(CGFloat)__unused progress
{
}

- (void)performTransitionToImage:(UIImage *)image partial:(bool)__unused partial duration:(NSTimeInterval)duration
{
    if (((_expectExtendedEdges && _extendedInsetsImageView.image != nil) || (!_expectExtendedEdges && self.image != nil)) && duration > DBL_EPSILON)
    {
        self.alpha = 1.0f;
        _extendedInsetsImageView.alpha = 1.0f;
        
        if (_transitionOverlayView == nil)
            _transitionOverlayView = [[UIImageView alloc] init];
        
        _transitionOverlayView.frame = _extendedInsetsImageView == nil ? self.bounds : _extendedInsetsImageView.frame;
        [self insertSubview:_transitionOverlayView atIndex:0];
        
        _transitionOverlayView.image = _extendedInsetsImageView == nil ? self.image : _extendedInsetsImageView.image;
        _transitionOverlayView.backgroundColor = self.backgroundColor;
        _transitionOverlayView.alpha = 1.0;
        
        [UIView animateWithDuration:duration animations:^
        {
            _transitionOverlayView.alpha = 0.0;
        } completion:^(__unused BOOL finished)
        {
            _transitionOverlayView.image = nil;
            [_transitionOverlayView removeFromSuperview];
        }];
        
        if (_extendedInsetsImageView != nil)
        {
            _extendedInsetsImageView.alpha = 0.0f;
            [UIView animateWithDuration:duration / 2.0f animations:^
            {
                _extendedInsetsImageView.alpha = 1.0f;
            }];
        }
    }
    else if (image != nil && duration > DBL_EPSILON)
    {
        if (_expectExtendedEdges)
            _extendedInsetsImageView.alpha = 0.0f;
        else
            self.alpha = 0.0f;
        [UIView animateWithDuration:duration animations:^
        {
            if (_expectExtendedEdges)
                _extendedInsetsImageView.alpha = 1.0f;
            else
                self.alpha = 1.0f;
        } completion:^(__unused BOOL finished)
        {
        }];
    }
    else
    {
        self.alpha = 1.0f;
    }

    if (!_expectExtendedEdges)
    {
        self.image = image;
        if (_extendedInsetsImageView != nil)
        {
            [_extendedInsetsImageView removeFromSuperview];
            _extendedInsetsImageView = nil;
        }
    }
    else
    {
        UIEdgeInsets insets = [image extendedEdgeInsets];
        _extendedInsetsImageView.image = image;
        _extendedInsetsImageView.frame = CGRectMake(-insets.left, -insets.top, self.bounds.size.width + insets.left + insets.right, self.bounds.size.height + insets.top + insets.bottom);
    }
}

- (void)setSignal:(SSignal *)signal
{
    _version++;
    int version = _version;
    __weak TGImageView *weakSelf = self;
    
    [_disposable setDisposable:[signal startWithNext:^(id next)
    {
        bool synchronous = [NSThread isMainThread];
        TGDispatchOnMainThread(^
        {
            __strong TGImageView *strongSelf = weakSelf;
            if (strongSelf != nil && strongSelf->_version == version)
            {
                if ([next isKindOfClass:[UIImage class]])
                    [strongSelf _commitImage:next partial:[next degraded] && ![next edited] loadTime:synchronous ? 0.0 : 1.0];
                else if ([next respondsToSelector:@selector(floatValue)])
                    [strongSelf _updateProgress:[next floatValue]];
            }
        });
    } error:^(id error)
    {
        TGLog(@"TGImageView signal error: %@", error);
    } completed:^
    {
    }]];
}

@end
