//Header
#import "VRTUManager.h"

//Dependencies
#import "UnityAppController.h"

//VRTBannerInfo
@interface VRTBannerInfo : NSObject
@property VRTBanner *banner;
@property VRTBannerPosition position;
@end

@implementation VRTBannerInfo
@end


//VRTUManager
@interface VRTUManager()
@property (weak) UIViewController *unityViewController;
@property NSMutableDictionary *bannerInfoDict;
@property NSMutableDictionary *interstitialDict;
@end


@implementation VRTUManager

+ (instancetype) singleton {
    static VRTUManager *singleton = nil;
    static dispatch_once_t dispatchOnceToken;
    dispatch_once(&dispatchOnceToken, ^{
        singleton = [[VRTUManager alloc] init];
    });
    return singleton;
}

-(instancetype) init {
    self = [super init];
    self.bannerInfoDict = [@{} mutableCopy];
    self.interstitialDict = [@{} mutableCopy];
    
    //Observe orientation changes
    [[NSNotificationCenter defaultCenter] addObserver:self
                           selector:@selector(observer_UIApplicationDidChangeStatusBarOrientationNotification)
                               name:UIApplicationDidChangeStatusBarFrameNotification
                             object:nil];
    
    return self;
}


#pragma mark - SDK Init
-(void) initializeSdkWithAppId:(NSUInteger)appId {
    dispatch_async(dispatch_get_main_queue(), ^{
        self.unityViewController = ((UnityAppController *)[UIApplication sharedApplication].delegate).rootViewController;
        [VrtcalSDK initializeSdkWithAppId:appId sdkDelegate:[VRTUManager singleton]];
    });
}

//VrtcalSdkDelegate
/**
 * SDK successfully initialized.
 */
- (void)sdkInitialized {
    VRTLogWhereAmI();
    if (self.sdkInitializedCallback) {
        self.sdkInitializedCallback();
    }
}

/**
 * SDK initialization failed.
 */
- (void)sdkInitializationFailedWithError:(NSError*)error {
    VRTLogError(@"error: %@", error);
    if (self.sdkInitializationFailedWithErrorCallback) {
        self.sdkInitializationFailedWithErrorCallback([[error description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
}
#pragma mark - Pii
- (void)setPiiData: (nullable NSString*) piiData {
    VRTLogWhereAmI();
    [VrtcalSDK setPiiData: piiData];
}

- (void)setPiiDataTypes: (nullable NSString*) piiDataType {
    VRTLogWhereAmI();
    [VrtcalSDK setPiiDataTypes: piiDataType];
}

#pragma mark - Banner API
- (void) bannerLoadWithZoneId:(int)zoneId position:(VRTBannerPosition)position size:(CGSize)size {
    VRTLogWhereAmI();

    dispatch_async(dispatch_get_main_queue(), ^{

        //Destroy old banner
        [self helperBannerDestroyWithZoneId:zoneId];

        //Create new banner
        VRTBanner *banner = [[VRTBanner alloc] initWithFrame:CGRectMake(0,0,size.width,size.height)];
        banner.adDelegate = self;
        VRTBannerInfo *bannerInfo = [[VRTBannerInfo alloc] init];
        bannerInfo.banner = banner;
        bannerInfo.position = position;
        [self updateOrigin:bannerInfo];

        //Save banner
        [self.bannerInfoDict setObject:bannerInfo forKey:@(zoneId)];

        //Update UI and request ad
        [self unityAddSubview:banner];
        [banner loadAd:zoneId];
    });
}

- (void) bannerLoadWithZoneId:(int)zoneId frame:(CGRect)frame {
    VRTLogWhereAmI();

    dispatch_async(dispatch_get_main_queue(), ^{

        //Destroy old banner
        [self helperBannerDestroyWithZoneId:zoneId];

        //Create new bannerInfo
        VRTBanner *banner = [[VRTBanner alloc] initWithFrame:frame];
        banner.adDelegate = self;
        VRTBannerInfo *bannerInfo = [[VRTBannerInfo alloc] init];
        bannerInfo.banner = banner;
        bannerInfo.position = VRTBannerPositionManual;
        
        //Save banner in array
        [self.bannerInfoDict setObject:bannerInfo forKey:@(zoneId)];

        //Update UI and request ad
        [self unityAddSubview:banner];
        [banner loadAd:zoneId];
    });
}

-(void) bannerMoveWithZoneId:(int)zoneId origin:(CGPoint)origin {
    VRTLogWhereAmI();
    dispatch_async(dispatch_get_main_queue(), ^{
        VRTBannerInfo *bannerInfo = [self.bannerInfoDict objectForKey:@(zoneId)];

        if (!bannerInfo) {
            VRTLogInfo(@"No banner for zoneId %i", zoneId);
            return;
        }

        bannerInfo.banner.frame = CGRectMake(origin.x, origin.y, bannerInfo.banner.frame.size.width, bannerInfo.banner.frame.size.height);
        bannerInfo.position = VRTBannerPositionManual;
    });
}

-(void) bannerDestroyWithZoneId:(int)zoneId {
    VRTLogWhereAmI();
    dispatch_async(dispatch_get_main_queue(), ^{
        if (![self helperBannerDestroyWithZoneId:zoneId]) {
            VRTLogInfo(@"No banner for zoneId %i", zoneId);
        }
    });
}

-(BOOL) helperBannerDestroyWithZoneId:(int)zoneId {
    VRTBannerInfo *bannerInfo = [self.bannerInfoDict objectForKey:@(zoneId)];

    if (!bannerInfo) {
        return NO;
    }

    [bannerInfo.banner removeFromSuperview];
    [self.bannerInfoDict removeObjectForKey:@(zoneId)];
    return YES;
}

#pragma mark - VRTBannerDelegate
- (void)vrtBannerAdLoaded:(nonnull VRTBanner *)vrtBanner withAdSize:(CGSize)adSize {
    VRTLogWhereAmI();
    if (self.bannerAdLoadedCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdLoadedCallback, zoneId = %i", zoneId);
        self.bannerAdLoadedCallback(zoneId);
    }
}

- (void)vrtBannerAdFailedToLoad:(nonnull VRTBanner *)vrtBanner error:(nonnull NSError *)error {
    VRTLogWhereAmI();
    if (self.bannerAdFailedToLoadCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdFailedToLoadCallback, zoneId = %i", zoneId);
        self.bannerAdFailedToLoadCallback(zoneId, [[error description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
}

- (void)vrtBannerVideoStarted:(nonnull VRTBanner *)vrtBanner {
    VRTLogWhereAmI();
    if (self.bannerVideoStartedCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerVideoStartedCallback, zoneId = %i", zoneId);
        self.bannerVideoStartedCallback(zoneId);
    }
}

- (void)vrtBannerVideoCompleted:(nonnull VRTBanner *)vrtBanner {
    VRTLogWhereAmI();
    if (self.bannerVideoCompletedCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerVideoCompletedCallback, zoneId = %i", zoneId);
        self.bannerVideoCompletedCallback(zoneId);
    }
}

- (void)vrtBannerAdClicked:(nonnull VRTBanner *)vrtBanner {
    VRTLogWhereAmI();
    if (self.bannerAdClickedCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdClickedCallback, zoneId = %i", zoneId);
        self.bannerAdClickedCallback(zoneId);
    }
}

- (void)vrtBannerAdWillLeaveApplication:(nonnull VRTBanner *)vrtBanner {
    VRTLogWhereAmI();
    if (self.bannerAdWillLeaveApplicationCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdWillLeaveApplicationCallback, zoneId = %i", zoneId);
        self.bannerAdWillLeaveApplicationCallback(zoneId);
    }
}

- (void)vrtBannerWillPresentModal:(nonnull VRTBanner *)vrtBanner ofType:(VRTModalType)modalType {
    VRTLogWhereAmI();
    if (self.bannerAdWillPresentModalCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdWillPresentModalCallback, zoneId = %i", zoneId);
        self.bannerAdWillPresentModalCallback(zoneId);
    }
}

- (void)vrtBannerDidPresentModal:(nonnull VRTBanner *)vrtBanner ofType:(VRTModalType)modalType {
    VRTLogWhereAmI();
    if (self.bannerAdDidPresentModalCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdDidPresentModalCallback, zoneId = %i", zoneId);
        self.bannerAdDidPresentModalCallback(zoneId);
    }
}

- (void)vrtBannerWillDismissModal:(nonnull VRTBanner *)vrtBanner ofType:(VRTModalType)modalType {
    VRTLogWhereAmI();
    
    if (self.bannerAdWillDismissModalCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdWillDismissModalCallback, zoneId = %i", zoneId);
        self.bannerAdWillDismissModalCallback(zoneId);
    }
}

- (void)vrtBannerDidDismissModal:(nonnull VRTBanner *)vrtBanner ofType:(VRTModalType)modalType {
    VRTLogWhereAmI();
    if (self.bannerAdDidDismissModalCallback) {
        int zoneId = [self zoneIdForBanner:vrtBanner];
        VRTLogInfo(@"Calling bannerAdDidDismissModalCallback, zoneId = %i", zoneId);
        self.bannerAdDidDismissModalCallback(zoneId);
    }
}

//Shared by VRTBannerDelegate and VRTInterstitialDelegate
- (nonnull UIViewController *)vrtViewControllerForModalPresentation {
    return self.unityViewController;
}




#pragma mark - Interstitial API
- (void) interstitialLoadWithZoneId:(int)zoneId {
    VRTInterstitial *interstitial = [[VRTInterstitial alloc] init];
    [self.interstitialDict setObject:interstitial forKey:@(zoneId)];
    interstitial.adDelegate = self;
    [interstitial loadAd:zoneId];
}

- (void) interstitialShowWithZoneId:(int)zoneId {
    VRTInterstitial *interstitial = [self.interstitialDict objectForKey:@(zoneId)];

    if (!interstitial) {
        VRTLogInfo(@"No interstitial for zoneId %i", zoneId);
        return;
    }    
    [interstitial showAd];
}

-(void) interstitialDestroyWithZoneId:(int)zoneId {
    VRTLogWhereAmI();
    dispatch_async(dispatch_get_main_queue(), ^{
        VRTInterstitial *interstitial = [self.interstitialDict objectForKey:@(zoneId)];

        if (!interstitial) {
            VRTLogInfo(@"No interstitial for zoneId %i", zoneId);
            return;
        }

        [self.interstitialDict removeObjectForKey:@(zoneId)];
    });
}


#pragma mark - VRTInterstitialDelegate
- (void)vrtInterstitialAdLoaded:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialAdLoadedCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdLoadedCallback, zoneId = %i", zoneId);
        self.interstitialAdLoadedCallback(zoneId);
    }
}

- (void)vrtInterstitialAdFailedToLoad:(nonnull VRTInterstitial *)vrtInterstitial error:(nonnull NSError *)error {
    VRTLogWhereAmI();
    if (self.interstitialAdFailedToLoadCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdFailedToLoadCallback, zoneId = %i", zoneId);
        self.interstitialAdFailedToLoadCallback(zoneId, [[error description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
}

- (void)vrtInterstitialAdWillShow:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialAdWillShowCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdWillShowCallback, zoneId = %i", zoneId);
        self.interstitialAdWillShowCallback(zoneId);
    }
}

- (void)vrtInterstitialAdDidShow:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialAdDidShowCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdDidShowCallback, zoneId = %i", zoneId);
        self.interstitialAdDidShowCallback(zoneId);
    }
}

- (void)vrtInterstitialAdFailedToShow:(nonnull VRTInterstitial *)vrtInterstitial error:(nonnull NSError *)error {
    VRTLogWhereAmI();
    if (self.interstitialAdFailedToShowCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdFailedToShowCallback, zoneId = %i", zoneId);
        self.interstitialAdFailedToShowCallback(zoneId, [[error description] cStringUsingEncoding:NSUTF8StringEncoding]);
    }
}

- (void)vrtInterstitialVideoStarted:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialVideoStartedCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialVideoStartedCallback, zoneId = %i", zoneId);
        self.interstitialVideoStartedCallback(zoneId);
    }
}

- (void)vrtInterstitialVideoCompleted:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialVideoCompletedCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialVideoCompletedCallback, zoneId = %i", zoneId);
        self.interstitialVideoCompletedCallback(zoneId);
    }
}

- (void)vrtInterstitialAdClicked:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialAdClickedCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdClickedCallback, zoneId = %i", zoneId);
        self.interstitialAdClickedCallback(zoneId);
    }
}

- (void)vrtInterstitialAdWillLeaveApplication:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialAdWillLeaveApplicationCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdWillLeaveApplicationCallback, zoneId = %i", zoneId);
        self.interstitialAdWillLeaveApplicationCallback(zoneId);
    }
}

- (void)vrtInterstitialAdWillDismiss:(nonnull VRTInterstitial *)vrtInterstitial {
    VRTLogWhereAmI();
    if (self.interstitialAdWillDismissCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdWillDismissCallback, zoneId = %i", zoneId);
        self.interstitialAdWillDismissCallback(zoneId);
    }
}

- (void)vrtInterstitialAdDidDismiss:(nonnull VRTInterstitial *)vrtInterstitial {
    if (self.interstitialAdDidDismissCallback) {
        int zoneId = [self zoneIdForInterstitial:vrtInterstitial];
        VRTLogInfo(@"Calling interstitialAdDidDismissModalCallback, zoneId = %i", zoneId);        
        self.interstitialAdDidDismissCallback(zoneId);
    }
}

#pragma mark - Rotation
-(void)observer_UIApplicationDidChangeStatusBarOrientationNotification {
    dispatch_async(dispatch_get_main_queue(), ^{
        for (VRTBannerInfo* bannerInfo in [self.bannerInfoDict allValues]) {
            [self updateOrigin:bannerInfo];
        }
    });
}

#pragma mark - Utility

-(int) zoneIdForBanner:(VRTBanner*)banner {
    
    for (NSNumber* numZoneId in [self.bannerInfoDict allKeys]) {
        VRTBannerInfo *bannerInfo = [self.bannerInfoDict objectForKey:numZoneId];
        if (bannerInfo.banner == banner) {
            return [numZoneId intValue];
        }
    }

    return -1;
}

-(int) zoneIdForInterstitial:(VRTInterstitial*)interstitial {
    int ret = [[[self.interstitialDict allKeysForObject:interstitial] firstObject] intValue];
    return ret;
}

- (void) unityAddSubview:(UIView*)view {
    UIView *unityView = [self unityViewController].view;
    [unityView addSubview:view];
}

-(void) updateOrigin:(VRTBannerInfo*)bannerInfo {
    
    if (![NSThread isMainThread]) {
        VRTLogError(@"Must be called from main thread");
        return;
    }
    
    //Get the safe area frame if it is available
    CGRect parentFrame = self.unityViewController.view.frame;
    if (@available(iOS 11, *)) {
        CGRect safeAreaFrame = self.unityViewController.view.safeAreaLayoutGuide.layoutFrame;
        if (!CGSizeEqualToSize(CGSizeZero, safeAreaFrame.size)) {
            parentFrame = safeAreaFrame;
        }
    }
    
    CGFloat x;
    CGFloat y;
    CGFloat width = bannerInfo.banner.frame.size.width;
    CGFloat height = bannerInfo.banner.frame.size.height;

    switch (bannerInfo.position) {
        case VRTBannerPositionTop:
            x = parentFrame.size.width/2 - bannerInfo.banner.frame.size.width/2;
            y = 0;
            break;

        case VRTBannerPositionBottom:
            x = parentFrame.size.width/2 - bannerInfo.banner.frame.size.width/2;
            y = parentFrame.size.height - bannerInfo.banner.frame.size.height;
            break;
        
        case VRTBannerPositionManual:
            x = bannerInfo.banner.frame.origin.x;
            y = bannerInfo.banner.frame.origin.y;
    }
    
    //Adjust origin to account for safe area
    x += parentFrame.origin.x;
    y += parentFrame.origin.y;

    bannerInfo.banner.frame = CGRectMake(x, y, width, height);
    
    VRTLogInfo(@"frame: %f,%f,%fx%f", x,y,width,height);
}

@end
