//
//  BrowserView.swift
//  SBrowser
//
//  Created by James Magahern on 7/21/20.
//

import Combine
import UIKit
import WebKit

class BrowserView: UIView
{
    let titlebarView = TitlebarView()
    
    var findOnPageView: FindOnPageView? {
        didSet { addSubview(findOnPageView!) }
    }
    
    var toolbarView: ToolbarView? {
        didSet { addSubview(toolbarView!) }
    }
    
    var tabBarView: TabBarView? {
        didSet { addSubview(tabBarView!) }
    }
    
    var tabBarViewVisible: Bool = true {
        didSet { setNeedsLayout() }
    }
    
    var autocompleteView: UICollectionView? {
        didSet {
            addSubview(autocompleteView!)
            if let toolbarView = toolbarView {
                bringSubviewToFront(toolbarView)
            }
        }
    }
    
    var webView: WKWebView? {
        didSet {
            oldValue?.removeFromSuperview()
            
            if let toolbarView = toolbarView {
                insertSubview(webView!, belowSubview: toolbarView)
            } else {
                addSubview(webView!)
            }
        }
    }
    
    var findOnPageVisible: Bool = false {
        didSet { layoutSubviews() }
    }
    
    func setFindOnPageVisible(_ visible: Bool, animated: Bool) {
        if animated {
            UIView.animate(withDuration: 0.33, animations: { self.findOnPageVisible = visible })
        } else {
            findOnPageVisible = visible
        }
    }
    
    var keyboardWillShowObserver: AnyCancellable?
    var keyboardWillHideObserver: AnyCancellable?
    var keyboardLayoutOffset: CGFloat = 0 { didSet { setNeedsLayout() } }
    
    convenience init() {
        self.init(frame: .zero)
        backgroundColor = .systemBackground
        
        addSubview(titlebarView)
        
        keyboardWillShowObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillShowNotification).sink { [weak self] notification in
            self?.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
        }
        
        keyboardWillHideObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillHideNotification).sink { [weak self] notification in
            self?.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
        }
    }
    
    private func adjustOffsetForKeyboardNotification(userInfo: [AnyHashable : Any]) {
        guard var keyboardEndFrame = userInfo[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect else { return }
        guard let animationDuration = userInfo[UIWindow.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
        guard let animationCurve = userInfo[UIWindow.keyboardAnimationCurveUserInfoKey] as? Int else { return }
        
        // Convert keyboard frame to screen coordinate space
        keyboardEndFrame = self.convert(keyboardEndFrame, from: UIScreen.main.coordinateSpace)
        
        let animationOptions: UIView.AnimationOptions = { curve -> UIView.AnimationOptions in
            switch UIView.AnimationCurve(rawValue: curve) {
                case .easeIn: return .curveEaseIn
                case .easeOut: return .curveEaseOut
                case .easeInOut: return .curveEaseInOut
                default: return .init()
            }
        }(animationCurve)
        
        toolbarView?.layoutLatch.deactivate()
        keyboardLayoutOffset = bounds.height - keyboardEndFrame.minY
        UIView.animate(withDuration: animationDuration, delay: 0.0, options: animationOptions, animations: { self.layoutIfNeeded() }, completion: nil)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        var webViewContentInset = UIEdgeInsets()
        
        bringSubviewToFront(titlebarView)
        
        var titlebarHeight: CGFloat = 24.0
        titlebarHeight += safeAreaInsets.top
        titlebarView.frame = CGRect(origin: .zero, size: CGSize(width: bounds.width, height: titlebarHeight))
        webViewContentInset.top += titlebarView.frame.height
        
        if let toolbarView = toolbarView {
            var toolbarSize = toolbarView.sizeThatFits(bounds.size)
            
            // Compact: toolbar is at the bottom
            if traitCollection.horizontalSizeClass == .compact {
                var bottomOffset: CGFloat = 0.0
                if keyboardLayoutOffset < CGFloat.leastNonzeroMagnitude {
                    toolbarSize.height += safeAreaInsets.bottom
                } else if toolbarView.urlBar?.textField.isFirstResponder ?? false {
                    bottomOffset = keyboardLayoutOffset
                }
                
                toolbarView.bounds = CGRect(origin: .zero, size: toolbarSize)
                toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - bottomOffset)
                webViewContentInset.bottom += toolbarView.frame.height
                
                if findOnPageVisible {
                    // Hide off the bottom
                    toolbarView.center = CGPoint(x: toolbarView.center.x, y: toolbarView.center.y + toolbarView.frame.height)
                }
            } else {
                // Regular: toolbar is at the top
                toolbarView.frame = CGRect(origin: CGPoint(x: 0.0, y: titlebarView.frame.maxY), size: toolbarSize)
                webViewContentInset.top += toolbarView.frame.height
            }
        }
        
        if let tabBarView = tabBarView {
            bringSubviewToFront(tabBarView)
            
            if tabBarViewVisible {
                tabBarView.isHidden = false
                
                let tabViewSize = tabBarView.sizeThatFits(bounds.size)
                
                tabBarView.frame = CGRect(
                    x: 0.0, y: webViewContentInset.top,
                    width: bounds.width, height: tabViewSize.height
                )
                
                webViewContentInset.top += tabBarView.frame.height
            } else {
                tabBarView.isHidden = true
            }
        }
        
        // Fix web view content insets
        if let webView = webView {
            webView.scrollView.layer.masksToBounds = true
            webView.frame = bounds.inset(by: webViewContentInset)
        }
        
        // Autocomplete view
        if let autocompleteView = autocompleteView {
            // Compact: autocomplete view takes the space of the webview
            autocompleteView.frame = bounds.inset(by: webViewContentInset)
            autocompleteView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardLayoutOffset, right: 0)
            autocompleteView.scrollIndicatorInsets = autocompleteView.contentInset
            
            if traitCollection.horizontalSizeClass == .regular {
                // Regular: shows up just underneath the url bar
                autocompleteView.layer.shadowColor = UIColor.black.cgColor
                autocompleteView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
                autocompleteView.layer.shadowRadius = 8.0
                autocompleteView.layer.shadowOpacity = 0.6
                autocompleteView.layer.cornerRadius = 8.0
                autocompleteView.layer.masksToBounds = true
                
                let shadowPath = UIBezierPath(roundedRect: autocompleteView.bounds,
                                              cornerRadius: autocompleteView.layer.cornerRadius)
                autocompleteView.layer.shadowPath = shadowPath.cgPath
                
                if let toolbarView = toolbarView, let urlBar = toolbarView.urlBar {
                    var yOffset = toolbarView.frame.maxY + 3.0
                    if let tabBarView = tabBarView, tabBarViewVisible {
                        yOffset += tabBarView.frame.height
                    }
                    
                    let urlFrame = self.convert(urlBar.frame, from: urlBar.superview)
                    autocompleteView.frame = CGRect(
                        x: urlFrame.minX,
                        y: yOffset,
                        width: urlFrame.width,
                        height: bounds.height / 2.5
                    )
                }
            } else {
                autocompleteView.layer.cornerRadius = 0.0
                autocompleteView.layer.shadowOpacity = 0.0
            }
        }
        
        // Find on page view
        if let findOnPageView = findOnPageView {
            var bottomOffset: CGFloat = 0.0
            var findOnPageSize = CGSize(width: bounds.width, height: 54.0)
            if keyboardLayoutOffset == 0 {
                findOnPageSize.height += safeAreaInsets.bottom
            } else if findOnPageView.textField.isFirstResponder {
                bottomOffset = keyboardLayoutOffset
            }
            
            findOnPageView.bounds = CGRect(origin: .zero, size: findOnPageSize)
            findOnPageView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (findOnPageView.bounds.height / 2) - bottomOffset)
            bringSubviewToFront(findOnPageView)
            
            if !findOnPageVisible {
                // Hide off the bottom
                findOnPageView.center = CGPoint(x: findOnPageView.center.x, y: findOnPageView.center.y + findOnPageView.frame.height)
            }
        }
    }
}
