// Definitions.
import { NavigationType } from '../frame';
// Types.
import { View, IOSHelper } from '../core/view';
import { PageBase, actionBarHiddenProperty, statusBarStyleProperty } from './page-common';
import { profile } from '../../profiling';
import { layout } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
import { getLastFocusedViewOnPage, isAccessibilityServiceEnabled } from '../../accessibility';
import { SharedTransition } from '../transition/shared-transition';
export * from './page-common';
const ENTRY = '_entry';
const DELEGATE = '_delegate';
const TRANSITION = '_transition';
const NON_ANIMATED_TRANSITION = 'non-animated';
function isBackNavigationTo(page, entry) {
    const frame = page.frame;
    if (!frame) {
        return false;
    }
    // if executing context is null here this most probably means back navigation through iOS back button
    const navigationContext = frame._executingContext || {
        navigationType: NavigationType.back,
    };
    const isReplace = navigationContext.navigationType === NavigationType.replace;
    if (isReplace) {
        return false;
    }
    if (frame.navigationQueueIsEmpty()) {
        return true;
    }
    const queueContext = frame.getNavigationQueueContextByEntry(entry);
    return queueContext && queueContext.navigationType === NavigationType.back;
}
function isBackNavigationFrom(controller, page) {
    if (!page.frame) {
        return false;
    }
    // Controller is cleared or backstack skipped
    if (controller.isBackstackCleared || controller.isBackstackSkipped) {
        return false;
    }
    if (controller.navigationController && controller.navigationController.viewControllers.containsObject(controller)) {
        return false;
    }
    return true;
}
var UIViewControllerImpl = /** @class */ (function (_super) {
    __extends(UIViewControllerImpl, _super);
    function UIViewControllerImpl() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Object.defineProperty(UIViewControllerImpl.prototype, "isRunningLayout", {
        get: function () {
            return this._isRunningLayout !== 0;
        },
        enumerable: true,
        configurable: true
    });
    UIViewControllerImpl.prototype.startRunningLayout = function () {
        this._isRunningLayout++;
    };
    UIViewControllerImpl.prototype.finishRunningLayout = function () {
        this._isRunningLayout--;
        this.didFirstLayout = true;
    };
    UIViewControllerImpl.prototype.runLayout = function (cb) {
        try {
            this.startRunningLayout();
            cb();
        }
        finally {
            this.finishRunningLayout();
        }
    };
    UIViewControllerImpl.initWithOwner = function (owner) {
        var controller = UIViewControllerImpl.new();
        controller._owner = owner;
        controller._isRunningLayout = 0;
        controller.didFirstLayout = false;
        return controller;
    };
    UIViewControllerImpl.prototype.viewDidLoad = function () {
        _super.prototype.viewDidLoad.call(this);
        // Unify translucent and opaque bars layout
        // this.edgesForExtendedLayout = UIRectEdgeBottom;
        this.extendedLayoutIncludesOpaqueBars = true;
    };
    UIViewControllerImpl.prototype.viewWillAppear = function (animated) {
        var _a;
        _super.prototype.viewWillAppear.call(this, animated);
        var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
        if (!owner) {
            return;
        }
        var frame = this.navigationController ? this.navigationController.owner : null;
        var newEntry = this[ENTRY];
        // Don't raise event if currentPage was showing modal page.
        if (!owner._presentedViewController && newEntry && (!frame || frame.currentPage !== owner)) {
            var isBack = isBackNavigationTo(owner, newEntry);
            owner.onNavigatingTo(newEntry.entry.context, isBack, newEntry.entry.bindingContext);
        }
        if (frame) {
            frame._resolvedPage = owner;
            if (owner.parent === frame) {
                frame._inheritStyles(owner);
            }
            else {
                if (!owner.parent) {
                    if (!frame._styleScope) {
                        // Make sure page will have styleScope even if frame don't.
                        owner._updateStyleScope();
                    }
                    frame._addView(owner);
                }
                else {
                    throw new Error('Page is already shown on another frame.');
                }
            }
            frame._updateActionBar(owner);
        }
        // Set autoAdjustScrollInsets in will appear - as early as possible
        IOSHelper.updateAutoAdjustScrollInsets(this, owner);
        // Pages in backstack are unloaded so raise loaded here.
        if (!owner.isLoaded) {
            owner.callLoaded();
        }
        else {
            // Note: Handle the case of canceled backstack navigation. (https://github.com/NativeScript/NativeScript/issues/7430)
            // In this case viewWillAppear will be executed for the previous page and it will change the ActionBar
            // because changes happen in an interactive transition - IOS will animate between the the states.
            // If canceled - viewWillAppear will be called for the current page(which is already loaded) and we need to
            // update the action bar explicitly, so that it is not left styles as the previous page.
            owner.updateWithWillAppear(animated);
        }
    };
    UIViewControllerImpl.prototype.viewDidAppear = function (animated) {
        var _a, _b;
        _super.prototype.viewDidAppear.call(this, animated);
        var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
        if (!owner) {
            return;
        }
        var navigationController = this.navigationController;
        var frame = navigationController ? navigationController.owner : null;
        // Skip navigation events if modal page is shown.
        if (!owner._presentedViewController && frame) {
            var newEntry = this[ENTRY];
            // frame.setCurrent(...) will reset executing context so retrieve it here
            // if executing context is null here this most probably means back navigation through iOS back button
            var navigationContext = frame._executingContext || {
                navigationType: NavigationType.back,
            };
            var isReplace = navigationContext.navigationType === NavigationType.replace;
            frame.setCurrent(newEntry, navigationContext.navigationType);
            if (isReplace) {
                var controller = newEntry.resolvedPage.ios;
                if (controller) {
                    var animated_1 = frame._getIsAnimatedNavigation(newEntry.entry);
                    if (animated_1) {
                        controller[TRANSITION] = frame._getNavigationTransition(newEntry.entry);
                    }
                    else {
                        controller[TRANSITION] = {
                            name: NON_ANIMATED_TRANSITION,
                        };
                    }
                }
            }
            // If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.
            if ((_b = frame.ios) === null || _b === void 0 ? void 0 : _b.controller) {
                frame.ios.controller.delegate = this[DELEGATE];
            }
            frame._processNavigationQueue(owner);
            if (!__VISIONOS__) {
                // _processNavigationQueue will shift navigationQueue. Check canGoBack after that.
                // Workaround for disabled backswipe on second custom native transition
                if (frame.canGoBack()) {
                    var transitionState = SharedTransition.getState(owner.transitionId);
                    if (!(transitionState === null || transitionState === void 0 ? void 0 : transitionState.interactive)) {
                        // only consider when interactive transitions are not enabled
                        navigationController.interactivePopGestureRecognizer.delegate = navigationController;
                        navigationController.interactivePopGestureRecognizer.enabled = owner.enableSwipeBackNavigation;
                    }
                }
                else {
                    navigationController.interactivePopGestureRecognizer.enabled = false;
                }
            }
        }
        if (!this.presentedViewController) {
            // clear presented viewController here only if no presented controller.
            // this is needed because in iOS9 the order of events could be - willAppear, willDisappear, didAppear.
            // If we clean it when we have viewController then once presented VC is dismissed then
            owner._presentedViewController = null;
        }
    };
    UIViewControllerImpl.prototype.viewWillDisappear = function (animated) {
        var _a;
        _super.prototype.viewWillDisappear.call(this, animated);
        var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
        if (!owner) {
            return;
        }
        // Cache presentedViewController if any. We don't want to raise
        // navigation events in case of presenting view controller.
        if (!owner._presentedViewController) {
            owner._presentedViewController = this.presentedViewController;
        }
        var frame = owner.frame;
        // Skip navigation events if we are hiding because we are about to show a modal page,
        // or because we are closing a modal page,
        // or because we are in tab and another controller is selected.
        var tab = this.tabBarController;
        if (owner.onNavigatingFrom && !owner._presentedViewController && frame && (!this.presentingViewController || frame.backStack.length > 0) && frame.currentPage === owner) {
            var willSelectViewController = tab && tab._willSelectViewController;
            if (!willSelectViewController || willSelectViewController === tab.selectedViewController) {
                var isBack = isBackNavigationFrom(this, owner);
                owner.onNavigatingFrom(isBack);
            }
        }
        owner.updateWithWillDisappear(animated);
    };
    UIViewControllerImpl.prototype.viewDidDisappear = function (animated) {
        var _a;
        _super.prototype.viewDidDisappear.call(this, animated);
        var page = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
        // Exit if no page or page is hiding because it shows another page modally.
        if (!page || page.modal || page._presentedViewController) {
            return;
        }
        // Forward navigation does not remove page from frame so we raise unloaded manually.
        if (page.isLoaded) {
            page.callUnloaded();
        }
    };
    UIViewControllerImpl.prototype.viewWillLayoutSubviews = function () {
        var _a;
        _super.prototype.viewWillLayoutSubviews.call(this);
        var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
        if (owner) {
            IOSHelper.updateConstraints(this, owner);
        }
    };
    UIViewControllerImpl.prototype.viewSafeAreaInsetsDidChange = function () {
        var _this = this;
        var _a;
        _super.prototype.viewSafeAreaInsetsDidChange.call(this);
        if (this.isRunningLayout || !this.didFirstLayout) {
            return;
        }
        var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
        if (owner) {
            this.runLayout(function () { return IOSHelper.layoutView(_this, owner); });
        }
    };
    UIViewControllerImpl.prototype.viewDidLayoutSubviews = function () {
        var _a;
        this.startRunningLayout();
        _super.prototype.viewDidLayoutSubviews.call(this);
        var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
        if (owner) {
            // layout(owner.actionBar)
            // layout(owner.content)
            if (SDK_VERSION >= 11) {
                // Handle nested Page safe area insets application.
                // A Page is nested if its Frame has a parent.
                // If the Page is nested, cross check safe area insets on top and bottom with Frame parent.
                var frame = owner.parent;
                // There is a legacy scenario where Page is not in a Frame - the root of a Modal View, so it has no parent.
                var frameParent = frame && frame.parent;
                // Handle Angular scenario where TabView is in a ProxyViewContainer
                // It is possible to wrap components in ProxyViewContainers indefinitely
                // Not using instanceof ProxyViewContainer to avoid circular dependency
                // TODO: Try moving UIViewControllerImpl out of page module
                while (frameParent && !frameParent.nativeViewProtected) {
                    frameParent = frameParent.parent;
                }
                if (frameParent) {
                    var parentPageInsetsTop = frameParent.nativeViewProtected.safeAreaInsets.top;
                    var parentPageInsetsBottom = frameParent.nativeViewProtected.safeAreaInsets.bottom;
                    var currentInsetsTop = this.view.safeAreaInsets.top;
                    var currentInsetsBottom = this.view.safeAreaInsets.bottom;
                    // Safe area insets include additional safe area insets too, so subtract old values
                    if (this.additionalSafeAreaInsets) {
                        currentInsetsTop -= this.additionalSafeAreaInsets.top;
                        currentInsetsBottom -= this.additionalSafeAreaInsets.bottom;
                    }
                    var additionalInsetsTop = Math.max(parentPageInsetsTop - currentInsetsTop, 0);
                    var additionalInsetsBottom = Math.max(parentPageInsetsBottom - currentInsetsBottom, 0);
                    if (additionalInsetsTop > 0 || additionalInsetsBottom > 0) {
                        var additionalInsets = new UIEdgeInsets({
                            top: additionalInsetsTop,
                            left: 0,
                            bottom: additionalInsetsBottom,
                            right: 0,
                        });
                        this.additionalSafeAreaInsets = additionalInsets;
                    }
                    else {
                        this.additionalSafeAreaInsets = null;
                    }
                }
            }
            IOSHelper.layoutView(this, owner);
        }
        this.finishRunningLayout();
    };
    // Mind implementation for other controllerss
    UIViewControllerImpl.prototype.traitCollectionDidChange = function (previousTraitCollection) {
        var _a;
        _super.prototype.traitCollectionDidChange.call(this, previousTraitCollection);
        if (SDK_VERSION >= 13) {
            var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
            if (owner && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) {
                owner.notify({
                    eventName: IOSHelper.traitCollectionColorAppearanceChangedEvent,
                    object: owner,
                });
            }
        }
    };
    Object.defineProperty(UIViewControllerImpl.prototype, "preferredStatusBarStyle", {
        // TODO: a11y
        // public accessibilityPerformEscape() {
        // 	const owner = this._owner.get();
        // 	if (!owner) {
        // 		return false;
        // 	}
        // 	console.log('page accessibilityPerformEscape');
        // 	if (owner.onAccessibilityPerformEscape) {
        // 		const result = owner.onAccessibilityPerformEscape();
        // 		return result;
        // 	} else {
        // 		return false;
        // 	}
        // }
        // @ts-ignore
        get: function () {
            var _a;
            var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
            if (owner) {
                return owner.statusBarStyle === 'dark' ? UIStatusBarStyle.LightContent : UIStatusBarStyle.Default;
            }
            else {
                return UIStatusBarStyle.Default;
            }
        },
        enumerable: true,
        configurable: true
    });
    __decorate([
        profile,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Boolean]),
        __metadata("design:returntype", void 0)
    ], UIViewControllerImpl.prototype, "viewDidAppear", null);
    __decorate([
        profile,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Boolean]),
        __metadata("design:returntype", void 0)
    ], UIViewControllerImpl.prototype, "viewWillDisappear", null);
    __decorate([
        profile,
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Boolean]),
        __metadata("design:returntype", void 0)
    ], UIViewControllerImpl.prototype, "viewDidDisappear", null);
    return UIViewControllerImpl;
}(UIViewController));
export class Page extends PageBase {
    constructor() {
        super();
        this._backgroundColor = SDK_VERSION <= 12 && !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
        const controller = UIViewControllerImpl.initWithOwner(new WeakRef(this));
        controller.view.backgroundColor = this._backgroundColor;
        this.viewController = this._ios = controller;
    }
    createNativeView() {
        return this.viewController.view;
    }
    disposeNativeView() {
        this.viewController = null;
        this._ios = null;
        super.disposeNativeView();
    }
    // @ts-ignore
    get ios() {
        return this._ios;
    }
    layoutNativeView(left, top, right, bottom) {
        //
    }
    _setNativeViewFrame(nativeView, frame) {
        //
    }
    _shouldDelayLayout() {
        return this.frame && this.frame._animationInProgress;
    }
    onLoaded() {
        super.onLoaded();
        if (this.hasActionBar) {
            this.actionBar.update();
        }
    }
    updateWithWillAppear(animated) {
        // this method is important because it allows plugins to react to modal page close
        // for example allowing updating status bar background color
        if (this.hasActionBar) {
            this.actionBar.update();
        }
        this.updateStatusBar();
    }
    updateWithWillDisappear(animated) {
        // this method is important because it allows plugins to react to modal page close
        // for example allowing updating status bar background color
    }
    updateStatusBar() {
        this._updateStatusBarStyle(this.statusBarStyle);
    }
    _updateStatusBarStyle(value) {
        const frame = this.frame;
        if (frame && value) {
            const navigationController = frame.ios.controller;
            const navigationBar = navigationController.navigationBar;
            navigationBar.barStyle = value === 'dark' ? 1 /* UIBarStyle.Black */ : 0 /* UIBarStyle.Default */;
        }
    }
    _updateEnableSwipeBackNavigation(enabled) {
        const navController = this._ios.navigationController;
        if (this.frame && navController && navController.interactivePopGestureRecognizer) {
            // Make sure we don't set true if cannot go back
            enabled = enabled && this.frame.canGoBack();
            navController.interactivePopGestureRecognizer.enabled = enabled;
        }
    }
    onMeasure(widthMeasureSpec, heightMeasureSpec) {
        const width = layout.getMeasureSpecSize(widthMeasureSpec);
        const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
        const height = layout.getMeasureSpecSize(heightMeasureSpec);
        const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
        if (this.hasActionBar && this.frame && this.frame._getNavBarVisible(this)) {
            const { width, height } = this.actionBar._getActualSize;
            const widthSpec = layout.makeMeasureSpec(width, layout.EXACTLY);
            const heightSpec = layout.makeMeasureSpec(height, layout.EXACTLY);
            View.measureChild(this, this.actionBar, widthSpec, heightSpec);
        }
        const result = View.measureChild(this, this.layoutView, widthMeasureSpec, heightMeasureSpec);
        const measureWidth = Math.max(result.measuredWidth, this.effectiveMinWidth);
        const measureHeight = Math.max(result.measuredHeight, this.effectiveMinHeight);
        const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
        const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
        this.setMeasuredDimension(widthAndState, heightAndState);
    }
    onLayout(left, top, right, bottom) {
        if (this.hasActionBar) {
            const { width: actionBarWidth, height: actionBarHeight } = this.actionBar._getActualSize;
            View.layoutChild(this, this.actionBar, 0, 0, actionBarWidth, actionBarHeight);
        }
        const insets = this.getSafeAreaInsets();
        if (!__VISIONOS__ && SDK_VERSION <= 10) {
            // iOS 10 and below don't have safe area insets API,
            // there we need only the top inset on the Page
            insets.top = layout.round(layout.toDevicePixels(this.viewController.view.safeAreaLayoutGuide.layoutFrame.origin.y));
        }
        const childLeft = 0 + insets.left;
        const childTop = 0 + insets.top;
        const childRight = right - insets.right;
        const childBottom = bottom - insets.bottom;
        View.layoutChild(this, this.layoutView, childLeft, childTop, childRight, childBottom);
    }
    _addViewToNativeVisualTree(child, atIndex) {
        // ActionBar is handled by the UINavigationController
        if (this.hasActionBar && child === this.actionBar) {
            return true;
        }
        const nativeParent = this.nativeViewProtected;
        const nativeChild = child.nativeViewProtected;
        const viewController = child.ios instanceof UIViewController ? child.ios : child.viewController;
        if (viewController) {
            // Adding modal controllers to as child will make app freeze.
            if (this.viewController.presentedViewController === viewController) {
                return true;
            }
            this.viewController.addChildViewController(viewController);
        }
        if (nativeParent && nativeChild) {
            if (typeof atIndex !== 'number' || atIndex >= nativeParent.subviews.count) {
                nativeParent.addSubview(nativeChild);
            }
            else {
                nativeParent.insertSubviewAtIndex(nativeChild, atIndex);
            }
            return true;
        }
        return false;
    }
    _removeViewFromNativeVisualTree(child) {
        // ActionBar is handled by the UINavigationController
        if (this.hasActionBar && child === this.actionBar) {
            return;
        }
        const viewController = child.ios instanceof UIViewController ? child.ios : child.viewController;
        if (viewController) {
            viewController.removeFromParentViewController();
        }
        super._removeViewFromNativeVisualTree(child);
    }
    [actionBarHiddenProperty.setNative](value) {
        this._updateEnableSwipeBackNavigation(value);
        // Invalidate all inner controller.
        invalidateTopmostController(this.viewController);
        const frame = this.frame;
        if (frame) {
            // Update nav-bar visibility with disabled animations
            frame._updateActionBar(this, true);
        }
    }
    [statusBarStyleProperty.getDefault]() {
        return 0 /* UIBarStyle.Default */;
    }
    [statusBarStyleProperty.setNative](value) {
        const frame = this.frame;
        if (frame) {
            const navigationBar = frame.ios.controller.navigationBar;
            if (typeof value === 'string') {
                navigationBar.barStyle = value === 'dark' ? 1 /* UIBarStyle.Black */ : 0 /* UIBarStyle.Default */;
            }
            else {
                navigationBar.barStyle = value;
            }
        }
    }
    accessibilityScreenChanged(refocus = false) {
        if (!isAccessibilityServiceEnabled()) {
            return;
        }
        if (refocus) {
            const lastFocusedView = getLastFocusedViewOnPage(this);
            if (lastFocusedView) {
                const uiView = lastFocusedView.nativeViewProtected;
                if (uiView) {
                    UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, uiView);
                    return;
                }
            }
        }
        if (this.actionBarHidden) {
            UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, this.nativeViewProtected);
            return;
        }
        if (this.accessibilityLabel) {
            UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, this.nativeViewProtected);
            return;
        }
        if (this.actionBar.accessibilityLabel || this.actionBar.title) {
            UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, this.actionBar.nativeView);
            return;
        }
        UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, this.nativeViewProtected);
    }
}
function invalidateTopmostController(controller) {
    if (!controller) {
        return;
    }
    controller.view.setNeedsLayout();
    const presentedViewController = controller.presentedViewController;
    if (presentedViewController) {
        return invalidateTopmostController(presentedViewController);
    }
    const childControllers = controller.childViewControllers;
    let size = controller.childViewControllers.count;
    while (size > 0) {
        const childController = childControllers[--size];
        if (childController instanceof UITabBarController) {
            invalidateTopmostController(childController.selectedViewController);
        }
        else if (childController instanceof UINavigationController) {
            invalidateTopmostController(childController.topViewController);
        }
        else if (childController instanceof UISplitViewController) {
            invalidateTopmostController(childController.viewControllers.lastObject);
        }
        else {
            invalidateTopmostController(childController);
        }
    }
}
//# sourceMappingURL=index.ios.js.map