//
//  TUIThemeManager.m
//  TUICore
//
//  Created by harvy on 2022/1/5.
//  Copyright © 2023 Tencent. All rights reserved.
//

#import "TUIThemeManager.h"
#import "UIColor+TUIHexColor.h"

@interface TUIDarkThemeRootVC : UIViewController

@end
@implementation TUIDarkThemeRootVC
- (BOOL)shouldAutorotate {
    return NO;
}
@end

@interface TUIDarkWindow : UIWindow
@property(nonatomic, readonly, class) TUIDarkWindow *sharedInstance;
@property(nonatomic, strong) UIWindow *previousKeyWindow;
@end
@implementation TUIDarkWindow

+ (void)load {
    [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
}

+ (void)windowDidBecomeActive {
    UIWindow *darkWindow = [self sharedInstance];
    if (@available(iOS 13.0, *)) {
        UIScene *scene = UIApplication.sharedApplication.connectedScenes.anyObject;
        if (scene) {
            darkWindow.windowScene = (UIWindowScene *)scene;
        }
    }
    [darkWindow setRootViewController:[TUIDarkThemeRootVC new]];
    darkWindow.hidden = NO;
    [NSNotificationCenter.defaultCenter removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
}

- (void)becomeKeyWindow {
    _previousKeyWindow = [self appKeyWindow];
    [super becomeKeyWindow];
}

- (void)resignKeyWindow {
    [super resignKeyWindow];
    [_previousKeyWindow makeKeyWindow];
    _previousKeyWindow = nil;
}

- (UIWindow *)appKeyWindow {
    UIWindow *keywindow = UIApplication.sharedApplication.keyWindow;
    if (keywindow == nil) {
        if (@available(iOS 13.0, *)) {
            for (UIWindowScene *scene in UIApplication.sharedApplication.connectedScenes) {
                if (scene.activationState == UISceneActivationStateForegroundActive) {
                    UIWindow *tmpWindow = nil;
                    if (@available(iOS 15.0, *)) {
                        tmpWindow = scene.keyWindow;
                    }
                    if (tmpWindow == nil) {
                        for (UIWindow *window in scene.windows) {
                            if (window.windowLevel == UIWindowLevelNormal && window.hidden == NO &&
                                CGRectEqualToRect(window.bounds, UIScreen.mainScreen.bounds)) {
                                tmpWindow = window;
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    if (keywindow == nil) {
        for (UIWindow *window in UIApplication.sharedApplication.windows) {
            if (window.windowLevel == UIWindowLevelNormal && window.hidden == NO && CGRectEqualToRect(window.bounds, UIScreen.mainScreen.bounds)) {
                keywindow = window;
                break;
            }
        }
    }
    return keywindow;
}

+ (instancetype)sharedInstance {
    static TUIDarkWindow *shareWindow = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      shareWindow = [[self alloc] init];
      shareWindow.frame = UIScreen.mainScreen.bounds;
      shareWindow.userInteractionEnabled = YES;
      shareWindow.windowLevel = UIWindowLevelNormal - 1;
      shareWindow.hidden = YES;
      shareWindow.opaque = NO;
      shareWindow.backgroundColor = [UIColor clearColor];
      shareWindow.layer.backgroundColor = [UIColor clearColor].CGColor;
    });
    return shareWindow;
}

- (void)setOverrideUserInterfaceStyle:(UIUserInterfaceStyle)overrideUserInterfaceStyle {
}
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];
    if (@available(iOS 13.0, *)) {
        if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
            [NSNotificationCenter.defaultCenter postNotificationName:TUIDidApplyingThemeChangedNotfication object:nil];
            if ([TUIThemeManager.shareManager respondsToSelector:@selector(allListenerExcuteonApplyThemeMethod:module:)]) {
                [TUIThemeManager.shareManager performSelector:@selector(allListenerExcuteonApplyThemeMethod:module:) withObject:nil withObject:nil];
            }
        }
    }
}
@end

@implementation TUITheme

+ (UIColor *)dynamicColor:(NSString *)colorKey module:(TUIThemeModule)module defaultColor:(NSString *)hex {
    TUITheme *theme = TUICurrentTheme(module);
    TUITheme *darkTheme = TUIDarkTheme(module);
    if (theme) {
        return [theme dynamicColor:colorKey defaultColor:hex];
    } else {
        if (@available(iOS 13.0, *)) {
            return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull traitCollection) {
              switch (traitCollection.userInterfaceStyle) {
                  case UIUserInterfaceStyleDark:
                      if(darkTheme){
                          return [darkTheme dynamicColor:colorKey defaultColor:hex];
                      }
                  case UIUserInterfaceStyleLight:
                  case UIUserInterfaceStyleUnspecified:
                  default:
                      return [UIColor tui_colorWithHex:hex];
              }
            }];
        } else {
            return [UIColor tui_colorWithHex:hex];
        }
    }
}

- (UIColor *)dynamicColor:(NSString *)colorKey defaultColor:(NSString *)hex {
    UIColor *color = nil;

    NSString *colorHex = [self.manifest objectForKey:colorKey];
    if (colorHex && [colorHex isKindOfClass:NSString.class]) {
        color = [UIColor tui_colorWithHex:colorHex];
    }

    if (color == nil) {
        color = [UIColor tui_colorWithHex:hex];
    }
    return color;
}

+ (UIImage *)dynamicImage:(NSString *)imageKey module:(TUIThemeModule)module defaultImage:(UIImage *)image {
    TUITheme *theme = TUICurrentTheme(module);
    TUITheme *darkTheme = TUIDarkTheme(module);
    if (theme) {
        return [theme dynamicImage:imageKey defaultImage:image];
    } else {
        UIImage *lightImage = image;
        UIImage *darkImage = [darkTheme dynamicImage:imageKey defaultImage:image];
        return [self imageWithImageLight:lightImage dark:darkImage];
    }
}

- (UIImage *)dynamicImage:(NSString *)imageKey defaultImage:(UIImage *)image {
    UIImage *dynamic = nil;

    NSString *imageName = [self.manifest objectForKey:imageKey];
    if ([imageName isKindOfClass:NSString.class]) {
        imageName = [self.resourcePath stringByAppendingPathComponent:imageName];
        dynamic = [UIImage imageWithContentsOfFile:imageName];
    }

    if (dynamic == nil) {
        dynamic = image;
    }

    return dynamic;
}

+ (UIImage *)imageWithImageLight:(UIImage *)lightImage dark:(UIImage *)darkImage {
    if (@available(iOS 13.0, *)) {
        UITraitCollection *const scaleTraitCollection = [UITraitCollection currentTraitCollection];
        UITraitCollection *const darkUnscaledTraitCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
        UITraitCollection *const darkScaledTraitCollection =
            [UITraitCollection traitCollectionWithTraitsFromCollections:@[ scaleTraitCollection, darkUnscaledTraitCollection ]];
        UIImage *image = [lightImage
            imageWithConfiguration:[lightImage.configuration
                                       configurationWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]];
        darkImage = [darkImage
            imageWithConfiguration:[darkImage.configuration
                                       configurationWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]]];
        [image.imageAsset registerImage:darkImage withTraitCollection:darkScaledTraitCollection];
        return image;
    } else {
        return lightImage;
    }
}

@end

@interface TUIThemeManager ()

/**
 * The theme resource path of each module, module: theme path
 */
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSString *> *themeResourcePathCache;

/**
 * The theme currently used by module, module: theme
 */
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, TUITheme *> *currentThemeCache;

/**
 * The dark theme for each module, if any
 */
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, TUITheme *> *darkThemeCache;

@property(nonatomic, strong) NSHashTable *listeners;

- (void)allListenerExcuteonApplyThemeMethod:(TUITheme *)theme module:(TUIThemeModule)module;

@end

@implementation TUIThemeManager
#ifdef TUIThreadSafe
dispatch_queue_t _queue;
dispatch_queue_t _read_write_queue;
#endif
static id gShareInstance;

+ (instancetype)shareManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      gShareInstance = [[self alloc] init];
    });
    return gShareInstance;
}

- (instancetype)init {
    if (self = [super init]) {
#ifdef TUIThreadSafe
        _queue = dispatch_queue_create("theme_manager_queue", DISPATCH_QUEUE_SERIAL);
        _read_write_queue = dispatch_queue_create("read_write_secure_queue", DISPATCH_QUEUE_CONCURRENT);
#endif
        _themeResourcePathCache = [NSMutableDictionary dictionary];
        _currentThemeCache = [NSMutableDictionary dictionary];
        _darkThemeCache = [NSMutableDictionary dictionary];
        _listeners = [NSHashTable weakObjectsHashTable];
    }
    return self;
}

- (void)addListener:(id<TUIThemeManagerListener>)listener {
#ifdef TUIThreadSafe
    dispatch_async(_queue, ^{
#endif
      if (![self.listeners containsObject:listener]) {
          [self.listeners addObject:listener];
      }
#ifdef TUIThreadSafe
    });
#endif
}

- (void)removeListener:(id<TUIThemeManagerListener>)listener {
#ifdef TUIThreadSafe
    dispatch_async(_queue, ^{
#endif
      if ([self.listeners containsObject:listener]) {
          [self.listeners removeObject:listener];
      }
#ifdef TUIThreadSafe
    });
#endif
}

- (void)registerThemeResourcePath:(NSString *)path darkThemeID:(NSString *)darkThemeID forModule:(TUIThemeModule)module {
    if (path.length == 0) {
        return;
    }

#ifdef TUIThreadSafe
    dispatch_async(_queue, ^{
#endif
      [self.themeResourcePathCache setObject:path forKey:@(module)];

      TUITheme *theme = [self loadTheme:darkThemeID module:module];
      if (theme) {
          [self setDarkTheme:theme forModule:module];
      }
#ifdef TUIThreadSafe
    });
#endif
}

- (void)registerThemeResourcePath:(NSString *)path forModule:(TUIThemeModule)module {
    [self registerThemeResourcePath:path darkThemeID:@"dark" forModule:module];
}

- (TUITheme *)currentThemeForModule:(TUIThemeModule)module {
    __block TUITheme *theme = nil;
#ifdef TUIThreadSafe
    dispatch_sync(_read_write_queue, ^{
#endif
      theme = [self.currentThemeCache objectForKey:@(module)];
#ifdef TUIThreadSafe
    });
#endif
    return theme;
}

- (void)setCurrentTheme:(TUITheme *)theme forModule:(TUIThemeModule)module {
#ifdef TUIThreadSafe
    dispatch_barrier_async(_read_write_queue, ^{
#endif
      if ([self.currentThemeCache.allKeys containsObject:@(module)]) {
          [self.currentThemeCache removeObjectForKey:@(module)];
      }
      if (theme) {
          [self.currentThemeCache setObject:theme forKey:@(module)];
      }
#ifdef TUIThreadSafe
    });
#endif
}

- (TUITheme *)darkThemeForModule:(TUIThemeModule)module {
    __block TUITheme *theme = nil;
#ifdef TUIThreadSafe
    dispatch_sync(_read_write_queue, ^{
#endif
      if ([self.darkThemeCache.allKeys containsObject:@(module)]) {
          theme = [self.darkThemeCache objectForKey:@(module)];
      }
#ifdef TUIThreadSafe
    });
#endif
    return theme;
}

- (void)setDarkTheme:(TUITheme *)theme forModule:(TUIThemeModule)module {
#ifdef TUIThreadSafe
    dispatch_barrier_async(_read_write_queue, ^{
#endif
      if ([self.darkThemeCache.allKeys containsObject:@(module)]) {
          [self.darkThemeCache removeObjectForKey:@(module)];
      }
      if (theme) {
          [self.darkThemeCache setObject:theme forKey:@(module)];
      }
#ifdef TUIThreadSafe
    });
#endif
}

- (void)applyTheme:(NSString *)themeID forModule:(TUIThemeModule)module {
    if (themeID.length == 0) {
        NSLog(@"[theme][applyTheme] invalid themeID, module:%zd", module);
        return;
    }
#ifdef TUIThreadSafe
    dispatch_async(_queue, ^{
#endif
      BOOL isAll = NO;
      NSMutableArray *allKeys = [NSMutableArray arrayWithArray:self.themeResourcePathCache.allKeys];
      if (module == TUIThemeModuleAll || ((module & TUIThemeModuleAll) == TUIThemeModuleAll)) {
          isAll = YES;
      }

      if (isAll) {
          for (NSNumber *moduleObject in allKeys) {
              TUIThemeModule tmpModue = (TUIThemeModule)[moduleObject integerValue];
              [self doApplyTheme:themeID forSingleModule:tmpModue];
          }
      } else {
          for (NSNumber *moduleObject in allKeys) {
              TUIThemeModule tmpModue = (TUIThemeModule)[moduleObject integerValue];
              if ((module & tmpModue) == tmpModue) {
                  [self doApplyTheme:themeID forSingleModule:tmpModue];
              }
          }
      }
#ifdef TUIThreadSafe
    });
#endif
}

- (void)unApplyThemeForModule:(TUIThemeModule)module {
#ifdef TUIThreadSafe
    dispatch_async(_queue, ^{
#endif
      BOOL isAll = NO;
      NSMutableArray *allKeys = [NSMutableArray arrayWithArray:self.themeResourcePathCache.allKeys];
      if (module == TUIThemeModuleAll || ((module & TUIThemeModuleAll) == TUIThemeModuleAll)) {
          isAll = YES;
      }

      if (isAll) {
          for (NSNumber *moduleObject in allKeys) {
              TUIThemeModule tmpModue = (TUIThemeModule)[moduleObject integerValue];
              [self setCurrentTheme:nil forModule:tmpModue];
          }

          [NSNotificationCenter.defaultCenter postNotificationName:TUIDidApplyingThemeChangedNotfication object:nil userInfo:nil];
      } else {
          for (NSNumber *moduleObject in allKeys) {
              TUIThemeModule tmpModue = (TUIThemeModule)[moduleObject integerValue];
              if ((module & tmpModue) == tmpModue) {
                  [self setCurrentTheme:nil forModule:tmpModue];
              }
          }
      }
#ifdef TUIThreadSafe
    });
#endif
}

#pragma mark - Not thread safe

- (void)doApplyTheme:(NSString *)themeID forSingleModule:(TUIThemeModule)module {
    TUITheme *theme = [self loadTheme:themeID module:module];
    if (theme == nil) {
        return;
    }

    [self setCurrentTheme:theme forModule:module];

    [self notifyApplyTheme:theme module:module];
}

- (TUITheme *)loadTheme:(NSString *)themeID module:(TUIThemeModule)module {
    NSString *themeResourcePath = [self themeResourcePathForModule:module];
    if (themeResourcePath.length == 0) {
        NSLog(@"[theme][applyTheme] theme resurce path not set, themeID:%@, module:%zd", themeID, module);
        return nil;
    }

    themeResourcePath = [themeResourcePath stringByAppendingPathComponent:themeID];
    {
        BOOL isDirectory = NO;
        BOOL exist = [NSFileManager.defaultManager fileExistsAtPath:themeResourcePath isDirectory:&isDirectory];
        if (!exist || !isDirectory) {
            NSLog(@"[theme][applyTheme] invalid theme resurce, themeID:%@, module:%zd", themeID, module);
            return nil;
        }
    }

    NSString *manifestPath = [themeResourcePath stringByAppendingPathComponent:@"manifest.plist"];
    {
        BOOL isDirectory = NO;
        BOOL exist = [NSFileManager.defaultManager fileExistsAtPath:manifestPath isDirectory:&isDirectory];
        if (!exist || isDirectory) {
            NSLog(@"[theme][applyTheme] invalid manifest, themeID:%@, module:%zd", themeID, module);
            return nil;
        }
    }

    NSString *resourcePath = [themeResourcePath stringByAppendingPathComponent:@"resource"];
    {
        BOOL isDirectory = NO;
        BOOL exist = [NSFileManager.defaultManager fileExistsAtPath:resourcePath isDirectory:&isDirectory];
        if (!exist || !isDirectory) {
            NSLog(@"[theme][applyTheme] invalid resurce, themeID:%@, module:%zd", themeID, module);
        }
    }

    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:manifestPath];
    if (dict == nil) {
        NSLog(@"[theme][applyTheme] manifest is null");
        return nil;
    }
    TUITheme *theme = [[TUITheme alloc] init];
    theme.themeID = themeID;
    theme.module = module;
    theme.themeDesc = [NSString stringWithFormat:@"theme_%@_%zd", themeID, module];
    theme.resourcePath = resourcePath;
    theme.manifest = dict;

    return theme;
}

- (void)notifyApplyTheme:(TUITheme *)theme module:(TUIThemeModule)module {
    if (theme == nil) {
        return;
    }

    if (![NSThread isMainThread]) {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
          [weakSelf notifyApplyTheme:theme module:module];
        });
        return;
    }

    [self allListenerExcuteonApplyThemeMethod:theme module:module];

    NSDictionary *userInfo = @{TUIDidApplyingThemeChangedNotficationModuleKey : @(module), TUIDidApplyingThemeChangedNotficationThemeKey : theme};
    [NSNotificationCenter.defaultCenter postNotificationName:TUIDidApplyingThemeChangedNotfication object:nil userInfo:userInfo];
}

- (void)allListenerExcuteonApplyThemeMethod:(TUITheme *)theme module:(TUIThemeModule)module {
    for (id<TUIThemeManagerListener> listener in self.listeners) {
        if ([listener respondsToSelector:@selector(onApplyTheme:module:)]) {
            [listener onApplyTheme:theme module:module];
        }
    }
}

- (NSString *)themeResourcePathForModule:(TUIThemeModule)module {
    if ([self.themeResourcePathCache.allKeys containsObject:@(module)]) {
        return [self.themeResourcePathCache objectForKey:@(module)];
    }
    return @"";
}

@end
