//
//  NavigationController.swift
//  PaiaiUIKit
//
//  Created by FFIB on 2019/4/23.
//  Copyright © 2019 FFIB. All rights reserved.
//

import UIKit

public class NavigationController: UINavigationController {
    
    private var operation: Operation = .none
    private var barConfigures: [UIViewController?: NavigationBarConfiguration] = [:]
    
    private var _fromFakeBar = UIToolbar()
    private var _toFakeBar = UIToolbar()
    
    override public init(rootViewController: UIViewController) {
        super.init(navigationBarClass: NavigationBar.classForCoder(), toolbarClass: nil)
        self.viewControllers = [rootViewController]
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    override public func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
        interactivePopGestureRecognizer?.delegate = self
        interactivePopGestureRecognizer?.isEnabled = true
    }
    
    public override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if operation == .none { return }
        navigationBar.getBarBackground()?.alpha = 0
    }
}

fileprivate extension NavigationController {
    
    func setFakeNavigationBar() {
        UIView.setAnimationsEnabled(false)
        guard let to = transitionCoordinator?.viewController(forKey: .to),
            let from = transitionCoordinator?.viewController(forKey: .from) else { return }
        
        switch self.operation {
        case .push:
            barConfigures[to] = NavigationBarConfiguration(navigationBar: navigationBar)
        case .pop:
            navigationBar.setBackgroundImage(barConfigures[to]?.backgroundImage, for: .default)
        default:
            break
        }
        
        setToFakeNavigationBar(for: to)
        setFromFakeNavigationBar(for: from)
        
        navigationBar.apply(for: .transparent)
        UIView.setAnimationsEnabled(true)
        
        if barConfigures[to]?.isHidden != barConfigures[from]?.isHidden {
            setNavigationBarHidden(barConfigures[to]?.isHidden ?? false, animated: true)
        }
    }

    func setFromFakeNavigationBar(for from: UIViewController) {
        if let navBarFrame = from.getFakeBarFrame(for: navigationBar) {
            _fromFakeBar = UIToolbar(configure: barConfigures[from] ?? .default)
            _fromFakeBar.delegate = self
            _fromFakeBar.frame = navBarFrame
            from.view.addSubview(_fromFakeBar)
        }
    }
    
    func setToFakeNavigationBar(for to: UIViewController) {
        if let navBarFrame = to.getFakeBarFrame(for: navigationBar) {
            _toFakeBar = UIToolbar(configure: barConfigures[to] ?? .default)
            _toFakeBar.delegate = self
            _toFakeBar.frame = navBarFrame
            to.view.addSubview(_toFakeBar)
        }
    }
    
    func clearFake() {
        _fromFakeBar.removeFromSuperview()
        _toFakeBar.removeFromSuperview()
        
        guard let from = transitionCoordinator?.viewController(forKey: .from),
            operation == .pop else { return }
        barConfigures.removeValue(forKey: from)
    }
}

extension NavigationController: UINavigationControllerDelegate {

    public func navigationController(_ navigationController: UINavigationController,
                                     animationControllerFor operation: UINavigationController.Operation,
                                     from fromVC: UIViewController,
                                     to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        self.operation = operation
        return nil
    }

    public func navigationController(_ navigationController: UINavigationController,
                                     willShow viewController: UIViewController,
                                     animated: Bool) {
        if operation == .none {
            self.barConfigures[viewController] = NavigationBarConfiguration(navigationBar: navigationBar)
            return
        }
        
        transitionCoordinator?.animate(alongsideTransition: { context in
            if context.isInteractive {
                self.operation = .pop
            }
            self.setFakeNavigationBar()
        }, completion: { context in
            let vc: UIViewController?
            if context.isCancelled {
                self.operation = .push
                vc = context.viewController(forKey: .from)
            } else {
                vc = context.viewController(forKey: .to)
            }
            self.navigationBar.getBarBackground()?.alpha = 1
            self.navigationBar.apply(for: self.barConfigures[vc] ?? .default)
            self.clearFake()
        })
    }
}

extension NavigationController: UIGestureRecognizerDelegate {
    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if gestureRecognizer == interactivePopGestureRecognizer {
            return viewControllers.count > 1
        }
        return true
    }
}

extension NavigationController: UIToolbarDelegate {
    public func position(for bar: UIBarPositioning) -> UIBarPosition {
        return .top
    }
}