//
//  TCommonCell.m
//  TXIMSDK_TUIKit_iOS
//
//  Created by annidyfeng on 2019/5/6.
//  Copyright © 2023 Tencent. All rights reserved.
//

#import "TUICommonModel.h"
#import <objc/runtime.h>
#import "NSString+TUIUtil.h"
#import "TUIDarkModel.h"
#import "TUIGlobalization.h"
#import "TUIThemeManager.h"
#import "TUITool.h"
#import "UIView+TUILayout.h"

/////////////////////////////////////////////////////////////////////////////////
//
//                            TUIUserFullInfo
//
/////////////////////////////////////////////////////////////////////////////////
@implementation V2TIMUserFullInfo (TUIUserFullInfo)

- (NSString *)showName {
    if ([NSString isEmpty:self.nickName]) return self.userID;
    return self.nickName;
}

- (NSString *)showGender {
    if (self.gender == V2TIM_GENDER_MALE) return TUIKitLocalizableString(Male);
    if (self.gender == V2TIM_GENDER_FEMALE) return TUIKitLocalizableString(Female);
    return TUIKitLocalizableString(Unsetted);
}

- (NSString *)showSignature {
    if (self.selfSignature == nil) return TUIKitLocalizableString(TUIKitNoSelfSignature);
    return [NSString stringWithFormat:TUIKitLocalizableString(TUIKitSelfSignatureFormat), self.selfSignature];
}

- (NSString *)showAllowType {
    if (self.allowType == V2TIM_FRIEND_ALLOW_ANY) {
        return TUIKitLocalizableString(TUIKitAllowTypeAcceptOne);
    }
    if (self.allowType == V2TIM_FRIEND_NEED_CONFIRM) {
        return TUIKitLocalizableString(TUIKitAllowTypeNeedConfirm);
    }
    if (self.allowType == V2TIM_FRIEND_DENY_ANY) {
        return TUIKitLocalizableString(TUIKitAllowTypeDeclineAll);
    }
    return nil;
}
@end

/////////////////////////////////////////////////////////////////////////////////
//
//                             TUIUserModel
//
/////////////////////////////////////////////////////////////////////////////////
@implementation TUIUserModel

- (id)copyWithZone:(NSZone *)zone {
    TUIUserModel *model = [[TUIUserModel alloc] init];
    model.userId = self.userId;
    model.name = self.name;
    model.avatar = self.avatar;
    return model;
}

@end

/////////////////////////////////////////////////////////////////////////////////
//
//                          TUIScrollView
//
/////////////////////////////////////////////////////////////////////////////////

static void *gScrollViewBoundsChangeNotificationContext = &gScrollViewBoundsChangeNotificationContext;

@interface TUIScrollView ()

@property(nonatomic, readonly) CGFloat imageAspectRatio;
@property(nonatomic) CGRect initialImageFrame;
@property(strong, nonatomic, readonly) UITapGestureRecognizer *tap;

@end

@implementation TUIScrollView

@synthesize tap = _tap;

#pragma mark - Tap to Zoom

- (UITapGestureRecognizer *)tap {
    if (_tap == nil) {
        _tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToZoom:)];
        _tap.numberOfTapsRequired = 2;
    }
    return _tap;
}

- (void)tapToZoom:(UIGestureRecognizer *)gestureRecognizer {
    if (self.zoomScale > self.minimumZoomScale) {
        [self setZoomScale:self.minimumZoomScale animated:YES];
    } else {
        CGPoint tapLocation = [gestureRecognizer locationInView:self.imageView];
        CGFloat zoomRectWidth = self.imageView.frame.size.width / self.maximumZoomScale;
        CGFloat zoomRectHeight = self.imageView.frame.size.height / self.maximumZoomScale;
        CGFloat zoomRectX = tapLocation.x - zoomRectWidth * 0.5;
        CGFloat zoomRectY = tapLocation.y - zoomRectHeight * 0.5;
        CGRect zoomRect = CGRectMake(zoomRectX, zoomRectY, zoomRectWidth, zoomRectHeight);
        [self zoomToRect:zoomRect animated:YES];
    }
}

#pragma mark - Private Methods

- (void)configure {
    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;
    [self startObservingBoundsChange];
}

- (void)setImageView:(UIImageView *)imageView {
    if (_imageView.superview == self) {
        [_imageView removeGestureRecognizer:self.tap];
        [_imageView removeFromSuperview];
    }
    if (imageView) {
        _imageView = imageView;
        _initialImageFrame = CGRectNull;
        _imageView.userInteractionEnabled = YES;
        [_imageView addGestureRecognizer:self.tap];
        [self addSubview:imageView];
    }
}

- (CGFloat)imageAspectRatio {
    if (self.imageView.image) {
        return self.imageView.image.size.width / self.imageView.image.size.height;
    }
    return 1;
}

- (CGSize)rectSizeForAspectRatio:(CGFloat)ratio thatFitsSize:(CGSize)size {
    CGFloat containerWidth = size.width;
    CGFloat containerHeight = size.height;
    CGFloat resultWidth = 0;
    CGFloat resultHeight = 0;

    if ((ratio <= 0) || (containerHeight <= 0)) {
        return size;
    }

    if (containerWidth / containerHeight >= ratio) {
        resultHeight = containerHeight;
        resultWidth = containerHeight * ratio;
    } else {
        resultWidth = containerWidth;
        resultHeight = containerWidth / ratio;
    }

    return CGSizeMake(resultWidth, resultHeight);
}

- (void)scaleImageForScrollViewTransitionFromBounds:(CGRect)oldBounds toBounds:(CGRect)newBounds {
    CGPoint oldContentOffset = CGPointMake(oldBounds.origin.x, oldBounds.origin.y);
    CGSize oldSize = oldBounds.size;
    CGSize newSize = newBounds.size;

    CGSize containedImageSizeOld = [self rectSizeForAspectRatio:self.imageAspectRatio thatFitsSize:oldSize];

    CGSize containedImageSizeNew = [self rectSizeForAspectRatio:self.imageAspectRatio thatFitsSize:newSize];

    if (containedImageSizeOld.height <= 0) {
        containedImageSizeOld = containedImageSizeNew;
    }

    CGFloat orientationRatio = (containedImageSizeNew.height / containedImageSizeOld.height);

    CGAffineTransform t = CGAffineTransformMakeScale(orientationRatio, orientationRatio);

    self.imageView.frame = CGRectApplyAffineTransform(self.imageView.frame, t);

    self.contentSize = self.imageView.frame.size;

    CGFloat xOffset = (oldContentOffset.x + oldSize.width * 0.5) * orientationRatio - newSize.width * 0.5;
    CGFloat yOffset = (oldContentOffset.y + oldSize.height * 0.5) * orientationRatio - newSize.height * 0.5;

    xOffset -= MAX(xOffset + newSize.width - self.contentSize.width, 0);
    yOffset -= MAX(yOffset + newSize.height - self.contentSize.height, 0);
    xOffset += MAX(-xOffset, 0);
    yOffset += MAX(-yOffset, 0);

    self.contentOffset = CGPointMake(xOffset, yOffset);
}

- (void)setupInitialImageFrame {
    if (self.imageView.image && CGRectEqualToRect(self.initialImageFrame, CGRectNull)) {
        CGSize imageViewSize = [self rectSizeForAspectRatio:self.imageAspectRatio thatFitsSize:self.bounds.size];
        self.initialImageFrame = CGRectMake(0, 0, imageViewSize.width, imageViewSize.height);
        self.imageView.frame = self.initialImageFrame;
        self.contentSize = self.initialImageFrame.size;
    }
}

#pragma mark - KVO

- (void)startObservingBoundsChange {
    [self addObserver:self
           forKeyPath:@"bounds"
              options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
              context:gScrollViewBoundsChangeNotificationContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
    if (context == gScrollViewBoundsChangeNotificationContext) {
        CGRect oldRect = ((NSValue *)change[NSKeyValueChangeOldKey]).CGRectValue;
        CGRect newRect = ((NSValue *)change[NSKeyValueChangeNewKey]).CGRectValue;
        if (!CGSizeEqualToSize(oldRect.size, newRect.size)) {
            [self scaleImageForScrollViewTransitionFromBounds:oldRect toBounds:newRect];
        }
    }
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"bounds"];
}

#pragma mark - UIScrollView

- (void)setContentOffset:(CGPoint)contentOffset {
    const CGSize contentSize = self.contentSize;
    const CGSize scrollViewSize = self.bounds.size;

    if (contentSize.width < scrollViewSize.width) {
        contentOffset.x = -(scrollViewSize.width - contentSize.width) * 0.5;
    }

    if (contentSize.height < scrollViewSize.height) {
        contentOffset.y = -(scrollViewSize.height - contentSize.height) * 0.5;
    }

    [super setContentOffset:contentOffset];
}

#pragma mark - UIView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self configure];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self configure];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    [self setupInitialImageFrame];
}

@end

/////////////////////////////////////////////////////////////////////////////////
//
//                          TUIGroupAvatar
//
/////////////////////////////////////////////////////////////////////////////////
#define groupAvatarWidth (48 * [[UIScreen mainScreen] scale])
@implementation TUIGroupAvatar

+ (void)createGroupAvatar:(NSArray *)group finished:(void (^)(UIImage *groupAvatar))finished {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
      NSInteger avatarCount = group.count > 9 ? 9 : group.count;
      CGFloat width = groupAvatarWidth / 3 * 0.90;
      CGFloat space3 = (groupAvatarWidth - width * 3) / 4;
      CGFloat space2 = (groupAvatarWidth - width * 2 + space3) / 2;
      CGFloat space1 = (groupAvatarWidth - width) / 2;
      __block CGFloat y = avatarCount > 6 ? space3 : (avatarCount > 3 ? space2 : space1);
      __block CGFloat x = avatarCount % 3 == 0 ? space3 : (avatarCount % 3 == 2 ? space2 : space1);
      width = avatarCount > 4 ? width : (avatarCount > 1 ? (groupAvatarWidth - 3 * space3) / 2 : groupAvatarWidth);

      if (avatarCount == 1) {
          x = 0;
          y = 0;
      }
      if (avatarCount == 2) {
          x = space3;
      } else if (avatarCount == 3) {
          x = (groupAvatarWidth - width) / 2;
          y = space3;
      } else if (avatarCount == 4) {
          x = space3;
          y = space3;
      }

      dispatch_async(dispatch_get_main_queue(), ^{
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, groupAvatarWidth, groupAvatarWidth)];
        [view setBackgroundColor:[UIColor colorWithWhite:0.8 alpha:0.6]];
        view.layer.cornerRadius = 6;
        __block NSInteger count = 0;
        for (NSInteger i = avatarCount - 1; i >= 0; i--) {
            NSString *avatarUrl = [group objectAtIndex:i];

            UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(x, y, width, width)];
            [view addSubview:imageView];
            [imageView sd_setImageWithURL:[NSURL URLWithString:avatarUrl]
                         placeholderImage:DefaultAvatarImage
                                completed:^(UIImage *_Nullable image, NSError *_Nullable error, SDImageCacheType cacheType, NSURL *_Nullable imageURL) {
                                  count++;
                                  if (count == avatarCount) {
                                      UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 2.0);
                                      [view.layer renderInContext:UIGraphicsGetCurrentContext()];
                                      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
                                      UIGraphicsEndPDFContext();
                                      CGImageRef imageRef = image.CGImage;
                                      CGImageRef imageRefRect =
                                          CGImageCreateWithImageInRect(imageRef, CGRectMake(0, 0, view.frame.size.width * 2, view.frame.size.height * 2));
                                      UIImage *ansImage = [[UIImage alloc] initWithCGImage:imageRefRect];
                                      CGImageRelease(imageRefRect);
                                      dispatch_async(dispatch_get_main_queue(), ^{
                                        if (finished) {
                                            finished(ansImage);
                                        }
                                      });
                                  }
                                }];

            if (avatarCount == 3) {
                if (i == 2) {
                    y = width + space3 * 2;
                    x = space3;
                } else {
                    x += width + space3;
                }
            } else if (avatarCount == 4) {
                if (i % 2 == 0) {
                    y += width + space3;
                    x = space3;
                } else {
                    x += width + space3;
                }
            } else {
                if (i % 3 == 0) {
                    y += (width + space3);
                    x = space3;
                } else {
                    x += (width + space3);
                }
            }
        }
      });
    });
}

+ (void)fetchGroupAvatars:(NSString *)groupID placeholder:(UIImage *)placeholder callback:(void (^)(BOOL success, UIImage *image, NSString *groupID))callback {
    @tui_weakify(self);
    [[V2TIMManager sharedInstance] getGroupMemberList:groupID
        filter:V2TIM_GROUP_MEMBER_FILTER_ALL
        nextSeq:0
        succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
          @tui_strongify(self);
          int i = 0;
          NSMutableArray *groupMemberAvatars = [NSMutableArray arrayWithCapacity:1];
          for (V2TIMGroupMemberFullInfo *member in memberList) {
              if (member.faceURL.length > 0) {
                  [groupMemberAvatars addObject:member.faceURL];
              } else {
                  [groupMemberAvatars addObject:@"http://placeholder"];
              }
              if (++i == 9) {
                  break;
              }
          }

          if (i <= 1) {
              [self asyncClearCacheAvatarForGroup:groupID];
              if (callback) {
                  callback(NO, placeholder, groupID);
              }
              return;
          }

          NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", groupID];
          [NSUserDefaults.standardUserDefaults setInteger:groupMemberAvatars.count forKey:key];
          [NSUserDefaults.standardUserDefaults synchronize];

          [TUIGroupAvatar createGroupAvatar:groupMemberAvatars
                                   finished:^(UIImage *groupAvatar) {
                                     @tui_strongify(self);
                                     UIImage *avatar = groupAvatar;
                                     [self cacheGroupAvatar:avatar number:(UInt32)groupMemberAvatars.count groupID:groupID];

                                     if (callback) {
                                         callback(YES, avatar, groupID);
                                     }
                                   }];
        }
        fail:^(int code, NSString *msg) {
          if (callback) {
              callback(NO, placeholder, groupID);
          }
        }];
}

+ (void)cacheGroupAvatar:(UIImage *)avatar number:(UInt32)memberNum groupID:(NSString *)groupID {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
      if (groupID == nil || groupID.length == 0) {
          return;
      }
      NSString *tempPath = NSTemporaryDirectory();
      NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%d.png", tempPath, groupID, memberNum];
      NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

      // check to delete old file
      NSNumber *oldValue = [defaults objectForKey:groupID];
      if (oldValue != nil) {
          UInt32 oldMemberNum = [oldValue unsignedIntValue];
          NSString *oldFilePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%d.png", tempPath, groupID, oldMemberNum];
          NSFileManager *fileManager = [NSFileManager defaultManager];
          [fileManager removeItemAtPath:oldFilePath error:nil];
      }

      // Save image.
      BOOL success = [UIImagePNGRepresentation(avatar) writeToFile:filePath atomically:YES];
      if (success) {
          [defaults setObject:@(memberNum) forKey:groupID];
      }
    });
}

+ (void)getCacheGroupAvatar:(NSString *)groupID callback:(void (^)(UIImage * _Nullable image, NSString * _Nullable groupID))imageCallBack {
    if (groupID == nil || groupID.length == 0) {
        if (imageCallBack) {
            imageCallBack(nil, groupID);
        }
        return;
    }
    [[V2TIMManager sharedInstance] getGroupsInfo:@[ groupID ]
        succ:^(NSArray<V2TIMGroupInfoResult *> *groupResultList) {
          V2TIMGroupInfoResult *groupInfo = groupResultList.firstObject;
          if (!groupInfo) {
              imageCallBack(nil, groupID);
              return;
          }
          UInt32 memberNum = groupInfo.info.memberCount;
          memberNum = MAX(1, memberNum);
          memberNum = MIN(memberNum, 9);
          ;
          NSString *tempPath = NSTemporaryDirectory();
          NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%u.png", tempPath, groupID, (unsigned int)memberNum];
          NSFileManager *fileManager = [NSFileManager defaultManager];
          UIImage *avatar = nil;
          BOOL success = [fileManager fileExistsAtPath:filePath];

          if (success) {
              avatar = [[UIImage alloc] initWithContentsOfFile:filePath];
              NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", groupID];
              [NSUserDefaults.standardUserDefaults setInteger:memberNum forKey:key];
              [NSUserDefaults.standardUserDefaults synchronize];
          }
          imageCallBack(avatar, groupInfo.info.groupID);
        }
        fail:^(int code, NSString *msg) {
          imageCallBack(nil, groupID);
        }];
}

+ (UIImage *)getCacheAvatarForGroup:(NSString *)groupId number:(UInt32)memberNum {
    memberNum = MAX(1, memberNum);
    memberNum = MIN(memberNum, 9);
    ;
    NSString *tempPath = NSTemporaryDirectory();
    NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%u.png", tempPath, groupId, (unsigned int)memberNum];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    UIImage *avatar = nil;
    BOOL success = [fileManager fileExistsAtPath:filePath];

    if (success) {
        avatar = [[UIImage alloc] initWithContentsOfFile:filePath];
    }
    return avatar;
}

+ (void)asyncClearCacheAvatarForGroup:(NSString *)groupID {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
      NSString *tempPath = NSTemporaryDirectory();
      for (int i = 0; i < 9; i++) {
          NSString *filePath = [NSString stringWithFormat:@"%@groupAvatar_%@_%d.png", tempPath, groupID, (i + 1)];
          if ([NSFileManager.defaultManager fileExistsAtPath:filePath]) {
              [NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
          }
      }
    });
}

@end

/////////////////////////////////////////////////////////////////////////////////
//
//                          TUIImageCache
//
/////////////////////////////////////////////////////////////////////////////////
@interface TUIImageCache ()
@property(nonatomic, strong) NSMutableDictionary *resourceCache;
@property(nonatomic, strong) NSMutableDictionary *faceCache;
@end

@implementation TUIImageCache

+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    static TUIImageCache *instance;
    dispatch_once(&onceToken, ^{
      instance = [[TUIImageCache alloc] init];
      [UIImage d_fixResizableImage];
    });
    return instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _resourceCache = [NSMutableDictionary dictionary];
        _faceCache = [NSMutableDictionary dictionary];
    }
    return self;
}

- (void)addResourceToCache:(NSString *)path {
    __weak typeof(self) ws = self;
    [TUITool asyncDecodeImage:path
                     complete:^(NSString *key, UIImage *image) {
                       __strong __typeof(ws) strongSelf = ws;
                       if (key && image) {
                           [strongSelf.resourceCache setValue:image forKey:key];
                       }
                     }];
}

- (UIImage *)getResourceFromCache:(NSString *)path {
    if (path.length == 0) {
        return nil;
    }
    UIImage *image = [_resourceCache objectForKey:path];
    if (!image) {
        image = [UIImage d_imagePath:path];
    }
    return image;
}

- (void)addFaceToCache:(NSString *)path {
    __weak typeof(self) ws = self;
    [TUITool asyncDecodeImage:path
                     complete:^(NSString *key, UIImage *image) {
                       __strong __typeof(ws) strongSelf = ws;
                       if (key && image) {
                           [strongSelf.faceCache setValue:image forKey:key];
                       }
                     }];
}

- (UIImage *)getFaceFromCache:(NSString *)path {
    if (path.length == 0) {
        return nil;
    }
    UIImage *image = [_faceCache objectForKey:path];
    if (!image) {
        // gif extion
        if ([path tui_containsString:@".gif"]) {
            image = [UIImage sd_imageWithGIFData:[NSData dataWithContentsOfFile:path]];
        }
        else {
            image = [UIImage imageWithContentsOfFile:path];
            if (!image) {
                // gif
                NSString *formatPath = [path stringByAppendingString:@".gif"];
                image = [UIImage sd_imageWithGIFData:[NSData dataWithContentsOfFile:formatPath]];
            }
        }
        if (!image) {
            image = [_faceCache objectForKey:TUIChatFaceImagePath(@"ic_unknown_image")];
        }
    }
    return image;
}
@end
@interface IUCoreView : UIView
@property(nonatomic, strong) UIView *view;
@end

@implementation IUCoreView

- (instancetype)init {
    self = [super init];
    if (self) {
        self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
        [self addSubview:self.view];
    }
    return self;
}
@end

@implementation TUINavigationController

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
    if (self = [super initWithRootViewController:rootViewController]) {
        self.interactivePopGestureRecognizer.delegate = self;
        self.delegate = self;
    }
    return self;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (@available(iOS 13.0, *)) {
        UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
        [appearance configureWithDefaultBackground];
        appearance.shadowColor = nil;
        appearance.backgroundEffect = nil;
        appearance.backgroundColor = self.navigationBackColor;
        self.navigationBar.backgroundColor = self.navigationBackColor;
        self.navigationBar.barTintColor = self.navigationBackColor;
        self.navigationBar.shadowImage = [UIImage new];
        self.navigationBar.standardAppearance = appearance;
        self.navigationBar.scrollEdgeAppearance = appearance;

    } else {
        self.navigationBar.backgroundColor = self.navigationBackColor;
        self.navigationBar.barTintColor = self.navigationBackColor;
        self.navigationBar.shadowImage = [UIImage new];
    }
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.delegate = self;
}

- (void)back {
    if ([self.uiNaviDelegate respondsToSelector:@selector(navigationControllerDidClickLeftButton:)]) {
        [self.uiNaviDelegate navigationControllerDidClickLeftButton:self];
    }
    [self popViewControllerAnimated:YES];
}

- (UIColor *)navigationBackColor {
    if (!_navigationBackColor) {
        _navigationBackColor = [self tintColor];
    }
    return _navigationBackColor;
}

- (UIColor *)tintColor {
    return TUICoreDynamicColor(@"head_bg_gradient_start_color", @"#EBF0F6");
}


- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (self.viewControllers.count != 0) {
        viewController.hidesBottomBarWhenPushed = YES;
        self.tabBarController.tabBar.hidden = YES;

        UIImage *image = self.navigationItemBackArrowImage;
        image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
        UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:@selector(back)];
        viewController.navigationItem.leftBarButtonItems = @[ back ];
        viewController.navigationItem.leftItemsSupplementBackButton = NO;
    }
    [super pushViewController:viewController animated:animated];
}

- (UIImage *)navigationItemBackArrowImage {
    if (!_navigationItemBackArrowImage) {
        _navigationItemBackArrowImage = TUICoreDynamicImage(@"nav_back_img", [UIImage imageNamed:TUICoreImagePath(@"nav_back")]);
    }
    return _navigationItemBackArrowImage;
}

// fix: https://developer.apple.com/forums/thread/660750
- (NSArray<__kindof UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {
    if (@available(iOS 14.0, *)) {
        for (UIViewController *vc in self.viewControllers) {
            vc.hidesBottomBarWhenPushed = NO;
            self.tabBarController.tabBar.hidden = NO;
        }
    }
    return [super popToRootViewControllerAnimated:animated];
}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (navigationController.viewControllers.count == 1) {
        /**
         * If the number of view controllers in the stack is 1, it means only the root controller, clear the currentShowVC, and disable the swipe gesture for
         * the following method
         */
        self.currentShowVC = Nil;
    } else {
        /**
         * Assign the pushed view controller to currentShowVC
         */
        self.currentShowVC = viewController;
    }

    if ([navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        if (self.viewControllers.count == 1) {
            /**
             * Forbid the sliding back of the home page
             */
            navigationController.interactivePopGestureRecognizer.enabled = NO;
        } else {
            navigationController.interactivePopGestureRecognizer.enabled = YES;
        }
    }
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    /**
     * Listen to the event returned by the side slide
     */
    __weak typeof(self) weakSelf = self;
    if (@available(iOS 10.0, *)) {
        [viewController.transitionCoordinator notifyWhenInteractionChangesUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
          __strong typeof(weakSelf) strongSelf = weakSelf;
          [strongSelf handleSideSlideReturnIfNeeded:context];
        }];
    } else {
        [viewController.transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
          __strong typeof(weakSelf) strongSelf = weakSelf;
          [strongSelf handleSideSlideReturnIfNeeded:context];
        }];
    }
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer == self.interactivePopGestureRecognizer) {
        if (self.currentShowVC == self.topViewController) {
            /**
             * If currentShowVC exists, it means that the number of controllers in the stack is greater than 1, allowing the side slide gesture to be activated
             */
            return YES;
        }
        return NO;
    }
    return YES;
}

- (void)handleSideSlideReturnIfNeeded:(id<UIViewControllerTransitionCoordinatorContext>)context {
    if (context.isCancelled) {
        return;
    }
    UIViewController *fromVc = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
    if ([self.uiNaviDelegate respondsToSelector:@selector(navigationControllerDidSideSlideReturn:fromViewController:)]) {
        [self.uiNaviDelegate navigationControllerDidSideSlideReturn:self fromViewController:fromVc];
    }
}

@end

@implementation UIAlertController (TUITheme)

- (void)tuitheme_addAction:(UIAlertAction *)action {
    if (action.style == UIAlertActionStyleDefault || action.style == UIAlertActionStyleCancel) {
        UIColor *tempColor = TUICoreDynamicColor(@"primary_theme_color", @"#147AFF");
        [action setValue:tempColor forKey:@"_titleTextColor"];
    }
    [self addAction:action];
}

@end
static const void *tui_valueCallbackKey = @"tui_valueCallbackKey";
static const void *tui_nonValueCallbackKey = @"tui_nonValueCallbackKey";
static const void *tui_extValueObjKey = @"tui_extValueObjKey";

@implementation NSObject (TUIExtValue)

- (void)setTui_valueCallback:(TUIValueCallbck)tui_valueCallback {
    objc_setAssociatedObject(self, tui_valueCallbackKey, tui_valueCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (TUIValueCallbck)tui_valueCallback {
    return objc_getAssociatedObject(self, tui_valueCallbackKey);
}

- (void)setTui_nonValueCallback:(TUINonValueCallbck)tui_nonValueCallback {
    objc_setAssociatedObject(self, tui_nonValueCallbackKey, tui_nonValueCallback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (TUINonValueCallbck)tui_nonValueCallback {
    return objc_getAssociatedObject(self, tui_nonValueCallbackKey);
}

- (void)setTui_extValueObj:(id)tui_extValueObj {
    objc_setAssociatedObject(self, tui_extValueObjKey, tui_extValueObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)tui_extValueObj {
    return objc_getAssociatedObject(self, tui_extValueObjKey);
}

@end
