/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 "WXComponent+Display.h"
#import "WXComponent.h"
#import "WXComponent_internal.h"
#import "WXLayer.h"
#import "WXAssert.h"
#import "WXUtility.h"
#import "WXDisplayQueue.h"
#import "WXThreadSafeCounter.h"
#import "UIBezierPath+Weex.h"
#import "WXRoundedRect.h"
#import "WXSDKInstance.h"

#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

typedef NS_ENUM(NSInteger, WXComponentBorderRecord) {
    WXComponentBorderRecordNone = 0,
    WXComponentBorderRecordTop = 1,
    WXComponentBorderRecordRight = 1 << 1,
    WXComponentBorderRecordBottom = 1 << 2,
    WXComponentBorderRecordLeft = 1 << 3,
    WXComponentBorderRecordAll = WXComponentBorderRecordTop | WXComponentBorderRecordRight | WXComponentBorderRecordBottom | WXComponentBorderRecordLeft
};

@implementation WXComponent (Display)

#pragma mark Public

- (void)setNeedsDisplay
{
    WXAssertMainThread();
    
    if (_isCompositingChild) {
        WXComponent *supercomponent = self.supercomponent;
        while (supercomponent) {
            if (supercomponent->_useCompositing) {
                break;
            }
            supercomponent = supercomponent.supercomponent;
        }
        [supercomponent setNeedsDisplay];
    } else if (!_layer || _layer.frame.size.width ==0 || _layer.frame.size.height == 0) {
        return;
    } else {
        [_layer setNeedsDisplay];
    }
}

- (BOOL)needsDrawRect
{
    if (_useCompositing || _isCompositingChild) {
        return YES;
    }
    
    if (![self _needsDrawBorder] && _lastBorderRecords == WXComponentBorderRecordNone) {
        WXLogDebug(@"No need to draw border for %@", self.ref);
        WXPerformBlockOnMainThread(^{
            [self _resetNativeBorderRadius];
        });
        
        return NO;
    }
    
    return YES;
}

- (UIImage *)drawRect:(CGRect)rect
{
    CGSize size = rect.size;
    if (size.width <= 0 || size.height <= 0) {
        WXLogDebug(@"No need to draw border for %@, because width or height is zero", self.ref);
        return nil;
    }
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self _drawBorderWithContext:context size:size];

    return nil;
}

- (void)didFinishDrawingLayer:(BOOL)success
{
    WXAssertMainThread();
}

#pragma mark Private

- (WXDisplayBlock)_displayBlock
{
    __weak WXComponent* wself = self;
    WXDisplayBlock displayBlock = ^UIImage *(CGRect bounds, BOOL(^isCancelled)(void)) {
        if (isCancelled()) {
            return nil;
        }
        
        __strong WXComponent* sself = wself;
        if (sself) {
            UIGraphicsBeginImageContextWithOptions(bounds.size, [sself _bitmapOpaqueWithSize:bounds.size] , 0.0);
            UIImage *image = [sself drawRect:bounds];
            if (!image) {
                image = UIGraphicsGetImageFromCurrentImageContext();
            }
            UIGraphicsEndImageContext();
            
            return image;
        }
        else {
            return nil;
        }
    };
    
    return displayBlock;
}

- (WXDisplayCompletionBlock)_displayCompletionBlock
{
    __weak typeof(self) weakSelf = self;
    return ^(CALayer *layer, BOOL finished) {
        [weakSelf didFinishDrawingLayer:finished];
    };
}

- (void)_initCompositingAttribute:(NSDictionary *)attributes
{
    _useCompositing = attributes[@"compositing"] ? [WXConvert BOOL:attributes[@"compositing"]] : NO;
}

- (void)_willDisplayLayer:(CALayer *)layer
{
    WXAssertMainThread();
    
    if (_isCompositingChild) {
        // compsiting children do not have own layers, so return here.
        return;
    }
    
    CGRect displayBounds = CGRectMake(0, 0, self.calculatedFrame.size.width, self.calculatedFrame.size.height);
    
    BOOL needsDrawRect = [self needsDrawRect];
    WXDisplayBlock displayBlock;
    if (_useCompositing) {
        displayBlock = [self _compositeDisplayBlock];
    } else {
        displayBlock = [self _displayBlock];
    }
    WXDisplayCompletionBlock completionBlock = [self _displayCompletionBlock];
    
    if (!displayBlock || !needsDrawRect) {
        if (completionBlock) {
            completionBlock(layer, NO);
        }
        return;
    }
    
    if (_async) {
        WXThreadSafeCounter *displayCounter = _displayCounter;
        int32_t displayValue = [displayCounter increase];
        BOOL (^isCancelled)(void) = ^BOOL(){
            return displayValue != displayCounter.value;
        };
        
        [WXDisplayQueue addBlock:^{
            if (isCancelled()) {
                if (completionBlock) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        completionBlock(layer, NO);
                    });
                }
                return;
            }
            
            UIImage *image = displayBlock(displayBounds, isCancelled);
            
            dispatch_async(dispatch_get_main_queue(), ^{
                if (isCancelled()) {
                    if (completionBlock) {
                        completionBlock(layer, NO);
                    }
                    return;
                }
                
                layer.contents = (id)(image.CGImage);
                
                if (completionBlock) {
                    completionBlock(layer, YES);
                }
            });
            
        }];
    } else {
        UIImage *image = displayBlock(displayBounds, ^BOOL(){
            return NO;
        });
        
        _layer.contents = (id)image.CGImage;
        
        if (completionBlock) {
            completionBlock(layer, YES);
        }
    }
}

- (void)triggerDisplay
{
    WXPerformBlockOnMainThread(^{
        [self _willDisplayLayer:_layer];
    });
}

- (CGContextRef)beginDrawContext:(CGRect)bounds
{
    UIGraphicsBeginImageContextWithOptions(bounds.size, [self _bitmapOpaqueWithSize:bounds.size], 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
//    float scaleFactor = [[UIScreen mainScreen] scale];
//    CGColorSpaceRef	colorSpace = CGColorSpaceCreateDeviceRGB();
//    CGContextRef context = CGBitmapContextCreate(NULL, bounds.size.width * scaleFactor, bounds.size.height * scaleFactor, 8, 4 * bounds.size.width * scaleFactor, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
//    CGContextScaleCTM(context, scaleFactor, scaleFactor);
//
//    // Adjusts position and invert the image.
//    // The OpenGL uses the image data upside-down compared commom image files.
//    CGContextTranslateCTM(context, 0, bounds.size.height);
//    CGContextScaleCTM(context, 1.0, -1.0);
//    
//    CGColorSpaceRelease(colorSpace);
    
    return context;
}

- (UIImage *)endDrawContext:(CGContextRef)context
{
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
//    CGImageRef imageRef= CGBitmapContextCreateImage(context);
//    UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
//    CGContextRelease(context);
    
    return image;
}

- (WXDisplayBlock)_compositeDisplayBlock
{
    return ^UIImage* (CGRect bounds, BOOL(^isCancelled)(void)) {
        if (isCancelled()) {
            return nil;
        }
        NSMutableArray *displayBlocks = [NSMutableArray array];
        
        CGContextRef context = [self beginDrawContext:bounds];
        
        UIGraphicsPushContext(context);
        
        [self _collectCompositingDisplayBlocks:displayBlocks context:context isCancelled:isCancelled];
        
        for (dispatch_block_t block in displayBlocks) {
            if (isCancelled()) {
                [self endDrawContext:context];
                return nil;
            }
            block();
        }
        
        UIGraphicsPopContext();
        
        UIImage *image = [self endDrawContext:context];
        return image;
    };
}

- (void)_collectCompositingDisplayBlocks:(NSMutableArray *)displayBlocks context:(CGContextRef)context isCancelled:(BOOL(^)(void))isCancelled
{
    // TODO: compositingChild has no chance to applyPropertiesToView, need update here?
    UIColor *backgroundColor = self.styleBackgroundColor;
    BOOL clipsToBounds = _clipToBounds;
    CGRect frame = self.calculatedFrame;
    CGRect bounds = CGRectMake(0, 0, frame.size.width, frame.size.height);
    
    if (_useCompositing) {
        frame.origin = CGPointMake(0, 0);
    }
    
    BOOL needsDrawRect = [self needsDrawRect];
    
    BOOL shouldDisplay = needsDrawRect && (backgroundColor || CGPointEqualToPoint(CGPointZero, frame.origin) == NO || clipsToBounds);
    
    if (shouldDisplay) {
        dispatch_block_t displayBlockToPush = ^{
            CGContextSaveGState(context);
            CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);
            
            if (isCancelled && isCancelled()) {
                return ;
            }
            
            if (_isCompositingChild && clipsToBounds) {
                [[UIBezierPath bezierPathWithRect:bounds] addClip];
            }
            
            UIImage *image = [self drawRect:bounds];
            if (image) {
                [image drawInRect:bounds];
            }
        };
        [displayBlocks addObject:[displayBlockToPush copy]];
    }
    
    for (WXComponent *component in self.subcomponents) {
        if (!isCancelled()) {
            [component _collectCompositingDisplayBlocks:displayBlocks context:context isCancelled:isCancelled];
        }
    }
    
    if (shouldDisplay) {
        dispatch_block_t blockToPop = ^{
            CGContextRestoreGState(context);
        };
        [displayBlocks addObject:[blockToPop copy]];
    }
}

#pragma mark Border Drawing

- (void)_drawBorderWithContext:(CGContextRef)context size:(CGSize)size
{
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:rect topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius];
    // here is computed radii, do not use original style
    WXRadii *radii = borderRect.radii;
    CGFloat topLeft = radii.topLeft, topRight = radii.topRight, bottomLeft = radii.bottomLeft, bottomRight = radii.bottomRight;
    
    CGContextSetAlpha(context, _opacity);
    // fill background color
    if (self.styleBackgroundColor && CGColorGetAlpha(self.styleBackgroundColor.CGColor) > 0) {
        CGContextSetFillColorWithColor(context, self.styleBackgroundColor.CGColor);
        UIBezierPath *bezierPath = [UIBezierPath wx_bezierPathWithRoundedRect:rect topLeft:topLeft topRight:topRight bottomLeft:bottomLeft bottomRight:bottomRight];
        [bezierPath fill];
        WXPerformBlockOnMainThread(^{
            _view.backgroundColor = UIColor.clearColor;
        });
    }
    // Top
    if (_borderTopWidth > 0) {
        if(_borderTopStyle == WXBorderStyleDashed || _borderTopStyle == WXBorderStyleDotted){
            CGFloat lengths[2];
            lengths[0] = lengths[1] = (_borderTopStyle == WXBorderStyleDashed ? 3 : 1) * _borderTopWidth;
            CGContextSetLineDash(context, 0, lengths, sizeof(lengths) / sizeof(*lengths));
        } else{
            CGContextSetLineDash(context, 0, 0, 0);
        }
        CGContextSetLineWidth(context, _borderTopWidth);
        CGContextSetStrokeColorWithColor(context, _borderTopColor.CGColor);
        CGContextAddArc(context, size.width-topRight, topRight, topRight-_borderTopWidth/2, -M_PI_4+(_borderRightWidth>0?0:M_PI_4), -M_PI_2, 1);
        CGContextMoveToPoint(context, size.width-topRight, _borderTopWidth/2);
        CGContextAddLineToPoint(context, topLeft, _borderTopWidth/2);
        CGContextAddArc(context, topLeft, topLeft, topLeft-_borderTopWidth/2, -M_PI_2, -M_PI_2-M_PI_4-(_borderLeftWidth>0?0:M_PI_4), 1);
        CGContextStrokePath(context);
        _lastBorderRecords |= WXComponentBorderRecordTop;
    } else {
        _lastBorderRecords &= ~(WXComponentBorderRecordTop);
    }
    
    // Left
    if (_borderLeftWidth > 0) {
        if(_borderLeftStyle == WXBorderStyleDashed || _borderLeftStyle == WXBorderStyleDotted){
            CGFloat lengths[2];
            lengths[0] = lengths[1] = (_borderLeftStyle == WXBorderStyleDashed ? 3 : 1) * _borderLeftWidth;
            CGContextSetLineDash(context, 0, lengths, sizeof(lengths) / sizeof(*lengths));
        } else{
            CGContextSetLineDash(context, 0, 0, 0);
        }
        CGContextSetLineWidth(context, _borderLeftWidth);
        CGContextSetStrokeColorWithColor(context, _borderLeftColor.CGColor);
        CGContextAddArc(context, topLeft, topLeft, topLeft-_borderLeftWidth/2, -M_PI, -M_PI_2-M_PI_4+(_borderTopWidth > 0?0:M_PI_4), 0);
        CGContextMoveToPoint(context, _borderLeftWidth/2, topLeft);
        CGContextAddLineToPoint(context, _borderLeftWidth/2, size.height-bottomLeft);
        CGContextAddArc(context, bottomLeft, size.height-bottomLeft, bottomLeft-_borderLeftWidth/2, M_PI, M_PI-M_PI_4-(_borderBottomWidth>0?0:M_PI_4), 1);
        CGContextStrokePath(context);
        _lastBorderRecords |= WXComponentBorderRecordLeft;
    } else {
        _lastBorderRecords &= ~WXComponentBorderRecordLeft;
    }
    
    // Bottom
    if (_borderBottomWidth > 0) {
        if(_borderBottomStyle == WXBorderStyleDashed || _borderBottomStyle == WXBorderStyleDotted){
            CGFloat lengths[2];
            lengths[0] = lengths[1] = (_borderBottomStyle == WXBorderStyleDashed ? 3 : 1) * _borderBottomWidth;
            CGContextSetLineDash(context, 0, lengths, sizeof(lengths) / sizeof(*lengths));
        } else{
            CGContextSetLineDash(context, 0, 0, 0);
        }
        CGContextSetLineWidth(context, _borderBottomWidth);
        CGContextSetStrokeColorWithColor(context, _borderBottomColor.CGColor);
        CGContextAddArc(context, bottomLeft, size.height-bottomLeft, bottomLeft-_borderBottomWidth/2, M_PI-M_PI_4+(_borderLeftWidth>0?0:M_PI_4), M_PI_2, 1);
        CGContextMoveToPoint(context, bottomLeft, size.height-_borderBottomWidth/2);
        CGContextAddLineToPoint(context, size.width-bottomRight, size.height-_borderBottomWidth/2);
        CGContextAddArc(context, size.width-bottomRight, size.height-bottomRight, bottomRight-_borderBottomWidth/2, M_PI_2, M_PI_4-(_borderRightWidth > 0?0:M_PI_4), 1);
        CGContextStrokePath(context);
        _lastBorderRecords |= WXComponentBorderRecordBottom;
    } else {
        _lastBorderRecords &= ~WXComponentBorderRecordBottom;
    }
    
    // Right
    if (_borderRightWidth > 0) {
        if(_borderRightStyle == WXBorderStyleDashed || _borderRightStyle == WXBorderStyleDotted){
            CGFloat lengths[2];
            lengths[0] = lengths[1] = (_borderRightStyle == WXBorderStyleDashed ? 3 : 1) * _borderRightWidth;
            CGContextSetLineDash(context, 0, lengths, sizeof(lengths) / sizeof(*lengths));
        } else{
            CGContextSetLineDash(context, 0, 0, 0);
        }
        CGContextSetLineWidth(context, _borderRightWidth);
        CGContextSetStrokeColorWithColor(context, _borderRightColor.CGColor);
        CGContextAddArc(context, size.width-bottomRight, size.height-bottomRight, bottomRight-_borderRightWidth/2, M_PI_4+(_borderBottomWidth>0?0:M_PI_4), 0, 1);
        CGContextMoveToPoint(context, size.width-_borderRightWidth/2, size.height-bottomRight);
        CGContextAddLineToPoint(context, size.width-_borderRightWidth/2, topRight);
        CGContextAddArc(context, size.width-topRight, topRight, topRight-_borderRightWidth/2, 0, -M_PI_4-(_borderTopWidth > 0?0:M_PI_4), 1);
        CGContextStrokePath(context);
        _lastBorderRecords |= WXComponentBorderRecordRight;
    } else {
        _lastBorderRecords &= ~WXComponentBorderRecordRight;
    }

    CGContextStrokePath(context);
    
    //clipRadius is beta feature
    //TO DO: remove _clipRadius property
    if (_clipToBounds && _clipRadius) {
        BOOL radiusEqual = _borderTopLeftRadius == _borderTopRightRadius && _borderTopRightRadius == _borderBottomRightRadius && _borderBottomRightRadius == _borderBottomLeftRadius;
        if (!radiusEqual) {
            self.layer.mask = [self drawBorderRadiusMaskLayer:rect];
        }
    }
}

- (BOOL)_needsDrawBorder
{
    if (_isCompositingChild) {
        return YES;
    }
    
    if (![_layer isKindOfClass:[WXLayer class]]) {
        // Only support WXLayer
        return NO;
    }
    // Set border property for most of components because border drawing consumes a lot of memory (v0.6.0)
//    if (_async) {
//        // Async layer always draw border
//        return YES;
//    }
    if (!(_borderLeftStyle == _borderTopStyle &&
          _borderTopStyle == _borderRightStyle &&
          _borderRightStyle == _borderBottomStyle &&
          _borderBottomStyle == WXBorderStyleSolid)
        ) {
        // Native border property doesn't support dashed or dotted border
        return YES;
    }
    
    // user native border property only when border width & color & radius are equal;
    BOOL widthEqual = _borderTopWidth == _borderRightWidth && _borderRightWidth == _borderBottomWidth && _borderBottomWidth == _borderLeftWidth;
    if (!widthEqual) {
        return YES;
    }
    BOOL radiusEqual = _borderTopLeftRadius == _borderTopRightRadius && _borderTopRightRadius == _borderBottomRightRadius && _borderBottomRightRadius == _borderBottomLeftRadius;
    if (!radiusEqual) {
        return YES;
    }
    BOOL colorEqual = [_borderTopColor isEqual:_borderRightColor] && [_borderRightColor isEqual:_borderBottomColor] && [_borderBottomColor isEqual:_borderLeftColor];
    if (!colorEqual) {
        return YES;
    }
    
    return NO;
}

- (void)_handleBorders:(NSDictionary *)styles isUpdating:(BOOL)updating
{
    if (!updating) {
        // init with default value
        _borderTopStyle = _borderRightStyle = _borderBottomStyle = _borderLeftStyle = WXBorderStyleSolid;
        _borderTopColor = _borderLeftColor = _borderRightColor = _borderBottomColor = [UIColor blackColor];
        _borderTopWidth = _borderLeftWidth = _borderRightWidth = _borderBottomWidth = 0;
        _borderTopLeftRadius = _borderTopRightRadius = _borderBottomLeftRadius = _borderBottomRightRadius = 0;
    }
    
    BOOL previousNeedsDrawBorder = YES;
    if (updating) {
        previousNeedsDrawBorder = [self _needsDrawBorder];
    }
    
#define WX_CHECK_BORDER_PROP(prop, direction1, direction2, direction3, direction4, type)\
do {\
    BOOL needsDisplay = NO; \
    NSString *styleProp= WX_NSSTRING(WX_CONCAT(border, prop));\
    if (styles[styleProp]) {\
        _border##direction1##prop = _border##direction2##prop = _border##direction3##prop = _border##direction4##prop = [WXConvert type:styles[styleProp]];\
        needsDisplay = YES;\
    }\
    NSString *styleDirection1Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction1, prop));\
    if (styles[styleDirection1Prop]) {\
        _border##direction1##prop = [WXConvert type:styles[styleDirection1Prop]];\
        needsDisplay = YES;\
    }\
    NSString *styleDirection2Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction2, prop));\
    if (styles[styleDirection2Prop]) {\
        _border##direction2##prop = [WXConvert type:styles[styleDirection2Prop]];\
        needsDisplay = YES;\
    }\
    NSString *styleDirection3Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction3, prop));\
    if (styles[styleDirection3Prop]) {\
        _border##direction3##prop = [WXConvert type:styles[styleDirection3Prop]];\
        needsDisplay = YES;\
    }\
    NSString *styleDirection4Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction4, prop));\
    if (styles[styleDirection4Prop]) {\
        _border##direction4##prop = [WXConvert type:styles[styleDirection4Prop]];\
        needsDisplay = YES;\
    }\
    if (needsDisplay && updating) {\
        [self setNeedsDisplay];\
    }\
} while (0);
    
// TODO: refactor this hopefully
#define WX_CHECK_BORDER_PROP_PIXEL(prop, direction1, direction2, direction3, direction4)\
do {\
    BOOL needsDisplay = NO; \
    NSString *styleProp= WX_NSSTRING(WX_CONCAT(border, prop));\
    if (styles[styleProp]) {\
        _border##direction1##prop = _border##direction2##prop = _border##direction3##prop = _border##direction4##prop = [WXConvert WXPixelType:styles[styleProp] scaleFactor:self.weexInstance.pixelScaleFactor];\
    needsDisplay = YES;\
    }\
    NSString *styleDirection1Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction1, prop));\
    if (styles[styleDirection1Prop]) {\
        _border##direction1##prop = [WXConvert WXPixelType:styles[styleDirection1Prop] scaleFactor:self.weexInstance.pixelScaleFactor];\
        needsDisplay = YES;\
    }\
    NSString *styleDirection2Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction2, prop));\
    if (styles[styleDirection2Prop]) {\
        _border##direction2##prop = [WXConvert WXPixelType:styles[styleDirection2Prop] scaleFactor:self.weexInstance.pixelScaleFactor];\
        needsDisplay = YES;\
    }\
    NSString *styleDirection3Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction3, prop));\
    if (styles[styleDirection3Prop]) {\
        _border##direction3##prop = [WXConvert WXPixelType:styles[styleDirection3Prop] scaleFactor:self.weexInstance.pixelScaleFactor];\
        needsDisplay = YES;\
    }\
    NSString *styleDirection4Prop = WX_NSSTRING(WX_CONCAT_TRIPLE(border, direction4, prop));\
    if (styles[styleDirection4Prop]) {\
        _border##direction4##prop = [WXConvert WXPixelType:styles[styleDirection4Prop] scaleFactor:self.weexInstance.pixelScaleFactor];\
        needsDisplay = YES;\
    }\
    if (needsDisplay && updating) {\
        [self setNeedsDisplay];\
    }\
} while (0);
    
    
    WX_CHECK_BORDER_PROP(Style, Top, Left, Bottom, Right, WXBorderStyle)
    WX_CHECK_BORDER_PROP(Color, Top, Left, Bottom, Right, UIColor)
    WX_CHECK_BORDER_PROP_PIXEL(Width, Top, Left, Bottom, Right)
    WX_CHECK_BORDER_PROP_PIXEL(Radius, TopLeft, TopRight, BottomLeft, BottomRight)

    if (updating) {
        WX_CHECK_COMPONENT_TYPE(self.componentType)
        BOOL nowNeedsDrawBorder = [self _needsDrawBorder];
        if (nowNeedsDrawBorder && !previousNeedsDrawBorder) {
            _layer.cornerRadius = 0;
            _layer.borderWidth = 0;
            _layer.backgroundColor = NULL;
        } else if (!nowNeedsDrawBorder) {
            [self _resetNativeBorderRadius];
            _layer.borderWidth = _borderTopWidth;
            _layer.borderColor = _borderTopColor.CGColor;
            if ((_transition.transitionOptions & WXTransitionOptionsBackgroundColor) != WXTransitionOptionsBackgroundColor ) {
                _layer.backgroundColor = self.styleBackgroundColor.CGColor;
            }
        }
    }
}

- (BOOL)_bitmapOpaqueWithSize:(CGSize)size
{
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:rect topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius];
    WXRadii *radii = borderRect.radii;
    BOOL hasBorderRadius = [radii hasBorderRadius];
    return (!hasBorderRadius) && _opacity == 1.0 && CGColorGetAlpha(self.styleBackgroundColor.CGColor) == 1.0 && [self _needsDrawBorder];
}

- (CAShapeLayer *)drawBorderRadiusMaskLayer:(CGRect)rect
{
    if ([self hasBorderRadiusMaskLayer]) {
        WXRoundedRect *borderRect = [[WXRoundedRect alloc] initWithRect:rect topLeft:_borderTopLeftRadius topRight:_borderTopRightRadius bottomLeft:_borderBottomLeftRadius bottomRight:_borderBottomRightRadius];
        WXRadii *radii = borderRect.radii;
        UIBezierPath *bezierPath = [UIBezierPath wx_bezierPathWithRoundedRect:rect topLeft:radii.topLeft topRight:radii.topRight bottomLeft:radii.bottomLeft bottomRight:radii.bottomRight];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.path = bezierPath.CGPath;
        return maskLayer;
    }
    return nil;
}

- (BOOL)hasBorderRadiusMaskLayer
{
    return _borderTopLeftRadius > 0.001 || _borderTopRightRadius > 0.001 || _borderBottomLeftRadius > 0.001 || _borderBottomLeftRadius > 0.001;
}

#pragma mark - Deprecated

- (WXDisplayBlock)displayBlock
{
    return [self _displayBlock];
}

- (WXDisplayCompletionBlock)displayCompletionBlock
{
    return [self _displayCompletionBlock];
}

@end
