//
//  ViewController.swift
//  Calzone
//
//  Created by Eric Kunz on 11/4/15.
//  Copyright © 2015 Tendigi. All rights reserved.
//

import Foundation
import UIKit
import WebKit
import JavaScriptCore

final class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    // MARK:- Constants
    
    private struct Constants {
        static let port = in_port_t(arc4random_uniform(65536 - 1024) + 1024)
        static let baseURL = NSURL(string: "http://127.0.0.1:\(port)")!
    }
    
    enum FileError: ErrorType {
        case DoesNotExist
    }
    
    // MARK:- Private Properties
    
    private let server = HttpServer()
    
    private let manager = NSFileManager.defaultManager()
    
    private let webView: WKWebView = {
        
        let config = WKWebViewConfiguration()
        
        config.allowsAirPlayForMediaPlayback = false
        config.allowsInlineMediaPlayback = true
        config.requiresUserActionForMediaPlayback = false
        config.allowsPictureInPictureMediaPlayback = true
        
        let webView = WKWebView(frame: UIScreen.mainScreen().bounds, configuration: config)
        
        webView.allowsBackForwardNavigationGestures = false
        webView.translatesAutoresizingMaskIntoConstraints = false
        
        return webView
        
    }()
    
    private var optionsContext = JSContext()
    
    private let optionsURL = NSBundle.mainBundle().URLForResource("options", withExtension: "js", subdirectory: "js")
    //    private let externalURL = NSBundle.mainBundle().URLForResource("External", withExtension: nil)
    private let htmlURL = NSBundle.mainBundle().resourceURL!.URLByAppendingPathComponent("html", isDirectory: true)
    
    private var options: [String]?
    
    private let appInfo = [
        "language": NSLocale.preferredLanguages().first!,
        "name": UIDevice.currentDevice().name,
        "date": NSDate().description
    ]
    
    // MARK:- Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
        startServer()
        print("Running at:", Constants.baseURL)
    }
    
    override func prefersStatusBarHidden() -> Bool {
        return true
    }
    
    private func setup() {
        setupWebView()
        setupContext()
        let threeFingerLongPress = UILongPressGestureRecognizer(target: self, action: "presentMenu:")
        threeFingerLongPress.numberOfTouchesRequired = 3
        webView.addGestureRecognizer(threeFingerLongPress)
    }
    
    private func setupContext() {
        
        optionsContext = contextFromURL(optionsURL!)
        optionsContext.exceptionHandler = { _, exception in print("JS Error: \(exception)") }
        
        let loadPage: @convention(block) (path: JSValue) -> Void = { path in
            
            guard let url = NSURL(string: path.toString(), relativeToURL: Constants.baseURL) else {
                print("Failed to load url")
                return
            }
            
            let request = NSURLRequest(URL: url.absoluteURL)
            self.webView.loadRequest(request)
        }
        
        optionsContext.setObject(unsafeBitCast(loadPage, AnyObject.self), forKeyedSubscript: "loadPage")
        
    }
    
    // MARK:- Server
    
    private func startServer() {
        
        guard let htmlDirectory = htmlURL.path else {
            fatalError("Failed to start server")
        }
        
        server["/(.+)"] = HttpHandlers.directory(htmlDirectory)
        server.start(Constants.port, error: nil)
    }
    
    // MARK:- Web View
    
    private func setupWebView() {
        
        view.addSubview(webView)
        
        let constraints = [
            NSLayoutConstraint(item: webView, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .Left, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: webView, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: webView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: webView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0)
        ]
        constraints.forEach { $0.active = true }
        
        loadFile(name: "index", ext: "html", subdirectory: "", webView: webView)
        
        webView.scrollView.scrollEnabled = false
    }
    
    private func loadFile(name name: String, ext: String, subdirectory: String, webView: WKWebView) {
        let string = subdirectory + "/" + name + "." + ext
        guard let url = NSURL(string: string, relativeToURL: Constants.baseURL) else {
            print("Failed to load url:", string)
            return
        }
        let request = NSURLRequest(URL: url.absoluteURL)
        webView.loadRequest(request)
    }
    
    // MARK:- Menu
    
    private dynamic func presentMenu(gesture: UILongPressGestureRecognizer) {
        
        guard gesture.state == .Began else {
            return
        }
        
        options = getMenuOptions()
        
        guard options?.count > 0 else {
            return
        }
        
        let tableViewController = UITableViewController()
        let cancelBarButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "didCancelOptionsTable")
        tableViewController.navigationItem.rightBarButtonItem = cancelBarButton
        tableViewController.tableView.delegate = self
        tableViewController.tableView.dataSource = self
        let navigationController = UINavigationController(rootViewController: tableViewController)
        presentViewController(navigationController, animated: true, completion: nil)
    }
    
    func didCancelOptionsTable() {
        dismissViewControllerAnimated(true, completion: nil)
    }
    
    private func getMenuOptions() -> [String]? {
        
        var contents: AnyObject?
        
        do {
            contents = try getContents("\(htmlURL.path!)")
            print(contents)
        } catch {
            print("ERROR: could not get html/... files")
        }
        
        if let files = contents, optionsFileURL = optionsURL {
            
            guard let response = callJavaScriptFunction("getOptions", from: optionsFileURL, arguments: [files]) else {
                return nil
            }
            
            options = response.toArray() as? [String]
            return options
        }
        
        return nil
    }
    
    private func getContents(path: String) throws -> AnyObject {
        
        var directory = ObjCBool(false)
        
        let exists = manager.fileExistsAtPath(path, isDirectory: &directory)
        
        guard exists else {
            throw FileError.DoesNotExist
        }
        
        if directory {
            let contents = try manager.contentsOfDirectoryAtPath(path)
            var result: [String : AnyObject] = [:]
            for subPath in contents {
                let newPath = (path as NSString).stringByAppendingPathComponent(subPath)
                result[subPath] = try getContents(newPath)
            }
            return result
        } else {
            return NSNull()
        }
    }
    
    private func respondToOptionSelection(optionIndex: Int) {
        let option = options![optionIndex]
        
        guard let url = optionsURL else {
            return
        }
        
        if let response = callJavaScriptFunction("optionTapped", from: url, arguments: [option]) {
            print(response.toString())
        }
    }
    
    // MARK: UITableViewDataSource
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return options?.count ?? 0
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell") ?? UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
        cell.textLabel?.text = options?[indexPath.row] ?? ""
        return cell
    }
    
    // MARK: UITableViewDelegate
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        respondToOptionSelection(indexPath.row)
        dismissViewControllerAnimated(true, completion: nil)
    }
    
    // MARK:- JavaScript
    
    private func contextFromURL(scriptURL: NSURL) -> JSContext? {
        
        guard let scriptString = try? String(contentsOfURL: scriptURL, encoding: NSUTF8StringEncoding) else {
            return nil
        }
        
        let context = JSContext()
        context.evaluateScript(scriptString)
        
        return context
    }
    
    private func callJavaScriptFunction(name: String, from scriptURL: NSURL, arguments: [AnyObject]!) -> JSValue? {
        guard let function = javaScriptValue(name, from: scriptURL) else {
            return nil
        }
        
        return function.callWithArguments(arguments)
    }
    
    private func javaScriptValue(name: String, from scriptURL: NSURL) -> JSValue? {
        
        let value = optionsContext.objectForKeyedSubscript(name)
        
        guard !value.isUndefined || !value.isNull else {
            return nil
        }
        
        return value
    }
    
}
