//
//  MVMCoreAlertOperation.m
//  myverizon
//
//  Created by Scott Pfeil on 9/28/15.
//  Copyright © 2015 Verizon Wireless. All rights reserved.
//

#import "MVMCoreAlertOperation.h"
#import <MVMCoreUI/MVMCoreAlertHandler.h>
#import <MVMCore/MVMCoreAlertController.h>
#import <MVMCore/MVMCoreNavigationHandler.h>

@interface MVMCoreAlertOperation () {
    __block BOOL _paused;
    __block BOOL _displayed;
}

@property (readwrite, getter=isPaused) BOOL paused;

@property (readwrite, getter=isGreedy) BOOL greedy;

@property (readwrite, getter=isDisplayed) BOOL displayed;

@property (readwrite, nullable, nonatomic, weak) NSObject <MVMCoreAlertDelegateProtocol> *alertDelegate;

// The currently displayed alert view.
@property (nullable, strong, nonatomic) UIAlertController *currentAlertView;

// A boolean to keep track of if we alreadys signed up to observe.
@property (assign, nonatomic) BOOL alertBeingObserved;

// For thread safety
@property (strong, nonatomic) dispatch_queue_t pausedQueue;
@property (strong, nonatomic) dispatch_queue_t displayedQueue;

// Dismisses the alert.
- (void)dismissAlertView;

// Begins observing for when the alert is dismissed.
- (void)observeForCurrentAlertViewDismissal;

// Stops observing for when the alert is dismissed.
- (void)stopObservingAlertView;

@end

@implementation MVMCoreAlertOperation

// The context for kvo
static void * XXContext = &XXContext;

- (instancetype)init {
    
    self = [super init];
    if (self) {
        self.pausedQueue = dispatch_queue_create("paused", DISPATCH_QUEUE_CONCURRENT);
        self.displayedQueue = dispatch_queue_create("displayed", DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

- (nullable instancetype)initWithAlert:(nonnull UIAlertController *)alert isGreedy:(BOOL)isGreedy {

    if (self = [self init]) {
        self.currentAlertView = alert;
        self.greedy = isGreedy;
    }
    return self;
}

- (nullable instancetype)initWithAlert:(nonnull UIAlertController *)alert isGreedy:(BOOL)isGreedy alertDelegate:(nullable NSObject <MVMCoreAlertDelegateProtocol>*)alertDelegate {
    if (self = [self initWithAlert:alert isGreedy:isGreedy]) {
        self.alertDelegate = alertDelegate;
    }
    return self;
}

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

- (BOOL)isPaused {
    __block BOOL isPaused;
    dispatch_sync(self.pausedQueue, ^{
        isPaused = self->_paused;
    });
    return isPaused;
}

- (void)setPaused:(BOOL)paused {
    dispatch_barrier_async(self.pausedQueue, ^{
        self->_paused = paused;
    });
}

- (BOOL)isDisplayed {
    __block BOOL isDisplayed;
    dispatch_sync(self.displayedQueue, ^{
        isDisplayed = self->_displayed;
    });
    return isDisplayed;
}

- (void)setDisplayed:(BOOL)displayed {
    dispatch_barrier_async(self.displayedQueue, ^{
        self->_displayed = displayed;
    });
}

- (void)main {

    // Always check for cancellation before launching the task.
    if ([self checkAndHandleForCancellation]) {
        return;
    }
    
    // Display alert only if alerts aren't supressed.
    if (![[MVMCoreAlertHandler sharedAlertHandler] mfAlertsSupressed] && self.currentAlertView) {
        
        // Observe for when it is removed.
        [self observeForCurrentAlertViewDismissal];
        
        // Adds the presentation to the animation queue.
        [[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:self.currentAlertView animated:YES delegate:nil completionHandler:^{
            
            // We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task
            if (!self.isDisplayed) {
                [self markAsFinished];
            } else if (self.isCancelled) {
                [self dismissAlertView];
            }
        }];
    }
}

- (void)cancel {
    [super cancel];
    
    // Notify delegate that the alert is cancelled.
    if ([self.alertDelegate respondsToSelector:@selector(alertCancelled:)]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.alertDelegate alertCancelled:self.currentAlertView];
        });
    }
    
    [self dismissAlertView];
}

- (void)dismissAlertView {

    if (self.isDisplayed) {
        
        // Dismisses.
        [[MVMCoreNavigationHandler sharedNavigationHandler] dismissViewController:self.currentAlertView animated:YES];
    }
}

- (void)pause {
    [self willChangeValueForKey:@"isPaused"];
    self.paused = YES;
    [self didChangeValueForKey:@"isPaused"];
    
    // Notify delegate of pause.
    if ([self.alertDelegate respondsToSelector:@selector(alertPaused:)]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.alertDelegate alertPaused:self.currentAlertView];
        });
    }
    
    // Dismiss until unpaused.
    [self dismissAlertView];
}

- (void)unpause {
    [self willChangeValueForKey:@"isPaused"];
    self.paused = NO;
    [self didChangeValueForKey:@"isPaused"];
    
    // Notify delegate of unpause.
    if ([self.alertDelegate respondsToSelector:@selector(alertUnpaused:)]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.alertDelegate alertUnpaused:self.currentAlertView];
        });
    }
    
    // Show alert...
    if (self.currentAlertView) {
        [self start];
    }
}

#pragma mark - Observer Functions

- (void)observeForCurrentAlertViewDismissal {
    if (!self.alertBeingObserved && ![[MVMCoreAlertHandler sharedAlertHandler] mfAlertsSupressed] && self.currentAlertView && [self.currentAlertView isKindOfClass:[UIAlertController class]]) {
        self.alertBeingObserved = YES;
        [self.currentAlertView addObserver:self forKeyPath:@"visible" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:XXContext];
    }
}

- (void)stopObservingAlertView {
    if (self.alertBeingObserved) {
        [self.currentAlertView removeObserver:self forKeyPath:@"visible" context:XXContext];
        self.alertBeingObserved = NO;
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == XXContext && [keyPath isEqualToString:@"visible"]) {
        if (![object isVisible]) {

            self.displayed = NO;
            
            // Notify delegate that the alert is dismissed.
            if ([self.alertDelegate respondsToSelector:@selector(alertDismissed:)]) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.alertDelegate alertDismissed:self.currentAlertView];
                });
            }
            
            // Is visible was set to NO, meaning that the alertview is no longer visible.
            if (!self.isPaused) {
                [self stopObservingAlertView];
                self.currentAlertView = nil;
                [self markAsFinished];
            }
        } else {

            self.displayed = YES;
            
            // Notify delegate that the alert is shown.
            if ([self.alertDelegate respondsToSelector:@selector(alertShown:)]) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.alertDelegate alertShown:self.currentAlertView];
                });
            }
        }
    }
}

@end
