#include "PokktAdsPrivatePCH.h"
#include "PokktAdsFunctions.h"
#include "PokktAdsComponent.h"


/**
 * LOCAL CONSTS
 **/

#define EVENT_AD_CACHING_COMPLETED          "AdCachingCompleted"
#define EVENT_AD_CACHING_FAILED             "AdCachingFailed"
#define EVENT_AD_CLOSED                     "AdClosed"
#define EVENT_AD_COMPLETED                  "AdCompleted"
#define EVENT_AD_DISPLAYED                  "AdDisplayed"
#define EVENT_AD_CLICKED                    "AdClicked"
#define EVENT_AD_SKIPPED                    "AdSkipped"
#define EVENT_AD_GRATIFIED                  "AdGratified"
#define EVENT_AD_FAILED_TO_SHOW             "AdFailedToShow"
#define EVENT_BANNER_LOADED			        "BannerLoaded"
#define EVENT_BANNER_LOAD_FAILED			"BannerLoadFailed"
#define EVENT_BANNER_CLICKED                "BannerClicked"

#define VIDEO_TYPE           "0"
#define INTERSTITIAL_TYPE    "3"
#define BANNER_TYPE          "1"

/**
 * Platform Specific
 **/
#if PLATFORM_ANDROID

void notifyAndroid(FString operation, FString param);
bool isVideoAdCachedOnAndroid(FString screenName, bool isRewarded);
bool isInterstitialCachedOnAndroid(FString screenName, bool isRewarded);
FString getSDKVersionOnAndroid();

#elif PLATFORM_IOS

void notifyIOS(FString operation, FString param);
bool isVideoAdCachedOnIOS(FString screenName, bool isRewarded);
bool isInterstitialCachedOnIOS(FString screenName, bool isRewarded);
FString getSDKVersionOnIOS();

#endif


void UPokktAdsFunctions::SetPokktConfig(FString appId, FString secKey)
{
	TSharedPtr<FJsonObject> jsonObject = MakeShareable(new FJsonObject());
	jsonObject->SetStringField("appId", appId);
	jsonObject->SetStringField("securityKey", secKey);

	FString outputString;
	TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&outputString);

	if (FJsonSerializer::Serialize(jsonObject.ToSharedRef(), Writer))
		notifyNative(SET_POKKT_CONFIG_OP, outputString);
	else
		UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] failed to set pokkt config!"));
}

void UPokktAdsFunctions::LoadBanner(FString screenName, int bannerPosition)
{
	TSharedPtr<FJsonObject> jsonObject = MakeShareable(new FJsonObject());
	jsonObject->SetStringField("screenName", screenName);
	jsonObject->SetNumberField("bannerPosition", bannerPosition);

	FString outputString;
	TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&outputString);

	if (FJsonSerializer::Serialize(jsonObject.ToSharedRef(), Writer))
		notifyNative(BANNER_LOAD_OP, outputString);
	else
		UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] failed to load banner!"));
}

void UPokktAdsFunctions::SetPokktConsentInfo(bool gdprApplicable, bool gdprConsentAvailable)
{
	TSharedPtr<FJsonObject> jsonObject = MakeShareable(new FJsonObject());
	jsonObject->SetStringField("GDPRApplicable", gdprApplicable ? "true" : "false");
	jsonObject->SetStringField("GDPRConsentAvailable", gdprConsentAvailable ? "true" : "false");

	FString outputString;
	TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&outputString);

	if (FJsonSerializer::Serialize(jsonObject.ToSharedRef(), Writer))
		notifyNative(SET_DATA_ACCESS_CONSENT_OP, outputString);
	else
		UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] failed to set pokkt config!"));
}

void UPokktAdsFunctions::notifyNative(FString operation, FString param)
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] notifying native of operation: %s"), *operation);

#if PLATFORM_ANDROID

	notifyAndroid(operation, param);

#elif PLATFORM_IOS

	notifyIOS(operation, param);

#endif
}

bool UPokktAdsFunctions::isVideoAdCachedOnNative(FString screenName, bool isRewarded)
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] getting video ad cached on native..."));

#if PLATFORM_ANDROID

	return isVideoAdCachedOnAndroid(screenName, isRewarded);

#elif PLATFORM_IOS

	return isVideoAdCachedOnIOS(screenName, isRewarded);

#endif

	return "";
}

bool UPokktAdsFunctions::isInterstitialCachedOnNative(FString screenName, bool isRewarded)
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] getting interstitial cached on native..."));

#if PLATFORM_ANDROID

	return isInterstitialCachedOnAndroid(screenName, isRewarded);

#elif PLATFORM_IOS

	return isInterstitialCachedOnIOS(screenName, isRewarded);

#endif

	return "";
}

FString UPokktAdsFunctions::getSDKVersionOnNative()
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] getting sdk version on native..."));

#if PLATFORM_ANDROID

	return getSDKVersionOnAndroid();

#elif PLATFORM_IOS

	return getSDKVersionOnIOS();

#endif
    
	return "";
}


void UPokktAdsFunctions::handleNativeMessage(FString operation, FString param)
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] message received! operation: %s, param: %s"), *operation, *param);

	TSharedPtr<FJsonObject> jsonObject = MakeShareable(new FJsonObject());
	TSharedRef< TJsonReader<> > jsonReader = TJsonReaderFactory<>::Create(param);

	if (!FJsonSerializer::Deserialize(jsonReader, jsonObject))
	{
		UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] failed to handle native message, error parsing param json!"));
		return;
	}

	FString screenName =		jsonObject->GetStringField("SCREEN_NAME");
	bool isRewarded = false;	jsonObject->TryGetBoolField("IS_REWARDED", isRewarded);
	FString adFormat = "";		jsonObject->TryGetStringField("AD_FORMAT", adFormat);
	FString args = "";			jsonObject->TryGetStringField("ARGS", args);

	// process valid operation
	if (adFormat.Equals(VIDEO_TYPE) == 0)
	{
		if (operation.Equals(EVENT_AD_CACHING_COMPLETED) == 0)
			UPokktAdsComponent::VideoAdCachingCompletedDelegate.Broadcast(screenName, isRewarded, args);
		
		else if (operation.Equals(EVENT_AD_CACHING_FAILED) == 0)
			UPokktAdsComponent::VideoAdCachingFailedDelegate.Broadcast(screenName, isRewarded, args);
		
		else if (operation.Equals(EVENT_AD_CLOSED) == 0)
			UPokktAdsComponent::VideoAdClosedDelegate.Broadcast(screenName, isRewarded);
		
		else if (operation.Equals(EVENT_AD_COMPLETED) == 0)
			UPokktAdsComponent::VideoAdCompletedDelegate.Broadcast(screenName, isRewarded);
		
		else if (operation.Equals(EVENT_AD_FAILED_TO_SHOW) == 0)
			UPokktAdsComponent::VideoAdFailedToShowDelegate.Broadcast(screenName, isRewarded, args);
		
		else if (operation.Equals(EVENT_AD_DISPLAYED) == 0)
			UPokktAdsComponent::VideoAdDisplayedDelegate.Broadcast(screenName, isRewarded);
        
        else if (operation.Equals(EVENT_AD_CLICKED) == 0)
            UPokktAdsComponent::VideoAdClickedDelegate.Broadcast(screenName, isRewarded);
        
        else if (operation.Equals(EVENT_AD_SKIPPED) == 0)
            UPokktAdsComponent::VideoAdSkippedDelegate.Broadcast(screenName, isRewarded);

		else if (operation.Equals(EVENT_AD_GRATIFIED) == 0)
			UPokktAdsComponent::VideoAdGratifiedDelegate.Broadcast(screenName, isRewarded, args);
	}
	else if (adFormat.Equals(INTERSTITIAL_TYPE) == 0)
	{
		if (operation.Equals(EVENT_AD_CACHING_COMPLETED) == 0)
			UPokktAdsComponent::InterstitialCachingCompletedDelegate.Broadcast(screenName, isRewarded, args);
		
		else if (operation.Equals(EVENT_AD_CACHING_FAILED) == 0)
			UPokktAdsComponent::InterstitialCachingFailedDelegate.Broadcast(screenName, isRewarded, args);
		
		else if (operation.Equals(EVENT_AD_CLOSED) == 0)
			UPokktAdsComponent::InterstitialClosedDelegate.Broadcast(screenName, isRewarded);
		
		else if (operation.Equals(EVENT_AD_COMPLETED) == 0)
			UPokktAdsComponent::InterstitialCompletedDelegate.Broadcast(screenName, isRewarded);
		
		else if (operation.Equals(EVENT_AD_FAILED_TO_SHOW) == 0)
			UPokktAdsComponent::InterstitialFailedToShowDelegate.Broadcast(screenName, isRewarded, args);
		
		else if (operation.Equals(EVENT_AD_DISPLAYED) == 0)
			UPokktAdsComponent::InterstitialDisplayedDelegate.Broadcast(screenName, isRewarded);
        
        else if (operation.Equals(EVENT_AD_CLICKED) == 0)
            UPokktAdsComponent::InterstitialClickedDelegate.Broadcast(screenName, isRewarded);
        
        else if (operation.Equals(EVENT_AD_SKIPPED) == 0)
            UPokktAdsComponent::InterstitialSkippedDelegate.Broadcast(screenName, isRewarded);

		else if (operation.Equals(EVENT_AD_GRATIFIED) == 0)
			UPokktAdsComponent::InterstitialGratifiedDelegate.Broadcast(screenName, isRewarded, args);
	}
	else if (operation.Equals(EVENT_BANNER_LOADED) == 0)
	{
		UPokktAdsComponent::BannerLoadedDelegate.Broadcast(screenName);
	}
    else if (operation.Equals(EVENT_BANNER_LOAD_FAILED) == 0)
    {
        UPokktAdsComponent::BannerLoadFailedDelegate.Broadcast(screenName, args);
    }
    else if (operation.Equals(EVENT_BANNER_CLICKED) == 0)
    {
        UPokktAdsComponent::BannerClickedDelegate.Broadcast(screenName);
    }

	// TODO: IGA
}


#if PLATFORM_ANDROID

#include "Android/AndroidJNI.h"
#include "AndroidApplication.h"


extern "C" void Java_com_pokkt_plugin_PokktNativeExtension_notifyFrameworkUE4(JNIEnv* env, jobject thiz, jstring operation, jstring param)
{
	FString operationString = FString(UTF8_TO_TCHAR(env->GetStringUTFChars(operation, 0)));
	FString paramString = FString(UTF8_TO_TCHAR(env->GetStringUTFChars(param, 0)));

	UPokktAdsFunctions::handleNativeMessage(operationString, paramString);
}

void notifyAndroid(FString operation, FString param)
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] notifying android of operation: %s, param: %s"), *operation, *param);

	JNIEnv* env = FAndroidApplication::GetJavaEnv();
	FAndroidApplication::CheckJavaException();
	if (!env)
	{
		return;
	}

	jclass classContainingMethod = FJavaWrapper::GameActivityClassID;
	if (classContainingMethod)
	{
		jmethodID methodToExecute = FJavaWrapper::FindMethod(env, classContainingMethod, "notifyNativeUE4", "(Ljava/lang/String;Ljava/lang/String;)V", false);
		FAndroidApplication::CheckJavaException();
		if (methodToExecute)
		{
			jstring joperation = env->NewStringUTF(TCHAR_TO_UTF8(*operation));
			jstring jparam = env->NewStringUTF(TCHAR_TO_UTF8(*param));

			FJavaWrapper::CallVoidMethod(env, FJavaWrapper::GameActivityThis, methodToExecute, joperation, jparam);
			FAndroidApplication::CheckJavaException();
		}
	}
}

bool isVideoAdCachedOnAndroid(FString screenName, bool isRewarded)
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] getting video ad cached for screen: %s on android device..."), *screenName);

	JNIEnv* env = FAndroidApplication::GetJavaEnv();
	FAndroidApplication::CheckJavaException();
	if (!env)
	{
		return false;
	}

	jclass classContainingMethod = FJavaWrapper::GameActivityClassID;
	if (classContainingMethod)
	{
		jmethodID methodToExecute = FJavaWrapper::FindMethod(env, classContainingMethod, "isVideoAdCachedOnNativeUE4", "(Ljava/lang/String;Z)Z", false);
		FAndroidApplication::CheckJavaException();
		if (methodToExecute)
		{
			jstring jScreenName = env->NewStringUTF(TCHAR_TO_UTF8(*screenName));
			bool result = FJavaWrapper::CallBooleanMethod(env, FJavaWrapper::GameActivityThis, methodToExecute, jScreenName, isRewarded);
			FAndroidApplication::CheckJavaException();

			UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] video ad caching status: %s!"), (result ? TEXT("true") : TEXT("false")));
			return result;
		}
	}

	return false;
}

bool isInterstitialCachedOnAndroid(FString screenName, bool isRewarded)
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] getting interstitial cached for screen: %s on android device..."), *screenName);

	JNIEnv* env = FAndroidApplication::GetJavaEnv();
	FAndroidApplication::CheckJavaException();
	if (!env)
	{
		return false;
	}

	jclass classContainingMethod = FJavaWrapper::GameActivityClassID;
	if (classContainingMethod)
	{
		jmethodID methodToExecute = FJavaWrapper::FindMethod(env, classContainingMethod, "isInterstitialCachedOnNativeUE4", "(Ljava/lang/String;Z)Z", false);
		FAndroidApplication::CheckJavaException();
		if (methodToExecute)
		{
			jstring jScreenName = env->NewStringUTF(TCHAR_TO_UTF8(*screenName));
			bool result = FJavaWrapper::CallBooleanMethod(env, FJavaWrapper::GameActivityThis, methodToExecute, jScreenName, isRewarded);
			FAndroidApplication::CheckJavaException();

			UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] interstitial caching status: %s!"), (result ? TEXT("true") : TEXT("false")));
			return result;
		}
	}

	return false;
}

FString getSDKVersionOnAndroid()
{
	UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] checking for sdk version on android device..."));

	JNIEnv* env = FAndroidApplication::GetJavaEnv();
	FAndroidApplication::CheckJavaException();
	if (!env)
	{
		return "not-found";
	}

	jclass classContainingMethod = FJavaWrapper::GameActivityClassID;
	if (classContainingMethod)
	{
		jmethodID methodToExecute = FJavaWrapper::FindMethod(env, classContainingMethod, "getSDKVersionOnNativeUE4", "()Ljava/lang/String;", false);
		FAndroidApplication::CheckJavaException();
		if (methodToExecute)
		{
			jstring jresult = (jstring)FJavaWrapper::CallObjectMethod(env, FJavaWrapper::GameActivityThis, methodToExecute);
			FAndroidApplication::CheckJavaException();
			FString result = FString(UTF8_TO_TCHAR(env->GetStringUTFChars(jresult, 0)));

			UE_LOG(LogPokktAds, Log, TEXT("[POKKT-UA] sdk version on android deivce currently is %s!"), *result);
			return result;
		}
	}

	return "not-found";
}


#elif PLATFORM_IOS

#import "PokktSDK.h"


static NSDictionary* _Nonnull operations = nil;
static BOOL delegateAssigned = false;
static PokktBannerView* _Nonnull bannerContainer = nil;

UIViewController* _Nullable getRootViewController();
NSDictionary* _Nullable extractJSON(NSString* _Nonnull jsonString);
PokktIAPDetails* _Nullable parseIAPDetails(NSString* _Nonnull detailsString);
PokktAdPlayerViewConfig* _Nullable getAdPlayerViewConfigFromJSONString(NSString* _Nonnull configString);
PokktUserInfo* _Nullable getUserDetailsFromJSONString(NSString* _Nonnull stringData);
PokktAnalyticsDetails* _Nullable getAnalyticsDetailsFromJSONString(NSString* _Nonnull stringData);
PokktConsentInfo* _Nullable getConsentInfoFromJSONString(NSString* _Nonnull stringData);
NSString* _Nullable stringifyJSONDictionary(NSDictionary* _Nonnull jsonObject);
NSString* _Nullable getReturnParamsFromValues(NSString* _Nonnull screenName,
                                             bool isRewarded,
                                             AdFormatType adFormat,
                                             NSMutableDictionary<NSString *, id>* _Nullable paramList);

// banner-util methods
void topLeft(UIView * _Nonnull view);
void topRight(UIView * _Nonnull view);
void topCenter(UIView * _Nonnull view);
void middleLeft(UIView* _Nonnull view);
void middleRight(UIView * _Nonnull view);
void middleCenter(UIView * _Nonnull view);
void bottomLeft(UIView * _Nonnull view);
void bottomRight(UIView * _Nonnull view);
void bottomCenter(UIView * _Nonnull view);


@interface PokktAdsAdapter : NSObject<PokktVideoAdsDelegate, PokktInterstitialDelegate, PokktBannerDelegate>

+ (void) processForOperation:(NSString* _Nonnull)operation withParams:(NSString* _Nonnull)params;

@end


void notifyIOS(FString operation, FString param)
{
    NSLog(@"[POKKT-OBJC] received from framework, operation: %@, with param: %@", operation.GetNSString(), param.GetNSString());
    
    if (!delegateAssigned)
    {
        PokktAdsAdapter *adapter = [[PokktAdsAdapter alloc] init];
        [PokktVideoAds setPokktVideoAdsDelegate:adapter];
        [PokktInterstitial setPokktInterstitialDelegate:adapter];
        [PokktBanner setPokktBannerDelegate:adapter];
        //[PokktInGameAd setIGADelegate:adapter];
        
        delegateAssigned = true;
    }
    
    NSLog(@"[POKKT-OBJC] processing...");
    [PokktAdsAdapter processForOperation:operation.GetNSString() withParams:param.GetNSString()];
}

bool isVideoAdCachedOnIOS(FString screenName, bool isRewarded)
{
    return [PokktVideoAds isAdCached:screenName.GetNSString() isRewarded:isRewarded];
}

bool isInterstitialCachedOnIOS(FString screenName, bool isRewarded)
{
	return [PokktInterstitial isAdCached:screenName.GetNSString() isRewarded:isRewarded];
}

FString getSDKVersionOnIOS()
{
    return FString([PokktAds getSDKVersion]);
}

void notifyFramework(NSString* _Nonnull operation, NSString* _Nonnull param)
{
    UPokktAdsFunctions::handleNativeMessage(FString(operation), FString(param));
}


@implementation PokktAdsAdapter

+ (void) processForOperation:(NSString* _Nonnull)operation withParams:(NSString* _Nonnull)params
{
    NSLog(@"[POKKT-OBJC] %@ operation requested: %@", operation, params);
    
    // Common APIs
    
    if ([@SET_POKKT_CONFIG_OP isEqualToString:operation])
    {
        NSDictionary *jsonObject = extractJSON(params);
        NSString *appId = jsonObject[@"appId"];
        NSString *securityKey = jsonObject[@"securityKey"];
        dispatch_async(dispatch_get_main_queue(), ^{
            [PokktAds setPokktConfigWithAppId:appId securityKey:securityKey];
        });
    }
    else if ([@SET_THIRDPARTY_ID_OP isEqualToString:operation])
    {
        [PokktAds setThirdPartyUserId:params];
    }
    else if ([@SET_PLAYER_VIEW_CONFIG_OP isEqualToString:operation])
    {
        [PokktAds setPokktAdPlayerViewConfig:getAdPlayerViewConfigFromJSONString(params)];
    }
    else if ([@SET_USER_DETAILS_OP isEqualToString:operation])
    {
        [PokktAds setUserDetails:getUserDetailsFromJSONString(params)];
    }
    else if ([@SET_CALLBACK_EXTRA_PARAMS_OP isEqualToString:operation])
    {
        NSDictionary *paramDict = extractJSON(params);
        [PokktAds setCallbackExtraParam:paramDict];
    }
    
    
    // Video Ads APIs
    else if ([@VIDEO_CACHE_REWARDED_OP isEqualToString:operation])
    {
        [PokktVideoAds cacheRewarded:params];
    }
    else if ([@VIDEO_SHOW_REWARDED_OP isEqualToString:operation])
    {
        [PokktVideoAds showRewarded:params withViewController:getRootViewController()];
    }
    else if ([@VIDEO_CACHE_NONREWARDED_OP isEqualToString:operation])
    {
        [PokktVideoAds cacheNonRewarded:params];
    }
    else if ([@VIDEO_SHOW_NONREWARDED_OP isEqualToString:operation])
    {
        [PokktVideoAds showNonRewarded:params withViewController:getRootViewController()];
    }
    
    
    // Interstitial APIs
    else if ([@INTERSTITIAL_CACHE_REWARDED_OP isEqualToString:operation])
    {
        [PokktInterstitial cacheRewarded:params];
    }
    else if ([@INTERSTITIAL_SHOW_REWARDED_OP isEqualToString:operation])
    {
        [PokktInterstitial showRewarded:params withViewController:getRootViewController()];
    }
    else if ([@INTERSTITIAL_CACHE_NONREWARDED_OP isEqualToString:operation])
    {
        [PokktInterstitial cacheNonRewarded:params];
    }
    else if ([@INTERSTITIAL_SHOW_NONREWARDED_OP isEqualToString:operation])
    {
        [PokktInterstitial showNonRewarded:params withViewController:getRootViewController()];
    }
    
    
    // Banner Ads APIs
    else if ([@BANNER_LOAD_OP isEqualToString:operation])
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSDictionary* jsonObject = extractJSON(params);
            NSString* screenName = jsonObject[@"screenName"];
            NSLog(@"banner Pos: %@", jsonObject[@"bannerPosition"]);
            int position = [jsonObject[@"bannerPosition"] intValue];
            NSLog(@"banner Pos int: %d", position);
            
            if (bannerContainer != NULL)
            {
                [PokktBanner destroyBanner:bannerContainer];
                bannerContainer = NULL;
            }
            
            bannerContainer = [[PokktBannerView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
            bannerContainer.translatesAutoresizingMaskIntoConstraints = NO;
            
            [getRootViewController().view addSubview:bannerContainer];
            
            switch (position)
            {
                case 1: topLeft(bannerContainer);       break;
                case 2: topCenter(bannerContainer);     break;
                case 3: topRight(bannerContainer);      break;
                case 4: middleLeft(bannerContainer);    break;
                case 5: middleCenter(bannerContainer);  break;
                case 6: middleRight(bannerContainer);   break;
                case 7: bottomLeft(bannerContainer);    break;
                case 8: bottomCenter(bannerContainer);  break;
                case 9: bottomRight(bannerContainer);   break;
                default: topCenter(bannerContainer);    break;
            }
            
            [PokktBanner loadBanner:bannerContainer withScreenName:screenName rootViewContorller:getRootViewController()];
        });
    }
    else if ([@BANNER_DESTROY_OP isEqualToString:operation])
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            [PokktBanner destroyBanner:bannerContainer];
        });
    }
    
    
    // Analytics APIs
    else if ([@ANALYTICS_SET_DETAILS_OP isEqualToString:operation])
    {
        [PokktAds setPokktAnalyticsDetail:getAnalyticsDetailsFromJSONString(params)];
    }
    else if ([@ANALYTICS_TRACK_IAP_OP isEqualToString:operation])
    {
        [PokktAds trackIAP:parseIAPDetails(params)];
    }
    
    
    // Debugging APIs
    else if ([@DEBUGGING_SHOULD_DEBUG_OP isEqualToString:operation])
    {
        [PokktDebugger setDebug:[params boolValue]];
    }
    else if ([@DEBUGGING_SHOW_LOG_OP isEqualToString:operation])
    {
        [PokktDebugger printLog:params];
    }
    else if ([@DEBUGGING_SHOW_TOAST_OP isEqualToString:operation])
    {
        [PokktDebugger showToast:params viewController:getRootViewController()];
    }
    else if ([@DEBUGGING_EXPORT_LOG_OP isEqualToString:operation])
    {
        [PokktDebugger exportLog:getRootViewController()];
    }
    
    // Data access consent
    else if ([@SET_DATA_ACCESS_CONSENT_OP isEqualToString:operation])
    {
        [PokktAds setPokktConsentInfo:getConsentInfoFromJSONString(params)];
    }
    
    else
    {
        NSLog(@"[POKKT-NATIVE] Unknown operation requested: %@", operation);
    }
}


/***
 * Video Ad Delegate
 ***/

- (void) videoAdCachingCompleted:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded reward: (float)reward
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = [[NSNumber numberWithFloat:reward] stringValue];
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, additionalParams);
    
    notifyFramework(@EVENT_AD_CACHING_COMPLETED, params);
}

- (void) videoAdCachingFailed:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded errorMessage:(NSString* _Nonnull) errorMessage
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = errorMessage;
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, additionalParams);
    
    notifyFramework(@EVENT_AD_CACHING_FAILED, params);
}

- (void) videoAdCompleted:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, nil);
    
    notifyFramework(@EVENT_AD_COMPLETED, params);
}

- (void) videoAdDisplayed:(NSString* _Nonnull) screenName isRewarded:(BOOL)isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, nil);
    
    notifyFramework(@EVENT_AD_DISPLAYED, params);
}

- (void) videoAdClicked:(NSString* _Nonnull)screenName isRewarded:(BOOL)isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, nil);
    
    notifyFramework(@EVENT_AD_CLICKED, params);
}

- (void) videoAdGratified:(NSString* _Nonnull) screenName reward:(float) reward
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = [[NSNumber numberWithFloat:reward] stringValue];
    NSString *params = getReturnParamsFromValues(screenName, true, VIDEO, additionalParams);
    
    notifyFramework(@EVENT_AD_GRATIFIED, params);
}

- (void) videoAdSkipped:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, nil);
    
    notifyFramework(@EVENT_AD_SKIPPED, params);
}

- (void) videoAdClosed:(NSString* _Nonnull) screenName isRewarded:(BOOL)isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, nil);
    
    notifyFramework(@EVENT_AD_CLOSED, params);
}

- (void) videoAdFailedToShow:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded errorMessage:(NSString* _Nonnull) errorMessage
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = errorMessage;
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, VIDEO, additionalParams);
    
    notifyFramework(@EVENT_AD_FAILED_TO_SHOW, params);
}


/***
 * Interstitial Delegate
 ***/

- (void) interstitialCachingCompleted:(NSString* _Nonnull)screenName isRewarded:(BOOL) isRewarded reward:(float) reward
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = [[NSNumber numberWithFloat:reward] stringValue];
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, additionalParams);
    
    notifyFramework(@EVENT_AD_CACHING_COMPLETED, params);
}

- (void) interstitialCachingFailed:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded errorMessage:(NSString* _Nonnull) errorMessage
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = errorMessage;
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, additionalParams);
    
    notifyFramework(@EVENT_AD_CACHING_FAILED, params);
}

- (void) interstitialCompleted:(NSString* _Nonnull) screenName isRewarded:(BOOL)isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, nil);
    
    notifyFramework(@EVENT_AD_COMPLETED, params);
}

- (void) interstitialDisplayed:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, nil);
    
    notifyFramework(@EVENT_AD_DISPLAYED, params);
}

- (void) interstitialClicked:(NSString* _Nonnull)screenName isRewarded:(BOOL)isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, nil);
    
    notifyFramework(@EVENT_AD_CLICKED, params);
}

- (void) interstitialGratified:(NSString* _Nonnull) screenName reward:(float) reward
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = [[NSNumber numberWithFloat:reward] stringValue];
    NSString *params = getReturnParamsFromValues(screenName, true, INTERSTITIAL, additionalParams);
    
    notifyFramework(@EVENT_AD_GRATIFIED, params);
}

- (void) interstitialSkipped:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, nil);
    
    notifyFramework(@EVENT_AD_SKIPPED, params);
}

- (void) interstitialClosed:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded
{
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, nil);
    
    notifyFramework(@EVENT_AD_CLOSED, params);
}

- (void) interstitialFailedToShow:(NSString* _Nonnull) screenName isRewarded:(BOOL) isRewarded errorMessage:(NSString* _Nonnull) errorMessage
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"ARGS"] = errorMessage;
    NSString *params = getReturnParamsFromValues(screenName, isRewarded, INTERSTITIAL, additionalParams);
    
    notifyFramework(@EVENT_AD_FAILED_TO_SHOW, params);
}


/***
 * Banner Ads Delegate
 ***/

- (void)bannerLoaded:(NSString* _Nonnull)screenName
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"SCREEN_NAME"] = screenName;
    NSString *params = stringifyJSONDictionary(additionalParams);
    
    notifyFramework(@EVENT_BANNER_LOADED, params);
}

- (void)bannerLoadFailed:(NSString* _Nonnull)screenName errorMessage:(NSString* _Nonnull)errorMessage
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"SCREEN_NAME"] = screenName;
    additionalParams[@"ARGS"] = errorMessage;
    NSString *params = stringifyJSONDictionary(additionalParams);
    
    notifyFramework(@EVENT_BANNER_LOAD_FAILED, params);
}

-(void)bannerClicked:(NSString* _Nonnull)screenName
{
    NSMutableDictionary *additionalParams = [[NSMutableDictionary alloc] init];
    additionalParams[@"SCREEN_NAME"] = screenName;
    NSString *params = stringifyJSONDictionary(additionalParams);
    
    notifyFramework(@EVENT_BANNER_CLICKED, params);
}

@end


/***
 *
 * UTILITIES
 *
 ***/

UIViewController* _Nullable getRootViewController()
{
    UIViewController *viewController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
    return viewController;
}

NSDictionary* _Nullable extractJSON(NSString* _Nonnull jsonString)
{
    NSError* jsonError;
    NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary* jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData
                                                               options:NSJSONReadingMutableContainers
                                                                 error:&jsonError];
    return jsonObject;
}

PokktIAPDetails* _Nonnull parseIAPDetails(NSString* _Nonnull detailsString)
{
    NSDictionary* jsonObject = extractJSON(detailsString);
    
    PokktIAPDetails* details = [[PokktIAPDetails alloc] init];
    
    details.productIdentifier = jsonObject[@"productId"];
    details.productPrice = [NSString stringWithFormat:@"%d", [jsonObject[@"price"] intValue]];
    details.productCurrency = jsonObject[@"currencyCode"];
    details.productTitle = jsonObject[@"title"];
    details.productDescription = jsonObject[@"description"];
    
    // TODO
    //detail.setPurchaseData(jsonObject.optString("purchaseData"));
    //detail.setPurchaseSignature(jsonObject.optString("purchaseSignature"));
    //detail.setPurchaseStore(IAPStoreType.valueOf(jsonObject.optString("purchaseStore")));
    
    return details;
}

PokktAdPlayerViewConfig* _Nonnull getAdPlayerViewConfigFromJSONString(NSString* _Nonnull configString)
{
    NSDictionary* jsonObject = extractJSON(configString);
    
    PokktAdPlayerViewConfig* config = [[PokktAdPlayerViewConfig alloc] init];
    
    config.shouldAllowSkip = [jsonObject[@"shouldAllowSkip"] boolValue];
    config.defaultSkipTime = [jsonObject[@"defaultSkipTime"] floatValue];
    config.skipConfirmMessage = jsonObject[@"skipConfirmMessage"];
    config.shouldAllowMute = [jsonObject[@"shouldAllowMute"] boolValue];
    config.shouldConfirmSkip = [jsonObject[@"shouldSkipConfirm"] boolValue];
    config.skipConfirmYesLabel = jsonObject[@"skipConfirmYesLabel"];
    config.skipConfirmNoLabel = jsonObject[@"skipConfirmNoLabel"];
    config.skipTimerMessage = jsonObject[@"skipTimerMessage"];
    config.incentiveMessage = jsonObject[@"incentiveMessage"];
    
    return config;
}

PokktUserInfo* _Nonnull getUserDetailsFromJSONString(NSString* _Nonnull stringData)
{
    NSDictionary *jsonObject = extractJSON(stringData);
    
    PokktUserInfo *info = [[PokktUserInfo alloc] init];
    
    info.name = jsonObject[@"name"];
    info.age = jsonObject[@"age"];
    info.sex = jsonObject[@"sex"];
    info.mobileNumber = jsonObject[@"mobileNo"];
    info.emailAddress = jsonObject[@"emailAddress"];
    info.location = jsonObject[@"location"];
    info.birthday = jsonObject[@"birthday"];
    info.maritalStatus = jsonObject[@"maritalStatus"];
    info.facebookId = jsonObject[@"facebookId"];
    info.twitterHandle = jsonObject[@"twitterHandle"];
    info.educationInformation = jsonObject[@"education"];
    info.nationality = jsonObject[@"nationality"];
    info.employmentStatus = jsonObject[@"employment"];
    info.maturityRating = jsonObject[@"maturityRating"];
    
    return info;
}

PokktAnalyticsDetails* _Nonnull getAnalyticsDetailsFromJSONString(NSString* _Nonnull stringData)
{
    NSDictionary *jsonObject = extractJSON(stringData);
    
    PokktAnalyticsDetails *details = [[PokktAnalyticsDetails alloc] init];
    
    // analytics
    details.googleTrackerID = jsonObject[@"googleAnalyticsID"];
    details.mixPanelTrackerID = jsonObject[@"mixPanelProjectToken"];
    details.flurryTrackerID = jsonObject[@"flurryApplicationKey"];
    
    if(jsonObject[@"selectedAnalyticsType"] != nil && ![jsonObject[@"selectedAnalyticsType"] isEqual:@""])
    {
        if ([jsonObject[@"selectedAnalyticsType"] isEqual:@"FLURRY"])
            details.eventType = FLURRY_ANALYTICS;
        
        else if ([jsonObject[@"selectedAnalyticsType"] isEqual:@"GOOGLE_ANALYTICS"])
            details.eventType = GOOGLE_ANALYTICS;
        
        else if ([jsonObject[@"selectedAnalyticsType"] isEqual:@"MIXPANNEL"])
            details.eventType = MIXPANNEL_ANALYTICS;
    }
    
    return details;
}

PokktConsentInfo* _Nonnull getConsentInfoFromJSONString(NSString* _Nonnull stringData)
{
    NSDictionary *jsonObject = extractJSON(stringData);
    
    PokktConsentInfo* info = [[PokktConsentInfo alloc] init];
    [info setIsGDPRApplicable:[jsonObject[@"GDPRApplicable"] boolValue]];
    [info setIsGDPRConsentAvailable:[jsonObject[@"GDPRConsentAvailable"] boolValue]];
    
    [PokktDebugger printLog:[NSString stringWithFormat:@"GDPRApplicable: %@, GDPRConsentAvailable: %@",
                             info.isGDPRApplicable ? @"true" : @"false", info.isGDPRConsentAvailable ? @"true" : @"false"]];
    
    return info;
}

NSString* _Nullable getReturnParamsFromValues(NSString* _Nonnull screenName,
                                    bool isRewarded,
                                    AdFormatType adFormat,
                                    NSMutableDictionary<NSString *, id>* _Nullable paramList)
{
    if (!paramList)
    {
        paramList = [[NSMutableDictionary alloc] init];
    }
    
    paramList[@"SCREEN_NAME"] = screenName;
    paramList[@"IS_REWARDED"] = isRewarded ? @"true" : @"false";;
    paramList[@"AD_FORMAT"] = [@(adFormat) stringValue];
    
    NSString *params = stringifyJSONDictionary(paramList);
    return params;
}

NSString* _Nullable stringifyJSONDictionary(NSDictionary* _Nonnull jsonObject)
{
    NSError *jsonError;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:&jsonError];
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    return jsonString;
}


/**
 * Banner Utils
 **/

NSLayoutConstraint* _Nonnull getLayoutConstraint(UIView* _Nonnull view, NSLayoutAttribute layoutAttribute1, NSLayoutRelation layoutRelation, UIView* _Nullable toItem, NSLayoutAttribute layoutAttribute2, CGFloat multiplier, CGFloat constant)
{
    return [NSLayoutConstraint constraintWithItem:view
                                        attribute:layoutAttribute1
                                        relatedBy:layoutRelation
                                           toItem:toItem
                                        attribute:layoutAttribute2
                                       multiplier:multiplier
                                         constant:constant];
}

void updateBannerLocation(UIView* _Nullable view, NSLayoutConstraint* _Nullable leading, NSLayoutConstraint* _Nullable bottom, NSLayoutConstraint* _Nullable trailing, NSLayoutConstraint* _Nullable height)
{
    [view addConstraint:trailing];
    [view.superview addConstraint:bottom];
    [view.superview addConstraint:leading];
    [view addConstraint:height];
}

//Top
void topLeft(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeLeft, NSLayoutRelationEqual, view.superview, NSLayoutAttributeLeft, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeTop, NSLayoutRelationEqual, view.superview, NSLayoutAttributeTop, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

void topRight(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeRight, NSLayoutRelationEqual, view.superview, NSLayoutAttributeRight, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeTop, NSLayoutRelationEqual, view.superview, NSLayoutAttributeTop, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

void topCenter(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeCenterX, NSLayoutRelationEqual, view.superview, NSLayoutAttributeCenterX, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeTop, NSLayoutRelationEqual, view.superview, NSLayoutAttributeTop, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

// Middle
void middleLeft(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeLeft, NSLayoutRelationEqual, view.superview, NSLayoutAttributeLeft, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeCenterY, NSLayoutRelationEqual, view.superview, NSLayoutAttributeCenterY, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

void middleRight(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeRight, NSLayoutRelationEqual, view.superview, NSLayoutAttributeRight, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeCenterY, NSLayoutRelationEqual, view.superview, NSLayoutAttributeCenterY, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

void middleCenter(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeCenterX, NSLayoutRelationEqual, view.superview, NSLayoutAttributeCenterX, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeCenterY, NSLayoutRelationEqual, view.superview, NSLayoutAttributeCenterY, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

//Bottom
void bottomLeft(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeLeading, NSLayoutRelationEqual, view.superview, NSLayoutAttributeLeading, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeBottom, NSLayoutRelationEqual, view.superview, NSLayoutAttributeBottom, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

void bottomRight(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeRight, NSLayoutRelationEqual, view.superview, NSLayoutAttributeRight, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeBottom, NSLayoutRelationEqual, view.superview, NSLayoutAttributeBottom, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

void bottomCenter(UIView* _Nonnull view)
{
    NSLayoutConstraint* leading = getLayoutConstraint(view, NSLayoutAttributeCenterX, NSLayoutRelationEqual, view.superview, NSLayoutAttributeCenterX, 1.0f, 0.f);
    NSLayoutConstraint* bottom = getLayoutConstraint(view, NSLayoutAttributeBottom, NSLayoutRelationEqual, view.superview, NSLayoutAttributeBottom, 1.0f, 0.f);
    NSLayoutConstraint* trailing = getLayoutConstraint(view, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeWidth, 1.0f, 320);
    NSLayoutConstraint* height = getLayoutConstraint(view, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 50);
    
    updateBannerLocation(view, leading, bottom, trailing, height);
}

#endif
