//
//  StringSizeCache.m
//  NaviDemo
//
//  Created by linbiao on 2019/5/9.
//  Copyright © 2019年 Baidu. All rights reserved.
//

#import "StringSizeCache.h"

#define MaxCacheStringCount         (200)

@interface SpecialStringSizeItem : NSObject

@property (assign, nonatomic) CGFloat fontSize;

@property (assign, nonatomic) CGFloat minFontSize;

@property (assign, nonatomic) CGFloat forWidth;

@property (assign, nonatomic) NSLineBreakMode lineBreakMode;

@property (assign, nonatomic) CGSize stringSize;

@property (assign, nonatomic) CGFloat actualFontSize;

@end

@implementation SpecialStringSizeItem

@end


@interface StringSizeCache()

/*
 字典的格式：
 string:@{font.pointsize:size}
 
 */
@property (strong, nonatomic) NSMutableDictionary* stringSizeDict;

/*
 字典的格式：
 string:BNSpecialStringSizeItem
 
 */
@property (strong, nonatomic) NSMutableDictionary* specialStringSizeDict;

@property (assign, nonatomic) NSInteger maxCacheCount;

@end

@implementation StringSizeCache

- (instancetype)init {
    if (self = [super init]) {
        [self initData];
        [self registerNotification];
    }
    return self;
}

- (void)registerNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}

- (void)unRegisterNotification {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)dealloc {
    [self unRegisterNotification];
}

- (void)initData {
    _maxCacheCount = MaxCacheStringCount;
}

- (void)clearCache {
    [self.stringSizeDict removeAllObjects];
    [self.specialStringSizeDict removeAllObjects];
}

- (void)applicationDidReceiveMemoryWarning:(NSNotification*)ns {
    if (![NSThread isMainThread]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self applicationDidReceiveMemoryWarning:ns];
        });
    } else {
        [self clearCache];
    }
}

- (NSMutableDictionary*)stringSizeDict {
    if (!_stringSizeDict) {
        _stringSizeDict = [[NSMutableDictionary alloc] init];
    }
    return _stringSizeDict;
}

- (NSMutableDictionary*)specialStringSizeDict {
    if (!_specialStringSizeDict) {
        _specialStringSizeDict = [[NSMutableDictionary alloc] init];
    }
    return _specialStringSizeDict;
}

- (void)setMaxCacheCount:(NSInteger)maxCount {
    _maxCacheCount = maxCount;
}

- (CGSize)getStringSize:(NSString*)string
                   font:(UIFont*)font {
    if (!string || !font) return CGSizeZero;
    if (font.pointSize == 0) return CGSizeZero;
    NSMutableDictionary* font_size_dict = self.stringSizeDict[string];
    NSValue* sizeValue = font_size_dict[@(font.pointSize)];

    if (font_size_dict && sizeValue) {
        return [sizeValue CGSizeValue];
    } else {
        CGSize size = [string sizeWithAttributes:@{NSFontAttributeName:font}];
        
        if (size.height && size.width) {
            if (self.stringSizeDict.count > self.maxCacheCount) {
                [self.stringSizeDict removeAllObjects];
            }
            
            if (!font_size_dict) {
                font_size_dict = [NSMutableDictionary dictionary];
            }
            font_size_dict[@(font.pointSize)] = [NSValue valueWithCGSize:size];
            
            self.stringSizeDict[string] = font_size_dict;
        }
        return size;
    }
}

- (CGSize)getStringsize:(NSString*)string
                   font:(UIFont *)font
            minFontSize:(CGFloat)minFontSize
         actualFontSize:(CGFloat *)actualFontSize
               forWidth:(CGFloat)width
          lineBreakMode:(NSLineBreakMode)lineBreakMode {
    if (!string) return CGSizeZero;
    SpecialStringSizeItem* sizeItem = self.specialStringSizeDict[string];

    BOOL isTheSame = NO;
    if (sizeItem) {
        isTheSame = (sizeItem.fontSize == font.pointSize && sizeItem.forWidth == width && sizeItem.minFontSize == minFontSize && sizeItem.lineBreakMode == lineBreakMode);
    }
    
    if (!sizeItem || !isTheSame) {
        CGFloat theActualFontSize = 0;
        CGSize size = [string sizeWithFont:font
                               minFontSize:minFontSize
                            actualFontSize:&theActualFontSize
                                  forWidth:width
                             lineBreakMode:lineBreakMode];
        if (theActualFontSize > 0 && size.width > 0 && size.height > 0) {
            if (self.specialStringSizeDict.count > self.maxCacheCount) {
                [self.specialStringSizeDict removeAllObjects];
            }
            sizeItem.actualFontSize = theActualFontSize;
            if (!sizeItem) {
                sizeItem = [[SpecialStringSizeItem alloc] init];
            }
            sizeItem.fontSize = font.pointSize;
            sizeItem.minFontSize = minFontSize;
            sizeItem.forWidth = width;
            sizeItem.lineBreakMode = lineBreakMode;
            sizeItem.stringSize = size;
            sizeItem.actualFontSize = theActualFontSize;
            self.specialStringSizeDict[string] = sizeItem;
        }
        
        if (actualFontSize) {
            *actualFontSize = theActualFontSize;
        }
    } else {
        if (actualFontSize) {
            *actualFontSize = sizeItem.actualFontSize;
        }
    }
    return sizeItem.stringSize;
}

@end
