//
//  NavigationController.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 10/24/19.
//  Copyright © 2019 Verizon Wireless. All rights reserved.
//

import UIKit

@objcMembers open class NavigationController: UINavigationController, MVMCoreViewManagerViewControllerProtocol {
    public var separatorView: Line?
    public weak var manager: (UIViewController & MVMCoreViewManagerProtocol)?

    /// Getter for the main navigation controller
    public static func navigationController() -> Self? {
        return MVMCoreActionUtility.initializerClassCheck(MVMCoreUISession.sharedGlobal()?.navigationController, classToVerify: self) as? Self
    }
    
    /// Provides MVM styling to the navigation bar. Returns a reference to the line.
    public static func style(_ navigationBar: UINavigationBar) -> Line {
        navigationBar.backgroundColor = .white
        navigationBar.shadowImage = UIImage()
        navigationBar.isOpaque = true
        navigationBar.tintColor = .black
        navigationBar.titleTextAttributes = [NSAttributedString.Key.font: MFStyler.fontBoldBodySmall(false)];
        return Line(pinTo: navigationBar, edge: .bottom, useMargin: false)
    }
    
    /// Sets up the application with a navigation controller
    public static func setupNavigationController() -> Self? {
        let navigationController = self.init()
        navigationController.separatorView = style(navigationController.navigationBar)
        MVMCoreUISession.sharedGlobal()?.navigationController = navigationController
        MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController
        MVMCoreNavigationHandler.shared()?.navigationController = navigationController
        MVMCoreNavigationHandler.shared()?.addDelegate(navigationController)
        return navigationController
    }
    
    /// Sets up the application with a navigation controller as the main container.
    public static func setupNavigationControllerAsMainController() -> Self? {
        guard let navigationController = setupNavigationController() else { return nil }
        MVMCoreUISession.sharedGlobal()?.setup(asStandardLoadViewDelegate: navigationController)
        return navigationController
    }
    
    /// Convenience function for setting the navigation item.
    public static func setNavigationItem(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) {
        viewController.navigationItem.title = navigationItemModel.title
        viewController.navigationItem.accessibilityLabel = navigationItemModel.title
        viewController.navigationItem.hidesBackButton = navigationItemModel.hidesSystemBackButton
        viewController.navigationItem.leftItemsSupplementBackButton = !navigationItemModel.hidesSystemBackButton
        setNavigationButtons(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController)
        setNavigationTitleView(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: viewController)
    }
    
    /// Convenience function for setting the navigation buttons.
    public static func setNavigationButtons(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) {
        let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
        var leftItems: [UIBarButtonItem] = []
        if navigationItemModel.hidesSystemBackButton,
           navigationItemModel.alwaysShowBackButton != false {
            if let backButtonModel = navigationItemModel.backButton,
               MVMCoreNavigationHandler.shared()?.getViewControllers(for: navigationController)?.count ?? 0 > 1 || navigationItemModel.alwaysShowBackButton ?? false {
                leftItems.append(backButtonModel.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
            }
            if let leftItemModels = navigationItemModel.additionalLeftButtons {
                for item in leftItemModels {
                    leftItems.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
                }
            }
        }
        viewController.navigationItem.leftBarButtonItems = leftItems.count > 0 ? leftItems : nil

        var rightItems: [UIBarButtonItem] = []
        if let rightItemModels = navigationItemModel.additionalRightButtons {
            for item in rightItemModels {
                rightItems.append(item.createNavigationItemButton(delegateObject: delegate, additionalData: nil))
            }
        }
        viewController.navigationItem.rightBarButtonItems = rightItems.count > 0 ? rightItems : nil
    }
    
    /// Convenience function for setting the navigation bar ui, except for the buttons.
    public static func setNavigationBarUI(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol, viewController: UIViewController) {
        navigationController.setNavigationBarHidden(navigationItemModel.hidden, animated: true)
        navigationController.navigationBar.barTintColor = navigationItemModel.backgroundColor?.uiColor ?? .white
        
        let tint = navigationItemModel.tintColor.uiColor
        navigationController.navigationBar.tintColor = tint
        
        // Have the navigation title match the tint color
        navigationController.navigationBar.titleTextAttributes?.updateValue(tint, forKey: .foregroundColor)
                
        // Update line.
        if let navigationController = navigationController as? NavigationController {
            navigationController.separatorView?.isHidden = navigationItemModel.line?.type ?? .standard == .none
        }
    }

    /// Convenience function for setting the navigation titleView.
    public static func setNavigationTitleView(navigationController: UINavigationController, navigationItemModel: NavigationItemModelProtocol?, viewController: UIViewController) {
        let delegate = (viewController as? MVMCoreViewControllerProtocol)?.delegateObject?() as? MVMCoreUIDelegateObject
        if let titleViewModel = navigationItemModel?.titleView, let molecule = ModelRegistry.createMolecule(titleViewModel, delegateObject: delegate, additionalData: nil) {
            viewController.navigationItem.titleView = molecule
        }
    }
    
    /// Convenience function to return the navigation model of the lowest controller traversing managers if applicable.
    public func getNavigationModel(from viewController: UIViewController) -> NavigationItemModelProtocol? {
        return (viewController as? PageProtocol)?.pageModel?.navigationBar
    }
    
    /// Verifies the controller is the currently displayed controller.
    public func isDisplayed(viewController: UIViewController) -> Bool {
        guard let topViewController = topViewController,
              viewController == MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController) else {
            return false
        }
        return true
    }
}

extension NavigationController: MVMCoreViewManagerProtocol {
    public func getCurrentViewController() -> UIViewController? {
        guard let topViewController = topViewController else { return nil }
        return MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController)
    }
    
    public func containsPage(withPageType pageType: String?) -> Bool {
        for controller in viewControllers {
            if let manager = controller as? MVMCoreViewManagerProtocol,
               manager.containsPage(withPageType: pageType) {
                return true
            } else if let controller = controller as? MVMCoreViewControllerProtocol,
                      controller.pageType == pageType {
                return true
            }
        }
        return false
    }
    
    public func newDataReceived(in viewController: UIViewController) {
        
        if isDisplayed(viewController: viewController),
           let topViewController = topViewController,
           let model = getNavigationModel(from: viewController) {
            Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: topViewController)
            Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: topViewController)
        }
        manager?.newDataReceived?(in: viewController)
    }
    
    public func willDisplay(_ viewController: UIViewController) {
        if let topViewController = topViewController,
           let model = getNavigationModel(from: viewController) {
            Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: topViewController)
        }
        manager?.willDisplay?(viewController)
    }
    
    public func displayedViewController(_ viewController: UIViewController) {
        if isDisplayed(viewController: viewController),
           let topViewController = topViewController,
           let model = getNavigationModel(from: viewController) {
            Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: topViewController)
        }
        manager?.displayedViewController?(viewController)
    }
}

extension NavigationController: MVMCorePresentationDelegateProtocol {
    public func navigationController(_ navigationController: UINavigationController, prepareDisplayFor viewController: UIViewController) {
        guard self == navigationController,
              let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController),
              let model = getNavigationModel(from: newViewController) else { return }
        Self.setNavigationItem(navigationController: self, navigationItemModel: model, viewController: viewController)
    }
    
    public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) {
        guard self == navigationController,
              let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return }
        if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
            MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller)
        }
        manager?.willDisplay?(newViewController)
    }
    
    public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) {
        guard self == navigationController,
              let newViewController = MVMCoreUIUtility.getViewControllerTraversingManagers(viewController) else { return }
        if let model = getNavigationModel(from: newViewController) {
            Self.setNavigationBarUI(navigationController: self, navigationItemModel: model, viewController: viewController)
        }
        manager?.displayedViewController?(newViewController)
        if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
            controller.viewControllerReady?(inManager: self)
        }
    }
}
