#import "TGModernFlatteningViewModel.h"

#import "TGModernFlatteningView.h"

#import "TGImageUtils.h"

CGFloat TGModernFlatteningViewModelTilingLimit = 512.0f;

@interface TGModernFlatteningViewModel ()
{
    bool _needsContentUpdate;
    TGModernViewContext *_context;
    
    bool _tiledMode;
    TGModernViewStorage *_viewStorage;
    NSMutableArray *_tiledPartViews;
}

@end

@implementation TGModernFlatteningViewModel

- (instancetype)init
{
    return [self initWithContext:nil];
}

- (id)initWithContext:(TGModernViewContext *)context
{
    self = [super init];
    if (self != nil)
    {
        _context = context;
        self.disableSubmodelAutomaticBinding = 1;
    }
    return self;
}

- (Class)viewClass
{
    return [TGModernFlatteningView class];
}

- (void)setTiledMode:(bool)tiledMode
{
    if (tiledMode != _tiledMode)
    {
        _tiledMode = tiledMode;
        
        if ([self boundView] != nil)
        {
            if (_tiledMode)
            {
                if (_tiledPartViews == nil)
                    _tiledPartViews = [[NSMutableArray alloc] init];
                
                if (_context == nil || !_context.contentUpdatesDisabled)
                    [self boundView].layer.contents = nil;
            }
            else
            {
                if (_context == nil || !_context.contentUpdatesDisabled)
                    [self _dropTiledPartViews];
            }
        }
    }
}

- (void)bindViewToContainer:(UIView *)container viewStorage:(TGModernViewStorage *)viewStorage
{
    [super bindViewToContainer:container viewStorage:viewStorage];
    
    _viewStorage = viewStorage;
    
    [self _updateSubmodelContents];
}

- (void)unbindView:(TGModernViewStorage *)viewStorage
{
    [super unbindView:viewStorage];
    
    [self _dropTiledPartViews];
    
    _needsContentUpdate = false;
}

- (void)_dropTiledPartViews
{
    if (_tiledPartViews != nil)
    {
        for (TGModernFlatteningView *tileView in _tiledPartViews)
        {
            [tileView removeFromSuperview];
            [_viewStorage enqueueView:tileView];
        }
        
        [_tiledPartViews removeAllObjects];
    }
}

+ (CGContextRef)_createContentContext:(CGSize)size
{
    CGSize contextSize = size;
    CGFloat scaling = TGScreenScaling();

    contextSize.width *= scaling;
    contextSize.height *= scaling;
    
    size_t bytesPerRow = 4 * (int)contextSize.width;
    bytesPerRow = (bytesPerRow + 15) & ~15;
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
    
    CGContextRef context = CGBitmapContextCreate(NULL, (int)contextSize.width, (int)contextSize.height, 8, bytesPerRow, colorSpace, bitmapInfo);
    CGColorSpaceRelease(colorSpace);
    
    CGContextScaleCTM(context, scaling, scaling);
    
    return context;
}

- (void)animateWithSnapshot
{
    TGModernFlatteningView *view = (TGModernFlatteningView *)[self boundView];
    if (view != nil && !CGRectIsEmpty(self.frame) && view.layer.contents != nil)
    {
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithCGImage:(__bridge CGImageRef)view.layer.contents]];
        imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        imageView.frame = self.frame;
        [view.superview addSubview:imageView];
        
        imageView.alpha = 1.0f;
        view.alpha = 0.0f;
        [UIView animateWithDuration:0.3 animations:^
        {
            view.alpha = 1.0f;
            imageView.alpha = 0.0f;
        } completion:^(__unused BOOL finished)
        {
            [imageView removeFromSuperview];
        }];
    }
}

- (void)_updateSubmodelContents
{
    TGModernFlatteningView *view = (TGModernFlatteningView *)[self boundView];
    if (view != nil && !CGRectIsEmpty(self.frame))
    {
        if (_context != nil && _context.contentUpdatesDisabled)
            _needsContentUpdate = true;
        else if (!_tiledMode)
        {
            [self _updateSubmodelContentsForLayer:view.layer visibleRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
            _needsContentUpdate = false;
        }
        else
        {
            view.layer.contents = nil;
            
            [self _dropTiledPartViews];
            _needsContentUpdate = false;
        }
    }
}

- (void)setNeedsSubmodelContentsUpdate
{
    _needsContentUpdate = true;
}

- (bool)needsSubmodelContentsUpdate
{
    return _needsContentUpdate;
}

- (void)updateSubmodelContentsIfNeeded
{
    if (_needsContentUpdate)
        [self _updateSubmodelContents];
}

- (void)updateSubmodelContentsForVisibleRect:(CGRect)rect
{
    if (_tiledMode && (_context == nil || !_context.contentUpdatesDisabled) && [self boundView] != nil)
    {
        [UIView performWithoutAnimation:^
        {
            TGModernFlatteningView *view = (TGModernFlatteningView *)[self boundView];
            
            CGSize tiledViewSize = CGSizeMake(view.frame.size.width, TGModernFlatteningViewModelTilingLimit);
            
            CGRect frame = self.frame;
            for (CGFloat originY = 0.0f; originY < frame.size.height; originY += tiledViewSize.height)
            {
                NSInteger compareOriginY = (NSInteger)originY;
                int foundTileIndex = -1;
                
                if (_tiledPartViews == nil)
                    _tiledPartViews = [[NSMutableArray alloc] init];
                
                int index = -1;
                for (TGModernFlatteningView *tileView in _tiledPartViews)
                {
                    index++;
                    NSInteger tileOriginY = (NSInteger)tileView.frame.origin.y;
                    if (tileOriginY == compareOriginY)
                    {
                        foundTileIndex = index;
                        break;
                    }
                }
                
                CGRect possibleRect = CGRectMake(rect.origin.x, originY, rect.size.width, tiledViewSize.height);
                if (!CGRectIsNull(CGRectIntersection(rect, possibleRect)))
                {
                    if (foundTileIndex == -1)
                    {
                        static NSString *tileViewIdentifier = @"TGModernFlatteningView";
                        
                        TGModernFlatteningView *tileView = (TGModernFlatteningView *)[_viewStorage dequeueViewWithIdentifier:tileViewIdentifier viewStateIdentifier:nil];
                        if (tileView == nil)
                        {
                            tileView = [[TGModernFlatteningView alloc] init];
                            tileView.viewIdentifier = tileViewIdentifier;
                        }
                        
                        CGRect tileFrame = CGRectMake(0.0f, originY, tiledViewSize.width, MIN(tiledViewSize.height, frame.size.height - originY));
                        
                        tileView.frame = tileFrame;
                        [_tiledPartViews addObject:tileView];
                        [view addSubview:tileView];
                        
                        [self _updateSubmodelContentsForLayer:tileView.layer visibleRect:tileFrame];
                    }
                }
                else if (foundTileIndex != -1)
                {
                    TGModernFlatteningView *tileView = _tiledPartViews[foundTileIndex];
                    [tileView removeFromSuperview];
                    [_viewStorage enqueueView:tileView];
                    [_tiledPartViews removeObjectAtIndex:foundTileIndex];
                }
            }
        }];
    }
}

- (void)_updateSubmodelContentsForLayer:(CALayer *)layer visibleRect:(CGRect)visibleRect
{
    CGRect clipRect = CGRectIntersection(visibleRect, CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height));
    if (clipRect.size.height < FLT_EPSILON)
        return;
    
    CGSize contextSize = clipRect.size;
    CGContextRef context = [TGModernFlatteningViewModel _createContentContext:contextSize];
    UIGraphicsPushContext(context);
    
    CGContextTranslateCTM(context, contextSize.width / 2.0f, contextSize.height / 2.0f);
    CGContextScaleCTM(context, 1.0f, -1.0f);
    CGContextTranslateCTM(context, -contextSize.width / 2.0f, -contextSize.height / 2.0f);
    
    if (visibleRect.origin.y > FLT_EPSILON)
        CGContextTranslateCTM(context, 0.0f, -visibleRect.origin.y);
    
    [self drawSubmodelsInContext:context];
    
    UIGraphicsPopContext();
    
    CGImageRef contextImageRef = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    
    layer.contents = (__bridge id)(contextImageRef);
    CGImageRelease(contextImageRef);
}

@end
