//
//  MVMCoreAlertHandler.m
//  myverizon
//
//  Created by Scott Pfeil on 3/10/14.
//  Copyright (c) 2014 Verizon Wireless. All rights reserved.
//

#import "MVMCoreAlertHandler.h"
#import "MVMCoreAlertObject.h"
#import <MVMCore/MVMCoreAlertController.h>
#import "MVMCoreAlertOperation.h"
#import "MVMCoreTopAlertOperation.h"
#import <MVMCore/MVMCoreJSONConstants.h>
#import <MVMCore/NSDictionary+MFConvenience.h>
#import <MVMCore/NSArray+MFConvenience.h>
#import <MVMCoreUI/MVMCoreUI-Swift.h>

@interface MVMCoreAlertHandler ()

// Flag that keeps track of if the alerts are supressed or not.
@property (assign, nonatomic) BOOL mfAlertsSupressed;

@end

@implementation MVMCoreAlertHandler

+ (instancetype)sharedAlertHandler {
    static dispatch_once_t once;
    static id sharedInstance;
    
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    
    return sharedInstance;
}

- (nullable instancetype)init {
    if (self = [super init]) {
        self.popupAlertQueue = [[NSOperationQueue alloc] init];
        self.popupAlertQueue.maxConcurrentOperationCount = 1;
        self.topAlertQueue = [[NSOperationQueue alloc] init];
        self.topAlertQueue.maxConcurrentOperationCount = 1;
        [self registerForPageChanges];
    }
    return self;
}

#pragma mark - Popup Alert Functions

- (BOOL)alertCurrentlyShowing {
    return (self.popupAlertQueue.operationCount > 0);
}

- (BOOL)greedyAlertShowing {
    if ([self alertCurrentlyShowing]) {
        NSInteger index = [self.popupAlertQueue.operations indexOfObjectPassingTest:^BOOL(__kindof MVMCoreAlertOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (obj.isExecuting && obj.isGreedy && stop) {
                *stop = YES;
                return YES;
            } else {
                return NO;
            }
        }];
        return (index != NSNotFound);
    }
    return NO;
}

- (nonnull UIAlertController *)showAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nullable NSArray<UIAlertAction *>*)actions isGreedy:(BOOL)isGreedy {
    return [self showAlertWithTitle:title message:message actions:actions isGreedy:isGreedy alertDelegate:nil];
}

- (nonnull UIAlertController *)showAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nullable NSArray<UIAlertAction *>*)actions isGreedy:(BOOL)isGreedy alertDelegate:(nullable NSObject <MVMCoreAlertDelegateProtocol>*)alertDelegate {
    return [self showAlertWithTitle:title message:message actions:actions alertStyle:UIAlertControllerStyleAlert isGreedy:isGreedy alertDelegate:alertDelegate];
}

- (nonnull UIAlertController *)showAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nullable NSArray<UIAlertAction *>*)actions alertStyle:(UIAlertControllerStyle)alertStyle isGreedy:(BOOL)isGreedy alertDelegate:(nullable NSObject <MVMCoreAlertDelegateProtocol>*)alertDelegate {
    
    // It's a greedy alert! Clear all alerts that are queued up and the one that is showing
    if (isGreedy) {
        [self removeAllAlertViews];
    }
    
    // Create the alert. Adds the actions one by one.
    MVMCoreAlertController *alertController = [MVMCoreAlertController alertControllerWithTitle:(title ?: @"") message:message preferredStyle:alertStyle];
    for (NSUInteger i = 0; i < [actions count]; i++) {
        UIAlertAction *action = [actions objectAtIndex:i ofType:[UIAlertAction class]];
        if (action) {
            [alertController addAction:action];
        }
    }
    
    MVMCoreAlertOperation *alertOperation = [[MVMCoreAlertOperation alloc] initWithAlert:alertController isGreedy:isGreedy alertDelegate:alertDelegate];
    [self.popupAlertQueue addOperation:alertOperation];
    return alertController;
}

- (nonnull UIAlertController *)showAlertWithAlertObject:(nonnull MVMCoreAlertObject *)alertObject {
    MVMCoreAlertController *controller = (MVMCoreAlertController *)[self showAlertWithTitle:alertObject.title message:alertObject.message actions:alertObject.actions alertStyle:alertObject.alertStyle isGreedy:alertObject.isGreedy alertDelegate:alertObject.alertDelegate];
    controller.alertObject = alertObject;
    return controller;
}

- (void)removeAlertViewForObject:(MVMCoreAlertObject *)alertObject {
    for (MVMCoreAlertOperation *operation in self.popupAlertQueue.operations) {
        if ([operation.currentAlertView isKindOfClass:[MVMCoreAlertController class]] && [(MVMCoreAlertController *)operation.currentAlertView alertObject] == alertObject) {
            [operation cancel];
        }
    }
}

- (void)removeAlertViewUsingPredicate:(BOOL(^)(MVMCoreAlertObject *obj))predicate {
    for (MVMCoreAlertOperation *operation in self.popupAlertQueue.operations) {
        if ([operation.currentAlertView isKindOfClass:[MVMCoreAlertController class]]) {
            MVMCoreAlertObject *alertObject = [(MVMCoreAlertController *)operation.currentAlertView alertObject];
            if (alertObject && predicate(alertObject)) {
                [operation cancel];
            }
        }
    }
}

- (void)removeAllAlertViews {
    [self.popupAlertQueue cancelAllOperations];
}

#pragma mark - Supression Functions

- (BOOL)mfAlertsSupressed {
    return _mfAlertsSupressed;
}

- (void)supressMFAlerts {
    if (!self.mfAlertsSupressed) {
        self.mfAlertsSupressed = YES;
        for (MVMCoreAlertOperation *operation in self.popupAlertQueue.operations) {
            [operation pause];
        }
        for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) {
            [operation pause];
        }
    }
}

- (void)unSupressMFAlerts {
    if (self.mfAlertsSupressed) {
        self.mfAlertsSupressed = NO;
        for (MVMCoreAlertOperation *operation in self.popupAlertQueue.operations) {
            [operation unpause];
        }
        for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) {
            [operation unpause];
        }
    }
}

#pragma mark - Top Alert Functions

- (void)addTopAlertOperation:(nonnull MVMCoreTopAlertOperation *)topAlertOperation {
    __block MVMCoreTopAlertOperation *alertOperation = topAlertOperation;
    __weak typeof(self) weakSelf = self;
    [alertOperation setCompletionBlock:^{
        
        // If the alert was cancelled to show another with higher priority, re-add to the operation when cancelled to the queue.
        if (alertOperation.reAddAfterCancel) {
            MVMCoreTopAlertOperation *newOperation = [alertOperation copy];
            newOperation.reAddAfterCancel = NO;
            [weakSelf addTopAlertOperation:newOperation];
        }
        alertOperation = nil;
    }];
    
    NSString *currentPageType = ((UIViewController<MVMCoreViewControllerProtocol> *)[[MVMCoreUISplitViewController mainSplitViewController] getCurrentDetailViewController]).pageType;
    [alertOperation updateDisplayableByPageType:currentPageType];
    
    [self.topAlertQueue addOperation:alertOperation];
    [self reevaluteQueue];
}

- (void)showTopAlertWithObject:(nullable MVMCoreTopAlertObject *)topAlertObject {
    MVMCoreTopAlertOperation *alertOperation = [[MVMCoreTopAlertOperation alloc] initWithTopAlertObject:topAlertObject];
    [self addTopAlertOperation:alertOperation];
}

- (void)showTopAlertErrorWithMessage:(nullable NSString *)message {

    MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:ValueTypeError message:message];
    [self showTopAlertWithObject:topAlertObject];
}

- (void)showTopAlertConfirmationWithMessage:(nullable NSString *)message {

    MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:ValueTypeSuccess message:message];
    [self showTopAlertWithObject:topAlertObject];
}

- (void)showTopAlertWithType:(nullable NSString *)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData {
    
    MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:type message:message subMessage:subMessage persistent:persistent actionMap:actionMap additionalData:additionalData];
    [self showTopAlertWithObject:topAlertObject];
}

- (void)showTopAlertWithType:(nullable NSString *)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id  _Nonnull sender))userActionHandler {
    
    MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:type message:message subMessage:subMessage persistent:persistent buttonTitle:buttonTitle userActionHandler:userActionHandler];
    [self showTopAlertWithObject:topAlertObject];
}

- (void)hideTopAlertView {
    
    MVMCoreTopAlertOperation *currentOperation = [self.topAlertQueue.operations firstObject];
    currentOperation.topAlertObject.persistent = NO;
    currentOperation.reAddAfterCancel = NO;
    [currentOperation cancel];
}

- (BOOL)hasPersistentTopAlertOfType:(nullable NSString *)type {
    BOOL hasAlert = NO;
    for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) {
        if (operation.topAlertObject.persistent && [operation.topAlertObject.type isEqualToString:type]) {
            hasAlert = YES;
        }
    }
    return hasAlert;
}

- (void)hidePersistentTopAlertViewOfType:(nullable NSString *)type {
    if (type) {
        for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) {
            
            // Cancel all persistent operations of this type.
            if (operation.topAlertObject.persistent && [operation.topAlertObject.type isEqualToString:type]) {
                operation.reAddAfterCancel = NO;
                [operation cancel];
            }
        }
    }
}

- (void)hideTopAlertViewOfType:(nullable NSString *)type {
    if (type) {
        for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) {
            
            // Cancel all operations of this type.
            if ([operation.topAlertObject.type isEqualToString:type]) {
                operation.reAddAfterCancel = NO;
                [operation cancel];
            }
        }
    }
}

- (void)removeTopAlertForObject:(MVMCoreTopAlertObject *)topAlertObject {
    for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) {
        // Finds an cancels top alerts associated with the object.
        if (operation.topAlertObject == topAlertObject) {
            operation.reAddAfterCancel = NO;
            [operation cancel];
        }
    }
}

- (void)removeAllTopAlerts {
    [self.topAlertQueue cancelAllOperations];
}

@end
