import Cocoa
import WebKit

struct Extension {
    {{#macosExtensions}}
    struct {{&name}} {}
    {{/macosExtensions}}
}

class ViewController: NSViewController, NSWindowDelegate {
    var script: Script! = Script()
    var client: Client!
    var networking: Networking!
    var renderer: Renderer! = Renderer()
    var customApp: CustomApp!

    override func loadView() {
        super.loadView()

        // set ItemView as a main view
        self.view = ItemView()
    }

    private func pushWindowSize() {
        let size = view.window?.contentView?.frame.size
        if size != nil {
            client.pushAction(.windowResize, size!.width, size!.height)
        }
    }

    func windowDidResize(_ notification: Notification) {
        pushWindowSize()
    }

    private func initClient() {
        client = Client(script: script)
        client.actions[InAction.setWindow] = {
            (reader: Reader) in
            self.view.addSubview(self.renderer.getItemFromReader(reader)!.view)
        }
        Db.register()
    }

    private func initRenderer() {
        renderer.app = self
        (NSApplication.shared() as! NeftApplication).renderer = renderer
        renderer.load()
    }

    fileprivate var displayLink: CVDisplayLink?

    override func viewDidLoad() {
        App.app = self

        initClient()
        initRenderer()
        networking = Networking(script: script)

        super.viewDidLoad()

        view.wantsLayer = true
        view.layer!.backgroundColor = CGColor(red: 1, green: 1, blue: 1, alpha: 1)

        // init custom
        self.customApp = CustomApp()
        initExtensions()

        // run
        runScript()

        // watch on js bundle file change
        {{#buildServerUrl}}
        watchOnBundleChange("{{&buildServerUrl}}/onNewBundle/macos", "{{&buildServerUrl}}/bundle/macos")
        {{/buildServerUrl}}
    }

    private func runScript() {
        script.attach(view, debug: {{debug}})
        script.runScript("neft") {
            self.client.sendData() {
                self.script.runCode("__macos__.onLoad()")
                self.client.ready = true
            }
        }
    }

    private func watchOnBundleChange(_ onChangeUrl: String, _ bundleUrl: String) {
        var req = URLRequest(url: URL(string: onChangeUrl)!)
        let session = URLSession.shared
        req.timeoutInterval = 0

        let task = session.dataTask(with: req, completionHandler: {
            (data, response, error) in
            // try again
            if data == nil {
                DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(Int(1000))) {
                    self.watchOnBundleChange(onChangeUrl, bundleUrl)
                }
                return
            }

            // reload app
            let resp = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
            if resp == "" {
                let subtask = session.dataTask(with: URLRequest(url: URL(string: bundleUrl)!)) {
                    (data, response, error) in
                    let js = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
                    DispatchQueue.main.async {
                        self.runJsBundle(code: js as String)
                        self.watchOnBundleChange(onChangeUrl, bundleUrl)
                    }
                }
                subtask.resume()
                return
            }

            // hotReloads
            self.client.pushEvent("__neftHotReload", args: [resp])
            self.watchOnBundleChange(onChangeUrl, bundleUrl)
        })

        task.resume()
    }

    private func runJsBundle(code: String) {
        // clear
        client.destroy()
        view.subviews.forEach { $0.removeFromSuperview() }

        // init classes
        script = Script()
        initClient()
        renderer = Renderer()
        initRenderer()
        initExtensions()

        // run
        runScript()
    }

    private func initExtensions() {
        {{#macosExtensions}}
        Extension.{{&name}}.register()
        {{/macosExtensions}}
    }

    override func viewDidAppear() {
        self.view.window!.delegate = self
        pushWindowSize()
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
}
