//
//  BrowserViewController+Keyboard.swift
//  App
//
//  Created by James Magahern on 2/17/21.
//

import Foundation

@objc
protocol VIMBindings
{
    func scrollDownPressed(_ sender: Any?)
    func scrollUpPressed(_ sender: Any?)
}

extension BrowserViewController: VIMBindings
{
    static let keyboardScrollingEnabled = false // this is tricky...
    static let keyboardScrollAmount: CGFloat = 33.0
    
    override var keyCommands: [UIKeyCommand]? {
        get {
            [
                UIKeyCommand(input: "j", modifierFlags: [], action: #selector(VIMBindings.scrollDownPressed)),
                UIKeyCommand(input: "k", modifierFlags: [], action: #selector(VIMBindings.scrollUpPressed)),
            ]
        }
    }
    
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(VIMBindings.scrollDownPressed) || action == #selector(VIMBindings.scrollUpPressed) {
            return Self.keyboardScrollingEnabled && webView._contentViewIsFirstResponder && webView._currentContentView().isFocusingElement == false
        }
        
        return super.canPerformAction(action, withSender: sender)
    }
    
    func scrollDownPressed(_ sender: Any?) {
        var offset = webView.scrollView.contentOffset
        offset.y += Self.keyboardScrollAmount
        offset.y = min(offset.y, webView.scrollView.contentSize.height - webView.scrollView.bounds.height)
        
        webView.scrollView.setContentOffset(offset, animated: false)
    }
    
    func scrollUpPressed(_ sender: Any?) {
        var offset = webView.scrollView.contentOffset
        offset.y -= Self.keyboardScrollAmount
        offset.y = max(offset.y, webView.scrollView.contentInset.top)
        
        webView.scrollView.setContentOffset(offset, animated: false)
    }
}

extension BrowserViewController: ShortcutResponder
{
    internal func updateCommandKeyState(forPresses presses: Set<UIPress>) {
        guard let press = presses.first else { return }
        
        if let key = press.key {
            let isDown = press.phase == .began || press.phase == .changed || press.phase == .stationary
            if key.keyCode == .keyboardLeftGUI || key.keyCode == .keyboardRightGUI {
                self.commandKeyHeld = isDown
            } else if key.keyCode == .keyboardLeftShift || key.keyCode == .keyboardRightShift {
                self.shiftKeyHeld = isDown
            }
        }
    }
    
    override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        super.pressesBegan(presses, with: event)
        updateCommandKeyState(forPresses: presses)
    }
    
    override func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        super.pressesCancelled(presses, with: event)
        updateCommandKeyState(forPresses: presses)
    }
    
    override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        super.pressesEnded(presses, with: event)
        updateCommandKeyState(forPresses: presses)
    }

    // MARK: Keyboard Shortcuts
    
    func focusURLBar(_ sender: Any?) {
        toolbarController.urlBar.textField.becomeFirstResponder()
    }
    
    func focusWebView(_ sender: Any?) {
        webView.becomeFirstResponder()
    }
    
    func goBack(_ sender: Any?) {
        currentTab.webView.goBack()
    }
    
    func goForward(_ sender: Any?) {
        currentTab.webView.goForward()
    }
    
    func createTab(_ sender: Any?) {
        createNewTab(withURL: nil)
    }
    
    func previousTab(_ sender: Any?) {
        if let tabIndex = tabController.tabs.firstIndex(of: self.currentTab) {
            if tabIndex - 1 >= 0 {
                self.currentTab = tabController.tabs[tabIndex - 1]
            }
        }
    }
    
    func nextTab(_ sender: Any?) {
        if let tabIndex = tabController.tabs.firstIndex(of: self.currentTab) {
            if tabIndex + 1 < tabController.tabs.count {
                self.currentTab = tabController.tabs[tabIndex + 1]
            }
        }
    }
    
    func closeTab(_ sender: Any?) {
        if tabController.tabs.count > 1 {
            tabController.closeTab(self.currentTab)
        } else {
            #if targetEnvironment(macCatalyst)
            if let originWindowScene = self.view.window?.windowScene {
                UIApplication.shared.requestSceneSessionDestruction(originWindowScene.session, options: nil) { error in
                    print("Error when requesting scene destruction: " + error.localizedDescription)
                }
            }
            #endif
        }
    }
    
    func findOnPage(_ sender: Any?) {
        browserView.setFindOnPageVisible(true, animated: true)
        findOnPageController.findOnPageView.textField.becomeFirstResponder()
    }
    
    func refresh(_ sender: Any?) {
        webView.reload()
    }
    
    func stop(_ sender: Any?) {
        webView.stopLoading()
    }
    
    override func increaseSize(_ sender: Any?) {
        currentTab.webView._viewScale += 0.10
    }
    
    override func decreaseSize(_ sender: Any?) {
        currentTab.webView._viewScale -= 0.10
    }
    
    func zoomToActualSize(_ sender: Any?) {
        currentTab.webView._viewScale = 1.0
    }

    func showPreferences(_ sender: Any?) {
        showSettingsWindow()
    }
    
    func showHistory(_ sender: Any?) {
        showHistoryWindow()
    }
    
    func toggleDarkMode(_ sender: Any?) {
        self.darkModeEnabled = !self.darkModeEnabled
    }
    
    func openInReaderMode(_ sender: Any?) {
        showReaderWindow()
    }
    
    func handleOpenURL(_ sender: Any?, forEvent event: OpenURLEvent?) {
        guard let event else { return }
        
        if currentTab.url == nil {
            currentTab.beginLoadingURL(event.url)
        } else {
            createNewTab(withURL: event.url)
        }
    }
    
    func raiseScriptPolicyRestriction(_ sender: Any?) {
        guard let hostOrigin = currentTab.webView.url?.securityOrigin else { return }
        let currentPolicy = policyManager.scriptPolicy(forOrigin: hostOrigin)
        policyManager.setScriptPolicyType(currentPolicy.policyType--, forOrigin: hostOrigin)
        didChangeScriptPolicy()
    }
    
    func lowerScriptPolicyRestriction(_ sender: Any?) {
        guard let hostOrigin = currentTab.webView.url?.securityOrigin else { return }
        let currentPolicy = policyManager.scriptPolicy(forOrigin: hostOrigin)
        policyManager.setScriptPolicyType(currentPolicy.policyType++, forOrigin: hostOrigin)
        didChangeScriptPolicy()
    }
}

extension ScriptPolicy.PolicyType {
    static postfix func ++ (obj: ScriptPolicy.PolicyType) -> ScriptPolicy.PolicyType {
        switch obj {
        case .alpha:    .bravo
        case .bravo:    .charlie
        case .charlie:  .delta
        case .delta:    .echo
        default: obj
        }
    }
    
    static postfix func -- (obj: ScriptPolicy.PolicyType) -> ScriptPolicy.PolicyType {
        switch obj {
        case .bravo:    .alpha
        case .charlie:  .bravo
        case .delta:    .charlie
        case .echo:     .delta
        default: obj
        }
    }
}
