/*
 Copyright 2015 OpenMarket Ltd
 Copyright 2017 Vector Creations Ltd
 Copyright 2018 New Vector Ltd
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#import "MXKImageView.h"
#import "MXKPieChartView.h"
#import "MXKAttachment.h"

#import "MXKTools.h"

@interface MXKImageView ()
{
    NSString *mxcURI;
    NSString *mimeType;
    UIImageOrientation imageOrientation;
    
    // additional settings used in case of thumbnail.
    CGSize thumbnailViewSize;
    MXThumbnailingMethod thumbnailMethod;
    
    UIImage *currentImage;
    
    // the loading view is composed with the spinner and a pie chart
    // the spinner is display until progress > 0
    UIView *loadingView;
    UIActivityIndicatorView *waitingDownloadSpinner;
    MXKPieChartView *pieChartView;
    UILabel *progressInfoLabel;
    
    // validation buttons
    UIButton *leftButton;
    UIButton *rightButton;
    
    NSString *leftButtonTitle;
    NSString *rightButtonTitle;
    
    blockMXKImageView_onClick leftHandler;
    blockMXKImageView_onClick rightHandler;
    
    UIView* bottomBarView;
    
    // Subviews
    UIScrollView *scrollView;

    // Current attachment being displayed in the MXKImageView
    MXKAttachment *currentAttachment;
}
@end

@implementation MXKImageView
@synthesize stretchable, mediaFolder, imageView;

#define CUSTOM_IMAGE_VIEW_BUTTON_WIDTH 100

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [self stopActivityIndicator];
    
    if (loadingView)
    {
        [loadingView removeFromSuperview];
        loadingView = nil;
    }
    
    if (bottomBarView)
    {
        [bottomBarView removeFromSuperview];
        bottomBarView = nil;
    }
    
    pieChartView = nil;
}

#pragma mark - Override MXKView

-(void)customizeViewRendering
{
    [super customizeViewRendering];
    
    self.backgroundColor = (_defaultBackgroundColor ? _defaultBackgroundColor : [UIColor blackColor]);
    
    self.contentMode = UIViewContentModeScaleAspectFit;
    self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
}

#pragma mark -

- (void)startActivityIndicator
{
    // create the views if they don't exist
    if (!waitingDownloadSpinner)
    {
        waitingDownloadSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
        
        CGRect frame = waitingDownloadSpinner.frame;
        frame.size.width += 30;
        frame.size.height += 30;
        waitingDownloadSpinner.bounds = frame;
        [waitingDownloadSpinner.layer setCornerRadius:5];
    }
    
    if (!loadingView)
    {
        loadingView = [[UIView alloc] init];
        loadingView.frame = waitingDownloadSpinner.bounds;
        waitingDownloadSpinner.frame = waitingDownloadSpinner.bounds;
        [loadingView addSubview:waitingDownloadSpinner];
        loadingView.backgroundColor = [UIColor clearColor];
        [self addSubview:loadingView];
    }
    
    if (!pieChartView)
    {
        pieChartView = [[MXKPieChartView alloc] init];
        pieChartView.frame = loadingView.bounds;
        pieChartView.progress = 0;
        pieChartView.progressColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.25];
        pieChartView.unprogressColor = [UIColor clearColor];
        
        [loadingView addSubview:pieChartView];
    }
    
    // display the download statistics
    if (_fullScreen && !progressInfoLabel)
    {
        progressInfoLabel = [[UILabel alloc] init];
        progressInfoLabel.backgroundColor = [UIColor whiteColor];
        progressInfoLabel.textColor = [UIColor blackColor];
        progressInfoLabel.font = [UIFont systemFontOfSize:8];
        progressInfoLabel.alpha = 0.25;
        progressInfoLabel.text = @"";
        progressInfoLabel.numberOfLines = 0;
        [progressInfoLabel sizeToFit];
        [self addSubview:progressInfoLabel];
    }
    
    // initvalue
    loadingView.hidden = NO;
    pieChartView.progress = 0;
    
    // Adjust color
    if ([self.backgroundColor isEqual:[UIColor blackColor]])
    {
        waitingDownloadSpinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
        // a preview image could be displayed
        // ensure that the white spinner is visible
        // it could be drawn on a white area
        waitingDownloadSpinner.backgroundColor = [UIColor darkGrayColor];
        
    }
    else
    {
        waitingDownloadSpinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
    }
    
    // ensure that the spinner is drawn at the top
    [loadingView.superview bringSubviewToFront:loadingView];
    
    // Adjust position
    CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
    loadingView.center = center;
    
    // Start
    [waitingDownloadSpinner startAnimating];
}

- (void)stopActivityIndicator
{
    if (waitingDownloadSpinner && waitingDownloadSpinner.isAnimating)
    {
        [waitingDownloadSpinner stopAnimating];
    }
    
    pieChartView.progress = 0;
    loadingView.hidden = YES;
    
    if (progressInfoLabel)
    {
        [progressInfoLabel removeFromSuperview];
        progressInfoLabel = nil;
    }
}

#pragma mark - setters/getters

- (void)setDefaultBackgroundColor:(UIColor *)defaultBackgroundColor
{
    _defaultBackgroundColor = defaultBackgroundColor;
    [self customizeViewRendering];
}

- (void)setImage:(UIImage *)anImage
{
    // remove the observers
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    currentImage = anImage;
    imageView.image = anImage;

    [self initScrollZoomFactors];
}

- (UIImage*)image
{
    return currentImage;
}

- (void)showFullScreen
{
    // The full screen display mode is supported only if the shared application instance is available.
    UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
    if (sharedApplication)
    {
        _fullScreen = YES;
        
        [self initLayout];
        
        if (self.superview)
        {
            [super removeFromSuperview];
        }
        
        UIWindow *window = [sharedApplication keyWindow];
        
        self.frame = window.bounds;
        [window addSubview:self];
    }
}

#pragma mark -
- (IBAction)onButtonToggle:(id)sender
{
    if (sender == leftButton)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            self->leftHandler(self, self->leftButtonTitle);
        });
    }
    else if (sender == rightButton)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            self->rightHandler(self, self->rightButtonTitle);
        });
    }
}

// add a generic button to the bottom view
// return the added UIButton
- (UIButton*) addbuttonWithTitle:(NSString*)title
{
    UIButton* button = [[UIButton alloc] init];
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitle:title forState:UIControlStateHighlighted];
    
    if (_fullScreen)
    {
        // use the same text color as the tabbar
        [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
    }
    // TODO
    //    else {
    //        // use the same text color as the tabbar
    //        [button setTitleColor:[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor forState:UIControlStateNormal];
    //        [button setTitleColor:[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor forState:UIControlStateHighlighted];
    //    }
    
    // keep the bottomView background color
    button.backgroundColor = [UIColor clearColor];
    
    [button addTarget:self action:@selector(onButtonToggle:) forControlEvents:UIControlEventTouchUpInside];
    [bottomBarView addSubview:button];
    
    return button;
}

- (void)initScrollZoomFactors
{
    // check if the image can be zoomed
    if (self.image && self.stretchable && imageView.frame.size.width && imageView.frame.size.height)
    {
        // ensure that the content size is properly initialized
        scrollView.contentSize = scrollView.frame.size;
        
        // compute the appliable zoom factor
        // assume that the user does not expect to zoom more than 100%
        CGSize imageSize = self.image.size;
        
        CGFloat scaleX = imageSize.width  / imageView.frame.size.width;
        CGFloat scaleY = imageSize.height / imageView.frame.size.height;
        
        if (scaleX < scaleY)
        {
            scaleX = scaleY;
        }
        
        if (scaleX < 1.0)
        {
            scaleX = 1.0;
        }
        
        scrollView.zoomScale        = 1.0;
        scrollView.minimumZoomScale = 1.0;
        scrollView.maximumZoomScale = scaleX;
        
        // update the image frame to ensure that it fits to the scrollview frame
        imageView.frame = scrollView.bounds;
    }
}

- (void)removeFromSuperview
{
    [super removeFromSuperview];
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    if (pieChartView)
    {
        [self stopActivityIndicator];
    }
}

- (void)initLayout
{
    // create the subviews if they don't exist
    if (!scrollView)
    {
        scrollView = [[UIScrollView alloc] init];
        scrollView.delegate = self;
        scrollView.backgroundColor = [UIColor clearColor];
        [self addSubview:scrollView];
        
        imageView = [[UIImageView alloc] init];
        imageView.backgroundColor = [UIColor clearColor];
        imageView.userInteractionEnabled = YES;
        imageView.contentMode = self.contentMode;
        [scrollView addSubview:imageView];
    }
}

- (void)layoutSubviews
{
    // call upper layer
    [super layoutSubviews];
    
    [self initLayout];
    
    // the image has been updated
    if (imageView.image != self.image)
    {
        imageView.image = self.image;
    }
    
    CGRect tabBarFrame = CGRectZero;
    UITabBarController *tabBarController = nil;
    UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
    
    if (leftButtonTitle || rightButtonTitle)
    {
        UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
        if (sharedApplication)
        {
            safeAreaInsets = [sharedApplication keyWindow].safeAreaInsets;

            UIViewController *rootViewController = [sharedApplication keyWindow].rootViewController;
            tabBarController = rootViewController.tabBarController;
            if (!tabBarController && [rootViewController isKindOfClass:[UITabBarController class]])
            {
                tabBarController = (UITabBarController*)rootViewController;
            }
        }
        
        if (tabBarController)
        {
            tabBarFrame = tabBarController.tabBar.frame;
        }
        else
        {
            // Define a default tabBar frame
            tabBarFrame = CGRectMake(0, 0, self.frame.size.width, 44 + safeAreaInsets.bottom);
        }
    }
    
    // update the scrollview frame
    CGRect oneSelfFrame = self.frame;
    CGRect scrollViewFrame = CGRectIntegral(scrollView.frame);
    
    if (leftButtonTitle || rightButtonTitle)
    {
        oneSelfFrame.size.height -= tabBarFrame.size.height;
    }
    
    oneSelfFrame = CGRectIntegral(oneSelfFrame);
    oneSelfFrame.origin = scrollViewFrame.origin = CGPointZero;
    
    // use integral rect to avoid rounded value issue (float precision)
    if (!CGRectEqualToRect(oneSelfFrame, scrollViewFrame))
    {
        scrollView.frame = oneSelfFrame;
        imageView.frame = oneSelfFrame;
        
        [self initScrollZoomFactors];
    }
    
    // check if the dedicated buttons are already added
    if (leftButtonTitle || rightButtonTitle)
    {
        
        if (!bottomBarView)
        {
            bottomBarView = [[UIView alloc] init];
            
            if (leftButtonTitle)
            {
                leftButton = [self addbuttonWithTitle:leftButtonTitle];
            }
            
            rightButton = [[UIButton alloc] init];
            
            if (rightButtonTitle)
            {
                rightButton = [self addbuttonWithTitle:rightButtonTitle];
            }
            
            // in fullscreen, display both buttons above the view (do the same if there is no tab bar)
            if (_fullScreen || tabBarController == nil)
            {
                bottomBarView.backgroundColor = [UIColor blackColor];
                [self addSubview:bottomBarView];
            }
            else
            {
                // default tabbar background color
                CGFloat base = 248.0 / 255.0f;
                bottomBarView.backgroundColor = [UIColor colorWithRed:base green:base blue:base alpha:1.0];
                
                // Display them over the tabbar
                [tabBarController.tabBar addSubview:bottomBarView];
            }
        }
        
        if (_fullScreen)
        {
            tabBarFrame.origin.y = self.frame.size.height - tabBarFrame.size.height;
        }
        else
        {
            tabBarFrame.origin.y = 0;
        }
        bottomBarView.frame = tabBarFrame;
        
        if (leftButton)
        {
            leftButton.frame = CGRectMake(safeAreaInsets.left, 0, CUSTOM_IMAGE_VIEW_BUTTON_WIDTH, bottomBarView.frame.size.height - safeAreaInsets.bottom);
        }
        
        if (rightButton)
        {
            rightButton.frame = CGRectMake(bottomBarView.frame.size.width - CUSTOM_IMAGE_VIEW_BUTTON_WIDTH - safeAreaInsets.right, 0, CUSTOM_IMAGE_VIEW_BUTTON_WIDTH, bottomBarView.frame.size.height - safeAreaInsets.bottom);
        }
    }
    
    if (!loadingView.hidden)
    {
        // ensure that the spinner is drawn at the top
        [loadingView.superview bringSubviewToFront:loadingView];
        
        // Adjust positions
        CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
        loadingView.center = center;
        
        CGRect progressInfoLabelFrame = progressInfoLabel.frame;
        progressInfoLabelFrame.origin.x = center.x - (progressInfoLabelFrame.size.width / 2);
        progressInfoLabelFrame.origin.y = 10 + loadingView.frame.origin.y + loadingView.frame.size.height;
        progressInfoLabel.frame = progressInfoLabelFrame;
    }
}

- (void)setHideActivityIndicator:(BOOL)hideActivityIndicator
{
    _hideActivityIndicator = hideActivityIndicator;
    if (hideActivityIndicator)
    {
        [self stopActivityIndicator];
    }
    else if (mxcURI)
    {
        NSString *downloadId = [MXMediaManager downloadIdForMatrixContentURI:mxcURI inFolder:mediaFolder];
        if ([MXMediaManager existingDownloaderWithIdentifier:downloadId])
        {
            // Loading is in progress, start activity indicator
            [self startActivityIndicator];
        }
    }
}

- (void)setImageURI:(NSString *)mxContentURI
           withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
       previewImage:(UIImage*)previewImage
       mediaManager:(MXMediaManager*)mediaManager
{
    [self setImageURI:mxContentURI
             withType:mimeType
  andImageOrientation:orientation
          isThumbnail:NO
         previewImage:previewImage
         mediaManager:mediaManager];
}

- (void)setImageURI:(NSString *)mxContentURI
           withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
      toFitViewSize:(CGSize)viewSize
         withMethod:(MXThumbnailingMethod)thumbnailingMethod
       previewImage:(UIImage*)previewImage
       mediaManager:(MXMediaManager*)mediaManager
{
    // Store the thumbnail settings
    thumbnailViewSize = viewSize;
    thumbnailMethod = thumbnailingMethod;
    
    [self setImageURI:mxContentURI
             withType:mimeType
  andImageOrientation:orientation
          isThumbnail:YES
         previewImage:previewImage
         mediaManager:mediaManager];
}

- (void)setImageURI:(NSString *)mxContentURI
           withType:(NSString *)mimeType
andImageOrientation:(UIImageOrientation)orientation
        isThumbnail:(BOOL)isThumbnail
       previewImage:(UIImage*)previewImage
       mediaManager:(MXMediaManager*)mediaManager
{
    // Remove any pending observers
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    // Reset other data
    currentAttachment = nil;
    
    mxcURI = mxContentURI;
    if (!mxcURI)
    {
        // Set preview by default
        self.image = previewImage;
        return;
    }
    
    // Store image orientation
    imageOrientation = orientation;
    
    // Store the mime type used to define the cache path of the image.
    mimeType = mimeType;
    if (!mimeType.length)
    {
        // Set default mime type if no information is available
        mimeType = @"image/jpeg";
    }
    
    // Retrieve the image from cache if any
    NSString *cacheFilePath;
    if (isThumbnail)
    {
        cacheFilePath = [MXMediaManager thumbnailCachePathForMatrixContentURI:mxcURI
                                                                      andType:mimeType
                                                                     inFolder:mediaFolder
                                                                toFitViewSize:thumbnailViewSize
                                                                   withMethod:thumbnailMethod];
    }
    else
    {
        cacheFilePath = [MXMediaManager cachePathForMatrixContentURI:mxcURI
                                                             andType:mimeType
                                                            inFolder:mediaFolder];
    }
    
    UIImage* image = _enableInMemoryCache ? [MXMediaManager loadThroughCacheWithFilePath:cacheFilePath] : [MXMediaManager loadPictureFromFilePath:cacheFilePath];
    if (image)
    {
        if (imageOrientation != UIImageOrientationUp)
        {
            self.image = [UIImage imageWithCGImage:image.CGImage scale:1.0 orientation:imageOrientation];
        }
        else
        {
            self.image = image;
        }
        
        [self stopActivityIndicator];
    }
    else
    {
        // Set preview until the image is loaded
        self.image = previewImage;
        
        // Check whether the image download is in progress
        NSString *downloadId;
        if (isThumbnail)
        {
            downloadId = [MXMediaManager thumbnailDownloadIdForMatrixContentURI:mxcURI
                                                                       inFolder:mediaFolder
                                                                  toFitViewSize:thumbnailViewSize
                                                                     withMethod:thumbnailMethod];
        }
        else
        {
            downloadId = [MXMediaManager downloadIdForMatrixContentURI:mxcURI inFolder:mediaFolder];
        }
        
        MXMediaLoader* loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId];
        if (!loader && mediaManager)
        {
            // Trigger the download
            if (isThumbnail)
            {
                loader = [mediaManager downloadThumbnailFromMatrixContentURI:mxcURI
                                                                    withType:mimeType
                                                                    inFolder:mediaFolder
                                                               toFitViewSize:thumbnailViewSize
                                                                  withMethod:thumbnailMethod
                                                                     success:nil
                                                                     failure:nil];
            }
            else
            {
                loader = [mediaManager downloadMediaFromMatrixContentURI:mxcURI
                                                                withType:mimeType
                                                                inFolder:mediaFolder];
            }
        }
        
        if (loader)
        {
            // update the progress UI with the current info
            if (!_hideActivityIndicator)
            {
                [self startActivityIndicator];
            }
            [self updateProgressUI:loader.statisticsDict];
            
            [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMediaLoaderStateDidChange:) name:kMXMediaLoaderStateDidChangeNotification object:loader];
        }
    }
}

- (void)setAttachment:(MXKAttachment *)attachment
{
    // Remove any pending observers
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    // Set default orientation
    imageOrientation = UIImageOrientationUp;
    
    mediaFolder = attachment.eventRoomId;
    mxcURI = attachment.contentURL;
    mimeType = attachment.contentInfo[@"mimetype"];
    if (!mimeType.length)
    {
        // Set default mime type if no information is available
        mimeType = @"image/jpeg";
    }
    
    // while we wait for the content to download
    self.image = [attachment getCachedThumbnail];
    
    if (!_hideActivityIndicator)
    {
        [self startActivityIndicator];
    }

    currentAttachment = attachment;
    
    MXWeakify(self);
    [attachment getImage:^(MXKAttachment *attachment2, UIImage *img) {
        MXStrongifyAndReturnIfNil(self);

        if (self->currentAttachment != attachment2)
        {
            return;
        }

        self.image = img;
        [self stopActivityIndicator];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
    } failure:^(MXKAttachment *attachment2, NSError *error) {
        MXLogDebug(@"Unable to fetch image attachment! %@", error);
        MXStrongifyAndReturnIfNil(self);
        
        if (self->currentAttachment != attachment2)
        {
            return;
        }

        [self stopActivityIndicator];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
    }];
    
    // Check whether the image download is in progress
    NSString *downloadId = attachment.downloadId;
    MXMediaLoader* loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId];
    if (loader)
    {
        // Observer this loader to display progress
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(checkProgressOnMediaLoaderStateChange:)
                                                     name:kMXMediaLoaderStateDidChangeNotification
                                                   object:loader];
    }
}

- (void)setAttachmentThumb:(MXKAttachment *)attachment
{
    // Remove any pending observers
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    // Store image orientation
    imageOrientation = attachment.thumbnailOrientation;
    
    mediaFolder = attachment.eventRoomId;
    
    // Remove the existing image (if any) by using the potential preview.
    self.image = attachment.previewImage;
    
    if (!_hideActivityIndicator)
    {
        [self startActivityIndicator];
    }

    currentAttachment = attachment;
    
    MXWeakify(self);
    [attachment getThumbnail:^(MXKAttachment *attachment2, UIImage *img) {
        MXStrongifyAndReturnIfNil(self);
        
        dispatch_async(dispatch_get_main_queue(), ^{

            if (self->currentAttachment != attachment2)
            {
                return;
            }
            
            if (img && self->imageOrientation != UIImageOrientationUp)
            {
                self.image = [UIImage imageWithCGImage:img.CGImage scale:1.0 orientation:self->imageOrientation];
            }
            else
            {
                self.image = img;
            }
            [self stopActivityIndicator];
        });
            
    } failure:^(MXKAttachment *attachment2, NSError *error) {
        MXStrongifyAndReturnIfNil(self);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (self->currentAttachment != attachment2)
            {
                return;
            }
            
            [self stopActivityIndicator];
        });
    }];
}

- (void)updateProgressUI:(NSDictionary*)downloadStatsDict
{
    // Sanity check: updateProgressUI may be called while there is no stats available
    // This happens when the download failed at the very beginning.
    if (nil == downloadStatsDict)
    {
        return;
    }
    
    NSNumber* progressNumber = [downloadStatsDict valueForKey:kMXMediaLoaderProgressValueKey];
    
    if (progressNumber)
    {
        pieChartView.progress = progressNumber.floatValue;
        waitingDownloadSpinner.hidden = YES;
    }
    
    if (progressInfoLabel)
    {
        NSNumber* downloadRate = [downloadStatsDict valueForKey:kMXMediaLoaderCurrentDataRateKey];
        
        NSNumber* completedBytesCount = [downloadStatsDict valueForKey:kMXMediaLoaderCompletedBytesCountKey];
        NSNumber* totalBytesCount = [downloadStatsDict valueForKey:kMXMediaLoaderTotalBytesCountKey];
        
        NSMutableString* text = [[NSMutableString alloc] init];

        if (completedBytesCount && totalBytesCount)
        {
            NSString* progressString = [NSString stringWithFormat:@"%@ / %@", [NSByteCountFormatter stringFromByteCount:completedBytesCount.longLongValue countStyle:NSByteCountFormatterCountStyleFile], [NSByteCountFormatter stringFromByteCount:totalBytesCount.longLongValue countStyle:NSByteCountFormatterCountStyleFile]];
            
            [text appendString:progressString];
        }
        
        if (downloadRate)
        {
            if (completedBytesCount && totalBytesCount)
            {
                CGFloat remainimgTime = ((totalBytesCount.floatValue - completedBytesCount.floatValue)) / downloadRate.floatValue;
                [text appendFormat:@" (%@)", [MXKTools formatSecondsInterval:remainimgTime]];
            }
            
            [text appendFormat:@"\n %@/s", [NSByteCountFormatter stringFromByteCount:downloadRate.longLongValue countStyle:NSByteCountFormatterCountStyleFile]];
        }
        
        progressInfoLabel.text = text;
        
        // on multilines, sizeToFit uses the current width
        // so reset it
        progressInfoLabel.frame = CGRectZero;
        
        [progressInfoLabel sizeToFit];
        
        //
        CGRect progressInfoLabelFrame = progressInfoLabel.frame;
        progressInfoLabelFrame.origin.x = self.center.x - (progressInfoLabelFrame.size.width / 2);
        progressInfoLabelFrame.origin.y = 10 + loadingView.frame.origin.y + loadingView.frame.size.height;
        progressInfoLabel.frame = progressInfoLabelFrame;
    }
}

- (void)onMediaLoaderStateDidChange:(NSNotification *)notif
{
    MXMediaLoader *loader = (MXMediaLoader*)notif.object;
    switch (loader.state) {
        case MXMediaLoaderStateDownloadInProgress:
            [self updateProgressUI:loader.statisticsDict];
            break;
        case MXMediaLoaderStateDownloadCompleted:
        {
            [self stopActivityIndicator];
            // update the image
            UIImage* image = [MXMediaManager loadPictureFromFilePath:loader.downloadOutputFilePath];
            if (image)
            {
                if (imageOrientation != UIImageOrientationUp)
                {
                    self.image = [UIImage imageWithCGImage:image.CGImage scale:1.0 orientation:imageOrientation];
                }
                else
                {
                    self.image = image;
                }
            }
            [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
            break;
        }
        case MXMediaLoaderStateDownloadFailed:
        case MXMediaLoaderStateCancelled:
            [self stopActivityIndicator];
            [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
            break;
        default:
            break;
    }
}

- (void)checkProgressOnMediaLoaderStateChange:(NSNotification *)notif
{
    MXMediaLoader *loader = (MXMediaLoader*)notif.object;
    switch (loader.state) {
        case MXMediaLoaderStateDownloadInProgress:
            [self updateProgressUI:loader.statisticsDict];
            break;
        default:
            break;
    }
}

#pragma mark - buttons management

- (void)setLeftButtonTitle: aLeftButtonTitle handler:(blockMXKImageView_onClick)handler
{
    leftButtonTitle = aLeftButtonTitle;
    leftHandler = handler;
}

- (void)setRightButtonTitle:aRightButtonTitle handler:(blockMXKImageView_onClick)handler
{
    rightButtonTitle = aRightButtonTitle;
    rightHandler = handler;
}

- (void)dismissSelection
{
    if (bottomBarView)
    {
        [bottomBarView removeFromSuperview];
        bottomBarView = nil;
    }
}

#pragma mark - UIScrollViewDelegate
// require to be able to zoom an image
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.stretchable ? imageView : nil;
}

@end
