


#import "SDAnimatedImageView.h"

#if SD_UIKIT || SD_MAC

#import "UIImage+Metadata.h"
#import "NSImage+Compatibility.h"
#import "SDInternalMacros.h"
#import "objc/runtime.h"


@interface SDAnimatedImageFrameProvider : NSObject <SDAnimatedImageProvider>
@property (nonatomic, strong) id<SDAnimatedImageProvider> provider;
@property (nonatomic, strong) id<SDImageTransformer> transformer;
@end

@implementation SDAnimatedImageFrameProvider

- (instancetype)initWithProvider:(id<SDAnimatedImageProvider>)provider transformer:(id<SDImageTransformer>)transformer {
    self = [super init];
    if (self) {
        _provider = provider;
        _transformer = transformer;
    }
    return self;
}

- (NSUInteger)hash {
    NSUInteger prime = 31;
    NSUInteger result = 1;
    NSUInteger providerHash = self.provider.hash;
    NSUInteger transformerHash = self.transformer.transformerKey.hash;
    result = prime * result + providerHash;
    result = prime * result + transformerHash;
    return result;
}

- (BOOL)isEqual:(id)object {
    if (nil == object) {
      return NO;
    }
    if (self == object) {
      return YES;
    }
    if (![object isKindOfClass:[self class]]) {
      return NO;
    }
    return self.provider == [object provider]
    && [self.transformer.transformerKey isEqualToString:[object transformer].transformerKey];
}

- (NSData *)animatedImageData {
    return self.provider.animatedImageData;
}

- (NSUInteger)animatedImageFrameCount {
    return self.provider.animatedImageFrameCount;
}

- (NSUInteger)animatedImageLoopCount {
    return self.provider.animatedImageLoopCount;
}

- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
    return [self.provider animatedImageDurationAtIndex:index];
}

- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
    UIImage *frame = [self.provider animatedImageFrameAtIndex:index];
    return [self.transformer transformedImageWithImage:frame forKey:@""];
}

@end

@interface UIImageView () <CALayerDelegate>
@end

@interface SDAnimatedImageView () {
    BOOL _initFinished; 
    NSRunLoopMode _runLoopMode;
    NSUInteger _maxBufferSize;
    double _playbackRate;
    SDAnimatedImagePlaybackMode _playbackMode;
}

@property (nonatomic, strong, readwrite) SDAnimatedImagePlayer *player;
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
@property (nonatomic, assign, readwrite) NSUInteger currentLoopCount;
@property (nonatomic, assign) BOOL shouldAnimate;
@property (nonatomic, assign) BOOL isProgressive;
@property (nonatomic) CALayer *imageViewLayer; 

@end

@implementation SDAnimatedImageView
#if SD_UIKIT
@dynamic animationRepeatCount; 
#endif



#if SD_MAC
+ (instancetype)imageViewWithImage:(NSImage *)image
{
    NSRect frame = NSMakeRect(0, 0, image.size.width, image.size.height);
    SDAnimatedImageView *imageView = [[SDAnimatedImageView alloc] initWithFrame:frame];
    [imageView setImage:image];
    return imageView;
}
#else


- (instancetype)initWithImage:(UIImage *)image
{
    self = [super initWithImage:image];
    if (self) {
        [self commonInit];
    }
    return self;
}


- (instancetype)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage
{
    self = [super initWithImage:image highlightedImage:highlightedImage];
    if (self) {
        [self commonInit];
    }
    return self;
}
#endif

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (void)commonInit
{
    
    
    self.autoPlayAnimatedImage = YES;
    self.shouldCustomLoopCount = NO;
    self.shouldIncrementalLoad = YES;
    self.playbackRate = 1.0;
#if SD_MAC
    self.wantsLayer = YES;
#endif
    
    _initFinished = YES;
}




- (void)setImage:(UIImage *)image
{
    if (self.image == image) {
        return;
    }
    
    
    [self updateIsProgressiveWithImage:image];
    
    if (!self.isProgressive) {
        
        self.player = nil;
        self.currentFrame = nil;
        self.currentFrameIndex = 0;
        self.currentLoopCount = 0;
    }
    
    
    super.image = image;
    if ([image.class conformsToProtocol:@protocol(SDAnimatedImage)] && [(id<SDAnimatedImage>)image animatedImageFrameCount] > 1) {
        if (!self.player) {
            id<SDAnimatedImageProvider> provider;
            
            if (self.isProgressive) {
                provider = [(id<SDAnimatedImage>)image animatedCoder];
            } else {
                provider = (id<SDAnimatedImage>)image;
            }
            
            if (self.animationTransformer) {
                
                provider = [[SDAnimatedImageFrameProvider alloc] initWithProvider:provider transformer:self.animationTransformer];
                self.player = [SDAnimatedImagePlayer playerWithProvider:provider];
            } else {
                
                self.player = [SDAnimatedImagePlayer playerWithProvider:provider];
            }
        } else {
            
            self.player.totalFrameCount = [(id<SDAnimatedImage>)image animatedImageFrameCount];
        }
        
        if (!self.player) {
            
            return;
        }
        
        
        if (self.shouldCustomLoopCount) {
            self.player.totalLoopCount = self.animationRepeatCount;
        }
        
        
        self.player.runLoopMode = self.runLoopMode;
        
        
        self.player.maxBufferSize = self.maxBufferSize;
        
        
        self.player.playbackRate = self.playbackRate;
        
        
        self.player.playbackMode = self.playbackMode;

        
        @weakify(self);
        self.player.animationFrameHandler = ^(NSUInteger index, UIImage * frame) {
            @strongify(self);
            self.currentFrameIndex = index;
            self.currentFrame = frame;
            [self.imageViewLayer setNeedsDisplay];
        };
        self.player.animationLoopHandler = ^(NSUInteger loopCount) {
            @strongify(self);
            
            if (self.isProgressive) {
                NSUInteger lastFrameIndex = self.player.totalFrameCount - 1;
                [self.player seekToFrameAtIndex:lastFrameIndex loopCount:0];
                [self.player pausePlaying];
            } else {
                self.currentLoopCount = loopCount;
            }
        };
        
        
        super.highlighted = NO;
        
        [self stopAnimating];
        [self checkPlay];
    }
    [self.imageViewLayer setNeedsDisplay];
}



- (void)setRunLoopMode:(NSRunLoopMode)runLoopMode
{
    _runLoopMode = [runLoopMode copy];
    self.player.runLoopMode = runLoopMode;
}

- (NSRunLoopMode)runLoopMode
{
    if (!_runLoopMode) {
        _runLoopMode = [[self class] defaultRunLoopMode];
    }
    return _runLoopMode;
}

+ (NSString *)defaultRunLoopMode {
    
    return [NSProcessInfo processInfo].activeProcessorCount > 1 ? NSRunLoopCommonModes : NSDefaultRunLoopMode;
}

- (void)setMaxBufferSize:(NSUInteger)maxBufferSize
{
    _maxBufferSize = maxBufferSize;
    self.player.maxBufferSize = maxBufferSize;
}

- (NSUInteger)maxBufferSize {
    return _maxBufferSize; 
}

- (void)setPlaybackRate:(double)playbackRate
{
    _playbackRate = playbackRate;
    self.player.playbackRate = playbackRate;
}

- (double)playbackRate
{
    if (!_initFinished) {
        return 1.0; 
    }
    return _playbackRate;
}

- (void)setPlaybackMode:(SDAnimatedImagePlaybackMode)playbackMode {
    _playbackMode = playbackMode;
    self.player.playbackMode = playbackMode;
}

- (SDAnimatedImagePlaybackMode)playbackMode {
    if (!_initFinished) {
        return SDAnimatedImagePlaybackModeNormal; 
    }
    return _playbackMode;
}


- (BOOL)shouldIncrementalLoad
{
    if (!_initFinished) {
        return YES; 
    }
    return _initFinished;
}




#if SD_MAC
- (void)viewDidMoveToSuperview
#else
- (void)didMoveToSuperview
#endif
{
#if SD_MAC
    [super viewDidMoveToSuperview];
#else
    [super didMoveToSuperview];
#endif
    
    [self checkPlay];
}

#if SD_MAC
- (void)viewDidMoveToWindow
#else
- (void)didMoveToWindow
#endif
{
#if SD_MAC
    [super viewDidMoveToWindow];
#else
    [super didMoveToWindow];
#endif
    
    [self checkPlay];
}

#if SD_MAC
- (void)setAlphaValue:(CGFloat)alphaValue
#else
- (void)setAlpha:(CGFloat)alpha
#endif
{
#if SD_MAC
    [super setAlphaValue:alphaValue];
#else
    [super setAlpha:alpha];
#endif
    
    [self checkPlay];
}

- (void)setHidden:(BOOL)hidden
{
    [super setHidden:hidden];
    
    [self checkPlay];
}




- (void)setAnimationRepeatCount:(NSInteger)animationRepeatCount
{
#if SD_UIKIT
    [super setAnimationRepeatCount:animationRepeatCount];
#else
    _animationRepeatCount = animationRepeatCount;
#endif
    
    if (self.shouldCustomLoopCount) {
        self.player.totalLoopCount = animationRepeatCount;
    }
}

- (void)startAnimating
{
    if (self.player) {
        [self updateShouldAnimate];
        if (self.shouldAnimate) {
            [self.player startPlaying];
        }
    } else {
#if SD_UIKIT
        [super startAnimating];
#else
        [super setAnimates:YES];
#endif
    }
}

- (void)stopAnimating
{
    if (self.player) {
        if (self.resetFrameIndexWhenStopped) {
            [self.player stopPlaying];
        } else {
            [self.player pausePlaying];
        }
        if (self.clearBufferWhenStopped) {
            [self.player clearFrameBuffer];
        }
    } else {
#if SD_UIKIT
        [super stopAnimating];
#else
        [super setAnimates:NO];
#endif
    }
}

#if SD_UIKIT
- (BOOL)isAnimating
{
    if (self.player) {
        return self.player.isPlaying;
    } else {
        return [super isAnimating];
    }
}
#endif

#if SD_MAC
- (BOOL)animates
{
    if (self.player) {
        return self.player.isPlaying;
    } else {
        return [super animates];
    }
}

- (void)setAnimates:(BOOL)animates
{
    if (animates) {
        [self startAnimating];
    } else {
        [self stopAnimating];
    }
}
#endif



- (void)setHighlighted:(BOOL)highlighted
{
    
    if (!self.player) {
        [super setHighlighted:highlighted];
    }
}






- (void)checkPlay
{
    
    if (self.player && self.autoPlayAnimatedImage) {
        [self updateShouldAnimate];
        if (self.shouldAnimate) {
            [self startAnimating];
        } else {
            [self stopAnimating];
        }
    }
}



- (void)updateShouldAnimate
{
#if SD_MAC
    BOOL isVisible = self.window && self.superview && ![self isHidden] && self.alphaValue > 0.0;
#else
    BOOL isVisible = self.window && self.superview && ![self isHidden] && self.alpha > 0.0;
#endif
    self.shouldAnimate = self.player && isVisible;
}


- (void)updateIsProgressiveWithImage:(UIImage *)image
{
    self.isProgressive = NO;
    if (!self.shouldIncrementalLoad) {
        
        return;
    }
    
    
    id<SDAnimatedImageCoder> currentAnimatedCoder = [self progressiveAnimatedCoderForImage:image];
    if (currentAnimatedCoder) {
        UIImage *previousImage = self.image;
        if (!previousImage) {
            
            self.isProgressive = YES;
        } else {
            id<SDAnimatedImageCoder> previousAnimatedCoder = [self progressiveAnimatedCoderForImage:previousImage];
            if (previousAnimatedCoder == currentAnimatedCoder) {
                
                self.isProgressive = YES;
            }
        }
    }
}


- (id<SDAnimatedImageCoder, SDProgressiveImageCoder>)progressiveAnimatedCoderForImage:(UIImage *)image
{
    if ([image.class conformsToProtocol:@protocol(SDAnimatedImage)] && image.sd_isIncremental && [image respondsToSelector:@selector(animatedCoder)]) {
        id<SDAnimatedImageCoder> animatedCoder = [(id<SDAnimatedImage>)image animatedCoder];
        if ([animatedCoder respondsToSelector:@selector(initIncrementalWithOptions:)]) {
            return (id<SDAnimatedImageCoder, SDProgressiveImageCoder>)animatedCoder;
        }
    }
    return nil;
}





- (void)displayLayer:(CALayer *)layer
{
    UIImage *currentFrame = self.currentFrame;
    if (currentFrame) {
        layer.contentsScale = currentFrame.scale;
        layer.contents = (__bridge id)currentFrame.CGImage;
    } else {
        
        if ([UIImageView instancesRespondToSelector:@selector(displayLayer:)]) {
            [super displayLayer:layer];
        } else {
            
            currentFrame = super.image;
            layer.contentsScale = currentFrame.scale;
            layer.contents = (__bridge id)currentFrame.CGImage;
        }
    }
}

#if SD_UIKIT
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    
    
    
    
    [super traitCollectionDidChange:previousTraitCollection];
    [self.imageViewLayer setNeedsDisplay];
}
#endif

#if SD_MAC


- (NSView *)imageView {
    NSImageView *imageView = objc_getAssociatedObject(self, SD_SEL_SPI(imageView));
    if (!imageView) {
        
        imageView = objc_getAssociatedObject(self, SD_SEL_SPI(imageSubview));
    }
    return imageView;
}


- (CALayer *)imageViewLayer {
    NSView *imageView = self.imageView;
    if (!imageView) {
        return nil;
    }
    if (!_imageViewLayer) {
        _imageViewLayer = [CALayer new];
        _imageViewLayer.delegate = self;
        imageView.layer = _imageViewLayer;
        imageView.wantsLayer = YES;
    }
    return _imageViewLayer;
}
#else

- (CALayer *)imageViewLayer {
    return self.layer;
}

#endif

@end

#endif
