<commit_before>//
//  SocketHandler.swift
//  ioswall
//
//  Created by satoshi on 10/20/16.
//  Copyright © 2016 UIEvolution Inc. All rights reserved.
//

import UIKit

class SocketHandler {
    public static let didConnectionChange = NSNotification.Name("didConnectionChange")
    public static let didGuidsChange = NSNotification.Name("didGuidsChange")
    public private(set) var connected = false
    private let socket:SocketIOClient
    private var latency = 0
    private var latencyHistory = [Int]()
    private var roomData = [String:AnyObject]()
    private var beginTime:Int = 0
    public private(set) var guids = Set<String>()
    public private(set) var verbs = [String]()
    private var refreshCount = 0 // only for debugging
    private var config = ["rooms":["Lobby"], "scenes":["Main":"/"]] as [String : Any]
    public var rooms:[String] { return self.config["rooms"] as! [String] }
    public var scenes:[[String:Any]] { return self.config["scenes"] as! [[String:Any]] }

    init(baseURL:URL, configPath:String) {
        socket = SocketIOClient(socketURL: baseURL, config: [])
        extraInit()
        socket.connect()
        
        let configURL = URL(string: configPath, relativeTo: baseURL)!
        print(configURL.absoluteString)
        SNNet.get(configURL.absoluteString, params:nil) { url, error in
            guard
                error == nil,
                let url = url,
                let data = try? Data(contentsOf: url),
                let json = try? JSONSerialization.jsonObject(with: data),
                let config = json as? [String:Any],
                let _ = config["rooms"] as? [String],
                let _ = config["scenes"] as? [[String:Any]] else {
                return
            }
            self.config = config
        }
    }
    
    private func extraInit() {
        socket.on("connect") { data, ack in
            self.connected = true
            NotificationCenter.default.post(name: SocketHandler.didConnectionChange, object: nil)
            print("socket connected")

            self.syncTime()
            self.setTimeout(msec:2500, callback:self.syncTime)
            self.setTimeout(msec:5000, callback:self.syncTime)
            self.setTimeout(msec:7500, callback:self.syncTime)
            self.setTimeout(msec:10000, callback:self.syncTime)
            self.setTimeout(msec:15000, callback:self.syncTime)
            self.setTimeout(msec:20000, callback:self.syncTime)
            self.setTimeout(msec:25000, callback:self.syncTime)
            
            // HACK: I'm not sure why we need to add a delay here
            self.setTimeout(msec:100) {
                self.timerTick(fps:60)
            }
        }
        socket.on("/sync") { (_data, _) in
            let now = self.now()
            guard let data = _data[0] as? [String:AnyObject],
                  let time = data["time"] as? Int else {
                print("on /sync, invalid data ", _data)
                return
            }
            var delta = now - time
            if let client = data["client"] as? Int {
                delta -= (now - client) / 2
            }
            
            self.latencyHistory.append(delta)
            self.latency = self.latencyHistory.reduce(0, { (sum:Int, value:Int) -> Int in
                return sum + value
            }) / self.latencyHistory.count
            if self.latencyHistory.count > 10 {
                self.latencyHistory.removeFirst()
            }
        }
        socket.on("/room/info") { (_data, _) in
            print("on room/info", _data)
            guard let _ = _data[0] as? [String:AnyObject] else {
                print("on /room/info, invalid data ", _data)
                return
            }
        }
        
        socket.on("/room/clear") { (_data, _) in
            print("on room/clear", _data)
            self.roomData.removeAll()
            //self.prepareAllElements()
        }
        
        socket.on("/room/refresh") { (_data, _) in
            self.refreshCount += 1
            print("on room/refresh", self.refreshCount)
            guard let data = _data[0] as? [String:AnyObject] else {
                print("on /room/refresh, invalid data ", _data)
                return
            }
            self.roomData = data
            //self.prepareAllElements()
        }

        socket.on("/room/message") { (_data, _) in
            //print("on room/message", _data)
            guard let data = _data[0] as? [String:Any],
                  let cmd = data["cmd"] as? String else {
                print("on /room/scene, invalid data ", _data)
                return
            }
            switch(cmd) {
            case "advertise":
                guard let guid = data["guid"] as? String else {
                    return
                }
                self.guids.insert(guid)
                if let verbs = data["verbs"] as? [String], verbs.count > 0 {
                    self.verbs = verbs
                }
                print("on /room/message:advertise", guid, self.guids.count, self.verbs)
                
                NotificationCenter.default.post(name: SocketHandler.didGuidsChange, object: nil)
            case "scene":
                break
            default:
                break
            }
        }
        // NOTE: This code has never been tested
        /*
        socket.on("/room/patch") { (_data, _) in
            guard let newObjects = _data[0] as? [String:[String:AnyObject]] else {
                print("patch no/invalid objects")
                return
            }
            guard var objects = self.roomData["objects"] as? [String:[String:AnyObject]] else {
                print("prepare no/invalid objects")
                return
            }
            for (name, object) in newObjects {
                var objectCopy = object
                objectCopy["__noreuse"] = true
                objects[name] = objectCopy
            }
            self.roomData["objects"] = objects
            //self.prepareAllElements()
        }
        */
    }
    
    private func syncTime() {
        self.socket.emit("/sync", ["client":Int(self.now())])
    }
    
    private func setTimeout(msec:UInt64, callback: @escaping @convention(block) () -> Swift.Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute:callback)
    }
    
    private func timerTick(fps:UInt64) {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        //updateElements()
        CATransaction.commit()

        self.setTimeout(msec:1000 / fps) {
            self.timerTick(fps:fps)
        }
    }

    private func now() -> Int {
        return Int(NSDate().timeIntervalSince1970 * 1000)
    }

    func switchTo(scene:[String:Any]) {
        self.verbs = scene["commands"] as? [String] ?? [String]()
        socket.emit("/room/message", ["cmd":"scene", "scene":scene])
    }
    
    func switchTo(room:String) {
        socket.emit("/room/join", ["name":room])
    }

    func sendAppMessage(data:[String:Any]) {
        socket.emit("/room/message", data)
    }
    
    func add(objects:[String:[String:Any]]) {
        socket.emit("/room/add", objects)
    }
    
    func scan() {
        guids.removeAll()
        socket.emit("/room/message", ["cmd":"scan"])
    }
}<commit_msg>removed unused code<commit_after>//
//  SocketHandler.swift
//  ioswall
//
//  Created by satoshi on 10/20/16.
//  Copyright © 2016 UIEvolution Inc. All rights reserved.
//

import UIKit

class SocketHandler {
    public static let didConnectionChange = NSNotification.Name("didConnectionChange")
    public static let didGuidsChange = NSNotification.Name("didGuidsChange")
    public private(set) var connected = false
    private let socket:SocketIOClient
    public private(set) var guids = Set<String>()
    public private(set) var verbs = [String]()
    private var config = ["rooms":["Lobby"], "scenes":["Main":"/"]] as [String : Any]
    public var rooms:[String] { return self.config["rooms"] as! [String] }
    public var scenes:[[String:Any]] { return self.config["scenes"] as! [[String:Any]] }

    init(baseURL:URL, configPath:String) {
        socket = SocketIOClient(socketURL: baseURL, config: [])
        extraInit()
        socket.connect()
        
        let configURL = URL(string: configPath, relativeTo: baseURL)!
        print(configURL.absoluteString)
        SNNet.get(configURL.absoluteString, params:nil) { url, error in
            guard
                error == nil,
                let url = url,
                let data = try? Data(contentsOf: url),
                let json = try? JSONSerialization.jsonObject(with: data),
                let config = json as? [String:Any],
                let _ = config["rooms"] as? [String],
                let _ = config["scenes"] as? [[String:Any]] else {
                return
            }
            self.config = config
        }
    }
    
    private func extraInit() {
        socket.on("connect") { data, ack in
            self.connected = true
            NotificationCenter.default.post(name: SocketHandler.didConnectionChange, object: nil)
            print("socket connected")
        }

        socket.on("/room/message") { (_data, _) in
            //print("on room/message", _data)
            guard let data = _data[0] as? [String:Any],
                  let cmd = data["cmd"] as? String else {
                print("on /room/scene, invalid data ", _data)
                return
            }
            switch(cmd) {
            case "advertise":
                guard let guid = data["guid"] as? String else {
                    return
                }
                self.guids.insert(guid)
                if let verbs = data["verbs"] as? [String], verbs.count > 0 {
                    self.verbs = verbs
                }
                print("on /room/message:advertise", guid, self.guids.count, self.verbs)
                
                NotificationCenter.default.post(name: SocketHandler.didGuidsChange, object: nil)
            case "scene":
                break
            default:
                break
            }
        }
    }
    
    func switchTo(scene:[String:Any]) {
        self.verbs = scene["commands"] as? [String] ?? [String]()
        socket.emit("/room/message", ["cmd":"scene", "scene":scene])
    }
    
    func switchTo(room:String) {
        socket.emit("/room/join", ["name":room])
    }

    func sendAppMessage(data:[String:Any]) {
        socket.emit("/room/message", data)
    }
    
    func add(objects:[String:[String:Any]]) {
        socket.emit("/room/add", objects)
    }
    
    func scan() {
        guids.removeAll()
        socket.emit("/room/message", ["cmd":"scan"])
    }
}
{'fraction_non_alphanumeric': 0.12460538532961932, 'fraction_numerical': 0.008449396471680594, 'mean_word_length': 1.751916198262647, 'pattern_counts': {'":': 19, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2354177', 'n_tokens_mistral': 3118, 'n_tokens_neox': 2942, 'n_words': 687}