//
//  RNNSwizzles.m
//  ReactNativeNavigation
//
//  Created by Leo Natan (Wix) on 1/17/18.
//  Copyright © 2018 Wix. All rights reserved.
//

#import "RNNSwizzles.h"
@import ObjectiveC;
@import UIKit;

static id (*__SWZ_initWithEventDispatcher_orig)(id self, SEL _cmd, id eventDispatcher);
static void (*__SWZ_setFrame_orig)(id self, SEL _cmd, CGRect frame);

static void __RNN_setFrame_orig(UIScrollView* self, SEL _cmd, CGRect frame)
{
	CGPoint originalOffset = self.contentOffset;
	
	__SWZ_setFrame_orig(self, _cmd, frame);
	
	UIEdgeInsets contentInset;
	if (@available(iOS 11.0, *)) {
		contentInset = self.adjustedContentInset;
	} else {
		contentInset = self.contentInset;
	}
	
	CGSize contentSize = self.contentSize;
	
	// If contentSize has not been measured yet we can't check bounds.
	if (CGSizeEqualToSize(contentSize, CGSizeZero))
	{
		self.contentOffset = originalOffset;
	}
	else
	{
		// Make sure offset don't exceed bounds. This could happen on screen rotation.
		CGSize boundsSize = self.bounds.size;
		self.contentOffset = CGPointMake(MAX(-contentInset.left, MIN(contentSize.width - boundsSize.width + contentInset.right, originalOffset.x)),
										 MAX(-contentInset.top, MIN(contentSize.height - boundsSize.height + contentInset.bottom, originalOffset.y)));
	}
}

@implementation RNNSwizzles

#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3
- (id)__swz_initWithEventDispatcher:(id)eventDispatcher
{
	id returnValue = __SWZ_initWithEventDispatcher_orig(self, _cmd, eventDispatcher);
	
	if (@available(iOS 11.0, *)) {
		[(UIScrollView*)[returnValue valueForKey:@"scrollView"] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentScrollableAxes];
	}
	
	return returnValue;
}

+ (void)load
{
	Class cls = NSClassFromString(@"RCTScrollView");
	if(cls == NULL)
	{
		return;
	}
	Method m1 = class_getInstanceMethod(cls, NSSelectorFromString(@"initWithEventDispatcher:"));
	
	if(m1 == NULL)
	{
		return;
	}
	
	__SWZ_initWithEventDispatcher_orig = (void*)method_getImplementation(m1);
	Method m2 = class_getInstanceMethod([RNNSwizzles class], NSSelectorFromString(@"__swz_initWithEventDispatcher:"));
	method_exchangeImplementations(m1, m2);
	
	if (@available(iOS 11.0, *)) {
		cls = NSClassFromString(@"RCTCustomScrollView");
		if(cls == NULL)
		{
			return;
		}
			
		m1 = class_getInstanceMethod(cls, @selector(setFrame:));
		__SWZ_setFrame_orig = (void*)method_getImplementation(m1);
		method_setImplementation(m1, (IMP)__RNN_setFrame_orig);
	}
}
#endif

@end

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */

@interface UIView (OS10SafeAreaSupport) @end

@implementation UIView (OS10SafeAreaSupport)

- (UIEdgeInsets)_ln_safeAreaInsets
{
	static NSString* const b64 = @"X3ZpZXdDb250cm9sbGVyRm9yQW5jZXN0b3I=";
	UIViewController* vc = [self valueForKey:[[NSString alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:b64 options:0] encoding:NSUTF8StringEncoding]];
	if(vc == nil)
	{
		return UIEdgeInsetsZero;
	}
	
	CGRect myFrameInVCView = [vc.view convertRect:self.bounds fromView:self];
	
	UIEdgeInsets rv = UIEdgeInsetsZero;
	rv.top = CGRectIntersection(myFrameInVCView, CGRectMake(0, 0, vc.view.bounds.size.width, vc.topLayoutGuide.length)).size.height;
	rv.bottom = CGRectIntersection(myFrameInVCView, CGRectMake(0, vc.view.bounds.size.height - vc.bottomLayoutGuide.length, vc.view.bounds.size.width, vc.bottomLayoutGuide.length)).size.height;
	
	return rv;
}

- (void)_ln_triggerSafeAreaInsetsDidChange
{
	if([self respondsToSelector:@selector(safeAreaInsetsDidChange)])
	{
		[self performSelector:@selector(safeAreaInsetsDidChange)];
	}
}

- (void)_ln_layoutSubviews
{
	[self _ln_triggerSafeAreaInsetsDidChange];
	
	struct objc_super super = {.receiver = self, .super_class = class_getSuperclass(object_getClass(self))};
	void (*super_class)(struct objc_super*, SEL) = (void*)objc_msgSendSuper;
	super_class(&super, _cmd);
}

- (void)_ln_setFrame:(CGRect)frame
{
	[self _ln_triggerSafeAreaInsetsDidChange];
	
	struct objc_super super = {.receiver = self, .super_class = class_getSuperclass(object_getClass(self))};
	void (*super_class)(struct objc_super*, SEL, CGRect) = (void*)objc_msgSendSuper;
	super_class(&super, _cmd, frame);
}

- (void)_ln_setCenter:(CGPoint)center
{
	[self _ln_triggerSafeAreaInsetsDidChange];
	
	struct objc_super super = {.receiver = self, .super_class = class_getSuperclass(object_getClass(self))};
	void (*super_class)(struct objc_super*, SEL, CGPoint) = (void*)objc_msgSendSuper;
	super_class(&super, _cmd, center);
}

- (void)_ln_setBounds:(CGRect)bounds
{
	[self _ln_triggerSafeAreaInsetsDidChange];
	
	struct objc_super super = {.receiver = self, .super_class = class_getSuperclass(object_getClass(self))};
	void (*super_class)(struct objc_super*, SEL, CGRect) = (void*)objc_msgSendSuper;
	super_class(&super, _cmd, bounds);
}

+ (void)load
{
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		if(NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11)
		{
			Class cls = NSClassFromString(@"RCTSafeAreaView");
			if(cls == NULL)
			{
				return;
			}
			
			Method m = class_getInstanceMethod([UIView class], @selector(_ln_safeAreaInsets));
			if(NO == class_addMethod(cls, @selector(safeAreaInsets), method_getImplementation(m), method_getTypeEncoding(m)))
			{
				return;
			}
			
			m = class_getInstanceMethod([UIView class], @selector(_ln_layoutSubviews));
			if(NO == class_addMethod(cls, @selector(layoutSubviews), method_getImplementation(m), method_getTypeEncoding(m)))
			{
				return;
			}
			
			m = class_getInstanceMethod([UIView class], @selector(_ln_setFrame:));
			if(NO == class_addMethod(cls, @selector(setFrame:), method_getImplementation(m), method_getTypeEncoding(m)))
			{
				return;
			}
			
			m = class_getInstanceMethod([UIView class], @selector(_ln_setCenter:));
			if(NO == class_addMethod(cls, @selector(setCenter:), method_getImplementation(m), method_getTypeEncoding(m)))
			{
				return;
			}
			
			m = class_getInstanceMethod([UIView class], @selector(_ln_setBounds:));
			if(NO == class_addMethod(cls, @selector(setBounds:), method_getImplementation(m), method_getTypeEncoding(m)))
			{
				return;
			}
		}
	});
}

@end

#endif
