BABaseProject/BAMainCode/BABaseData(基类)/BABaseOther/BAFPSLabel.m
//
//  BAFPSLabel.m
//  Pods
//
//  Created by HyanCat on 16/4/4.
//
//

#import "BAFPSLabel.h"
#import <Masonry/Masonry.h>

#define IOS8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)

@interface BAFPSLabel ()
{
    NSUInteger _tickCount;
}
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) CFTimeInterval lastTime;
@property (nonatomic, assign) NSUInteger fps;

@end

@implementation BAFPSLabel

+ (instancetype)showInWindow:(UIWindow *)window
{
    BAFPSLabel *label = [[BAFPSLabel alloc] initWithFrame:CGRectZero];
    label.layer.cornerRadius = 4.f;
    label.layer.masksToBounds = YES;
    label.textAlignment = NSTextAlignmentCenter;
    label.userInteractionEnabled = NO;
    label.font = [UIFont fontWithName:@"Menlo" size:12];
    
    [window addSubview:label];
    
    label.translatesAutoresizingMaskIntoConstraints = NO;
    
    NSLayoutConstraint *leadingLayout =[NSLayoutConstraint constraintWithItem:label
                                                                    attribute:NSLayoutAttributeLeading
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:window
                                                                    attribute:NSLayoutAttributeLeading
                                                                   multiplier:1
                                                                     constant:10.f];
    NSLayoutConstraint *bottomLayout = [NSLayoutConstraint constraintWithItem:label
                                                                    attribute:NSLayoutAttributeBottom
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:window
                                                                    attribute:NSLayoutAttributeBottom
                                                                   multiplier:1
                                                                     constant:-10.f];
    NSLayoutConstraint *widthLayout = [NSLayoutConstraint constraintWithItem:label
                                                                   attribute:NSLayoutAttributeWidth
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:nil
                                                                   attribute:NSLayoutAttributeNotAnAttribute
                                                                  multiplier:0
                                                                    constant:60.f];
    NSLayoutConstraint *heightLayout = [NSLayoutConstraint constraintWithItem:label
                                                                    attribute:NSLayoutAttributeHeight
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:nil
                                                                    attribute:NSLayoutAttributeNotAnAttribute
                                                                   multiplier:0
                                                                     constant:20.f];
    if (IOS8_OR_LATER) {
        [NSLayoutConstraint activateConstraints:@[leadingLayout, bottomLayout, widthLayout, heightLayout]];
    }
    else {
        [window addConstraints:@[leadingLayout, bottomLayout, widthLayout, heightLayout]];
    }
    
    return label;
}

- (void)dealloc
{
    [_displayLink invalidate];
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
        [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        _autoHide = NO;
        
        [self addObserver:self forKeyPath:NSStringFromSelector(@selector(fps)) options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
    }
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if (object == self && [keyPath isEqualToString:NSStringFromSelector(@selector(fps))]) {
        NSUInteger oldFps = [[change valueForKey:NSKeyValueChangeOldKey] unsignedIntegerValue];
        NSUInteger newFps = [[change valueForKey:NSKeyValueChangeNewKey] unsignedIntegerValue];
        if (oldFps != newFps) {
            [self _displayFPS];
        }
    }
}

- (void)tick:(CADisplayLink *)displayLink
{
    CFTimeInterval currentTime = displayLink.timestamp;
    if (_lastTime == 0) {
        // first time.
        _lastTime = currentTime;
        return;
    }
    _tickCount++;
    CFTimeInterval delta = currentTime - _lastTime;
    if (delta < 1) return;
    // get fps
    self.fps = MIN(lrint(_tickCount / delta), 60);
    _tickCount = 0;
    _lastTime = currentTime;
}

- (void)fadeOut
{
    CATransition *fadeTransition = [CATransition animation];
    [self.layer addAnimation:fadeTransition forKey:kCATransition];
    [self setAttributedText:nil];
    self.layer.backgroundColor = nil;
}

- (void)_displayFPS
{
    if (self.attributedText == nil) {
        // fade in
        CATransition *fadeTransition = [CATransition animation];
        [self.layer addAnimation:fadeTransition forKey:kCATransition];
    }
    
    CGFloat hue = self.fps > 24 ? (self.fps - 24) / 120.f : 0;
    self.textColor = [UIColor colorWithHue:hue saturation:1 brightness:0.9 alpha:1];
    self.text = [NSString stringWithFormat:@"%@ FPS", @(self.fps)];
    self.layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7f].CGColor;
    
    if (self.autoHide) {
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(fadeOut) object:nil];
        [self performSelector:@selector(fadeOut) withObject:nil afterDelay:2];
    }
}

@end
