//
//  JKTimer.m
//  JKUtilDemo
//
//  Created by Jack on 7/1/16.
//  Copyright © 2016 Emerys. All rights reserved.
//

#import "JKTimerManager.h"

#define lockName(var) [NSString stringWithFormat:@"%@_canceller",var]
#define timerLock(var) [[JKTimerManager sharedTimerManager].lockContainer objectForKey:lockName(var)]

@interface JKTimerManager ()

@property (nonatomic,strong) NSMutableDictionary *timerContainer;
@property (nonatomic,strong) NSMutableDictionary *lockContainer;

@end

@implementation JKTimerManager

+ (instancetype)sharedTimerManager {
    static JKTimerManager *manager = nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        manager = [[JKTimerManager alloc] init];
    });
    
    return manager;
}

- (NSMutableDictionary *)timerContainer {
    if (!_timerContainer) {
        _timerContainer = [NSMutableDictionary dictionary];
    }
    return _timerContainer;
}

- (void)scheduledDispatchTimerWithName:(NSString *)name
                          timeInterval:(NSTimeInterval)timerInterval
                                 queue:(dispatch_queue_t)queue
                               repeats:(BOOL)repeats
                                action:(dispatch_block_t)action {
    if (name == nil)
        return;
    if (queue == nil)
        queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_source_t timer = [self.timerContainer objectForKey:name];
    if (!timer) {
        timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_resume(timer);
        [self.timerContainer setObject:timer forKey:name];
    }
    
    NSLock *timer_lock = [[NSLock alloc] init];
    [self.lockContainer setObject:timer_lock forKey:lockName(name)];
    
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, timerInterval * NSEC_PER_SEC), timerInterval * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
    __weak typeof(self) weakSelf = self;
    
    dispatch_source_set_event_handler(timer, ^{
        
        [timerLock(name) lock];
        
        if (timer && action) {
            
            action();
            if (!repeats) {
                [weakSelf cancelTimerWithName:name];
            }
            
        }
        
        [timerLock(name) unlock];
    });
    
}

- (void)cancelTimerWithName:(NSString *)name {
    
    dispatch_source_t timer = [self.timerContainer objectForKey:name];
    if (!timer) {
        return;
    }
    
    NSLock *timer_lock = timerLock(name);
    [timer_lock lock];

    [self.lockContainer removeObjectForKey:lockName(name)];
    [self.timerContainer removeObjectForKey:name];
    dispatch_source_cancel(timer);
    timer = nil;
    
    [timer_lock unlock];
    
}

- (void)cancelAllTimer {
    for (NSString *name in self.timerContainer.allKeys) {
        [self cancelTimerWithName:name];
    }
}

@end
