//
//  KeyboardShortcuts.swift
//  App
//
//  Created by James Magahern on 9/21/20.
//

import UIKit

@objc
protocol ShortcutResponder: AnyObject {
    @objc
    optional func focusURLBar(_ sender: Any?)
    
    @objc
    optional func focusWebView(_ sender: Any?)
    
    @objc
    optional func goBack(_ sender: Any?)
    
    @objc
    optional func goForward(_ sender: Any?)
    
    @objc
    optional func createTab(_ sender: Any?)
    
    @objc
    optional func previousTab(_ sender: Any?)
    
    @objc
    optional func nextTab(_ sender: Any?)
    
    @objc
    optional func closeTab(_ sender: Any?)
    
    @objc
    optional func findOnPage(_ sender: Any?)
    
    @objc
    optional func refresh(_ sender: Any?)
    
    @objc
    optional func stop(_ sender: Any?)
    
    @objc
    optional func showPreferences(_ sender: Any?)
    
    @objc
    optional func toggleDarkMode(_ sender: Any?)
    
    @objc
    optional func openInReaderMode(_ sender: Any?)
    
    @objc
    optional func showHistory(_ sender: Any?)
    
    @objc
    optional func handleOpenURL(_ sender: Any?, forEvent event: OpenURLEvent?)
    
    @objc
    optional func lowerScriptPolicyRestriction(_ sender: Any?)
    
    @objc
    optional func raiseScriptPolicyRestriction(_ sender: Any?)
    
    @objc
    optional func zoomToActualSize(_ sender: Any?)
}

public class OpenURLEvent: UIEvent {
    let url: URL
    public init(url: URL) {
        self.url = url
    }
}

fileprivate extension Array {
    func removeNulls() -> Array {
        self.filter { element in
            guard let keyCommand = element as? UIKeyCommand else { return true }
            return !keyCommand.isNull()
        }
    }
}

public class KeyboardShortcuts {
    public enum Category: CaseIterable {
        case application
        case file
        case go
        case view
    }
    
    public static func menu(for category: Category) -> [UIMenuElement] {
        switch category {
        case .application:
            return [
                // Preferences
                UIKeyCommand(
                    modifiers: .command,
                    input: ",",
                    title: "Preferences",
                    action: #selector(ShortcutResponder.showPreferences)
                )
            ]
            
        case .file:
            return [
                // Open Location...
                UIKeyCommand(
                    modifiers: .command, input: "L",
                    title: "Open Location",
                    action: #selector(ShortcutResponder.focusURLBar)
                ),
                
                // Open in Reader
                UIKeyCommand(
                    modifiers: [ .command, .shift ], input: "R",
                    title: "Open in Reader Mode…",
                    action: #selector(ShortcutResponder.openInReaderMode)
                ),
                
                // Tabs
                UIMenu(options: .displayInline, children: [
                    // Create Tab
                    UIKeyCommand(
                        modifiers: .command, input: "T",
                        title: "New Tab",
                        action: #selector(ShortcutResponder.createTab)
                    ),

                    // Close tab
                    UIKeyCommand(
                        modifiers: [.command], input: "W",
                        title: "Close Tab",
                        action: #selector(ShortcutResponder.closeTab)
                    ),
                ]),
                
                // Find on page
                (FindOnPageViewController.isEnabled() ?
                    UIKeyCommand(
                        modifiers: [.command], input: "F",
                        title: "Find on Page",
                        action: #selector(ShortcutResponder.findOnPage)
                    )
                 : UIKeyCommand.null()
                ),
                
                UIMenu(options: .displayInline, children: [
                    // Refresh
                    UIKeyCommand(
                        modifiers: [.command], input: "R",
                        title: "Refresh",
                        action: #selector(ShortcutResponder.refresh)
                    ),
                    
                    // Stop
                    UIKeyCommand(
                        modifiers: [.command], input: ".",
                        title: "Stop",
                        action: #selector(ShortcutResponder.stop)
                    )
                ]),
                
                UIMenu(options: .displayInline, children: [
                    // Raise Script Policy Restriction
                    UIKeyCommand(
                        modifiers: [.alternate], input: "x",
                        title: "Raise Script Policy Restriction",
                        action: #selector(ShortcutResponder.raiseScriptPolicyRestriction)
                    ),
                    
                    // Lower Script Policy Restriction
                    UIKeyCommand(
                        modifiers: [.alternate], input: "c",
                        title: "Lower Script Policy Restriction",
                        action: #selector(ShortcutResponder.lowerScriptPolicyRestriction)
                    ),
                ])
            ].removeNulls()
            
        case .go:
            return [
                // Focus Web View
                UIKeyCommand(
                    modifiers: [ .command, .shift ], input: "w",
                    title: "Focus Web View",
                    action: #selector(ShortcutResponder.focusWebView)
                ),

                // Back/Forward
                UIMenu(options: .displayInline, children: [
                    // Go Back
                    UIKeyCommand(
                        modifiers: .command, input: "[",
                        title: "Go Back",
                        action: #selector(ShortcutResponder.goBack)
                    ),
                    
                    // Go Forward
                    UIKeyCommand(
                        modifiers: .command, input: "]",
                        title: "Go Forward",
                        action: #selector(ShortcutResponder.goForward)
                    ),
                ]),
                
                // Tab Navigation
                UIMenu(options: .displayInline, children: [
                    // Previous Tab
                    UIKeyCommand(
                        modifiers: [.command, .shift], input: "[",
                        title: "Previous Tab",
                        action: #selector(ShortcutResponder.previousTab)
                    ),
                    
                    // Next Tab
                    UIKeyCommand(
                        modifiers: [.command, .shift], input: "]",
                        title: "Next Tab",
                        action: #selector(ShortcutResponder.nextTab)
                    ),
                ])
            ]
            
        case .view:
            return [
                // Zoom
                UIMenu(options: .displayInline, children: [
                    // Actual Size
                    UIKeyCommand(
                        modifiers: .command, input: "0",
                        title: "Actual Size",
                        action: #selector(ShortcutResponder.zoomToActualSize)
                    ),
                    
                    // Increase Zoom
                    UIKeyCommand(
                        modifiers: .command, input: "=",
                        title: "Zoom In",
                        action: #selector(UIResponder.increaseSize)
                    ),
                    
                    // Go Forward
                    UIKeyCommand(
                        modifiers: .command, input: "-",
                        title: "Zoom Out",
                        action: #selector(UIResponder.decreaseSize)
                    ),
                ]),

                // Toggle Dark Mode
                UIKeyCommand(
                    modifiers: [.command], input: "M",
                    title: "Toggle Dark Mode",
                    action: #selector(ShortcutResponder.toggleDarkMode)
                ),
            ]
        }
    }
    
    public static func hiddenKeyCommands() -> [UIKeyCommand] {
        return [
            // Go Back
            UIKeyCommand(input: UIKeyCommand.inputLeftArrow,
                         modifierFlags: [.command, .control],
                         action: #selector(ShortcutResponder.goBack)),
            
            // Go Forward
            UIKeyCommand(input: UIKeyCommand.inputRightArrow,
                         modifierFlags: [.command, .control],
                         action: #selector(ShortcutResponder.goForward)),
        ]
    }
    
    public static func allKeyCommands() -> [UIKeyCommand] {
        var commands: [UIKeyCommand] = []
        for category in Category.allCases {
            let menuElements = menu(for: category)
            for element in menuElements {
                if let command = element as? UIKeyCommand {
                    commands.append(command)
                } else if let menu = element as? UIMenu {
                    commands.append(contentsOf: menu.children as! [UIKeyCommand])
                }
            }
        }
    
        return commands + Self.hiddenKeyCommands()
    }
}
