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

import UIKit

class NavigationBar: UINavigationBar {
    
    private var currBgImage: UIImage?
    private var targetBgImage: UIImage?
    private var originShadowImage: UIImage?
    
    private var hasChangedBgImage: Bool = false
    private var hasChangedShadow: Bool = false
    private var titleView: UIView?
    
    var needsInteractive: Bool {
        return hasChangedBgImage || hasChangedShadow
    }
    var bounce: Bounce = .none
    var isPush: Bool = true
    
    override var shadowImage: UIImage? {
        didSet {
            originShadowImage = oldValue
            hasChangedShadow = true
        }
    }
    
    lazy var fakeView: UIVisualEffectView = {
        let v = UIVisualEffectView(effect: UIBlurEffect(style: .light))
        v.frame = getNavigationBarBounds()
        v.isUserInteractionEnabled = false
        v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
        return v
    }()
    
    lazy var backgroundImageView: UIImageView = {
        let v = UIImageView()
        v.isUserInteractionEnabled = false
        v.frame = getNavigationBarBounds().offsetBy(dx: bounds.width, dy: 0)
        
        return v
    }()
    
    lazy var shadowImageView: UIImageView = {
        let v = UIImageView()
        v.frame = CGRect(x: 0, y: bounds.height, width: bounds.width, height: 0.5)
        
        return v
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func setBackgroundImage(_ backgroundImage: UIImage?, for barMetrics: UIBarMetrics) {
        currBgImage = self.backgroundImage(for: .default)
        targetBgImage = backgroundImage
        hasChangedBgImage = true
    }
    
    override func setBackgroundImage(_ backgroundImage: UIImage?, for barPosition: UIBarPosition, barMetrics: UIBarMetrics) {
        currBgImage = self.backgroundImage(for: .default)
        targetBgImage = backgroundImage
        hasChangedBgImage = true
    }
    
    func setBackgroundImage() {
        super.setBackgroundImage(targetBgImage, for: .any, barMetrics: .default)
    }
    
    func getBarBackground() -> UIView? {
       return value(forKeyPath: "_backgroundView") as? UIView
    }
    
    func getContentView() -> UIView? {
        for v in subviews {
            if let ContentClass = NSClassFromString("_UINavigationBarContentView"), v.isKind(of: ContentClass) {
                return v
            }
        }
        return nil
    }
    
    func getShadowView() -> UIView? {
        guard let barBackground = getBarBackground() else { return nil }
        for v in barBackground.subviews {
            if (v.bounds.height == 0.5) {
                return v
            }
        }
        return nil
    }
    
    func getNavigationBarBounds() -> CGRect {
        let statusHeight = UIApplication.shared.statusBarFrame.height
        return CGRect(x: 0, y: -statusHeight, width: bounds.width, height: bounds.height + statusHeight)
    }
    
    override func value(forUndefinedKey key: String) -> Any? {
        return nil
    }
}

/// NavigationBar transition
extension NavigationBar {
    func constructViewHierarchy() {
        setupShadowView()
        setupBackgroundImageView()
        
        guard let barBackground = getBarBackground() else { return }
        
        insertSubview(shadowImageView, aboveSubview: barBackground)
        insertSubview(backgroundImageView, aboveSubview: barBackground)
        insertSubview(fakeView, belowSubview: backgroundImageView)
        layoutIfNeeded()
    }
    
    private func setupShadowView() {
        if let image = originShadowImage, image.size == CGSize.zero {
            shadowImageView.image = image
        } else {
            shadowImageView.backgroundColor = UIColor(red: 0, green: 0, blue: 0,
                                                      alpha: 77.0 / 255)
        }
        
        shadowImageView.alpha = originShadowImage == nil ? 1 : 0
        getShadowView()?.isHidden = true
    }
    
    private func setupBackgroundImageView() {
        if isPush {
            fakeView.alpha = 0
            backgroundImageView.image = targetBgImage
        } else {
            setBackgroundImage()
            fakeView.alpha = 1
            backgroundImageView.image = currBgImage
        }
    }
    
    /// interactivePopGestureRecognizer
    func transitionAnimationWithPercent(_ percent: CGFloat) {
        switch bounce {
        case .forward, .none:
            transitionShadowAnimationWithPercent(percent)
            transitionBackgroundAnimationWithPercent(percent)
            break
        case .backward:
            transitionShadowAnimationWithPercent(1 - percent)
            transitionBackgroundAnimationWithPercent(1 - percent)
            break
        }
    }
    
    func transitionAnimation() {
        transitionShadowAnimation()
        transitionBackgroundAnimation()
    }
    
    private func transitionShadowAnimation() {
        guard hasChangedShadow else { return }
        shadowImageView.alpha = originShadowImage == nil ? 0 : 1
//        if originShadowImage == nil {
//            shadowImageView.alpha = isInteractive ? (1 - percent) * 1 : 0
//        } else {
//            shadowImageView.alpha = isInteractive ? percent * 1 : 1
//        }
    }
    
    private func transitionBackgroundAnimation() {
        guard hasChangedBgImage else { return }
        if isPush {
            fakeView.alpha = 1
            backgroundImageView.frame.origin.x = 0
        } else {
            fakeView.alpha = 0
            backgroundImageView.frame.origin.x = bounds.width
        }
    }
    
    private func transitionShadowAnimationWithPercent(_ percent: CGFloat) {
        guard hasChangedShadow else { return }
        shadowImageView.alpha = originShadowImage == nil ? (1 - percent) * 1 : percent * 1
    }
    
    private func transitionBackgroundAnimationWithPercent(_ percent: CGFloat) {
        guard hasChangedBgImage else { return }
        fakeView.alpha = (1 - percent) * 1
        backgroundImageView.frame.origin.x = percent * bounds.width
    }
    
    func clear() {
        
        if isPush && hasChangedBgImage { setBackgroundImage() }
        
        if !isPush && bounce == .backward && hasChangedBgImage {
            targetBgImage = currBgImage
            setBackgroundImage()
        }
        
        getShadowView()?.isHidden = false
        
        fakeView.removeFromSuperview()
        shadowImageView.removeFromSuperview()
        backgroundImageView.removeFromSuperview()
        
        currBgImage = nil
        targetBgImage = nil
        
        hasChangedBgImage = false
        hasChangedShadow = false
        
        bounce = .none
    }
}

extension NavigationBar {
    enum Bounce {
        case none
        case backward
        case forward
    }
}