//
//  SocketIO.swift
//  Home
//
//  Created by Richard Kuckovsky on 21.4.17.
//  Copyright © 2017 Richard Kuckovsky. All rights reserved.
//

import UIKit
import SocketIO
import Foundation

struct outputsType {
    var id : Int
    var device : Int
    var room_index : Int
    var output_index : Int
    var name : String
    var output_name : String
    var room : Int
    var type : String
    var min_value : Int
    var max_value : Int
    var byte : Int
    var length : Int
    var value : Int
    var temp : CGFloat
    
    init() {
        
        id = 0
        device = 0
        room_index = 0
        output_index = 0
        name = ""
        output_name = ""
        room = 0
        type = ""
        min_value = 0
        max_value = 0
        byte = 0
        length = 0
        value = 0
        temp = 0
        
    }
}

struct sensorsType {
    var id : Int
    var device : Int
    var room_index : Int
    var sensor_index : Int
    var name : String
    var sensor_name : String
    var room : Int
    var color : Int
    var type : String
    var unit : String
    var value : Int
    
    init(id: Int = 0, device: Int = 0, room_index: Int = 0, sensor_index: Int = 0, name: String = "", sensor_name: String = "", room: Int = 0, color: Int = 0, type: String = "", unit: String = "", value: Int = 0) {
        
        self.id = id
        self.device = device
        self.room_index = room_index
        self.sensor_index = sensor_index
        self.name = name
        self.sensor_name = sensor_name
        self.room = room
        self.color = color
        self.type = type
        self.unit = unit
        self.value = value
        
    }
}

struct eventsType {
    var id : Int
    var room_index : Int
    var event_index : Int
    var name : String
    var room : Int
    var type : String
    var sensor : Int
    var start : String
    var stop : String
    var timeout : Int
    var days : String
    var event_on : Int
    var output : Int
    var value : Int
    var active : Int
    
    init(id: Int = 0, room_index: Int = 0, event_index: Int = 0, name: String = "", room: Int = 0, type: String = "", sensor: Int = 0, start: String = "", stop:String = "", timeout:Int = 0, days:String = "", event_on:Int = 0, output:Int = 0, value:Int = 0, active:Int = 0) {
        
        self.id = id
        self.room_index = room_index
        self.event_index = event_index
        self.name = name
        self.room = room
        self.type = type
        self.sensor = sensor
        self.start = start
        self.stop = stop
        self.timeout = timeout
        self.days = days
        self.event_on = event_on
        self.output = output
        self.value = value
        self.active = active
    }
}

struct roomsType {
    var id : Int = 0
    var image : UIImage
    var name : String
    var temp : Int
    var temp_value : Int
    var outputs = [outputsType]()
    var sensors = [sensorsType]()
    var events = [eventsType]()
    
    init(id : Int = 0, name : String = "") {
        self.id = id
        self.image = UIImage.init()
        self.name = name
        self.temp = 0
        self.temp_value = 0
        self.outputs = [outputsType()]
        self.sensors = [sensorsType()]
        self.events = [eventsType()]
    }
}

var rooms:[roomsType] = []
var favourite_outputs = [Int]()
var favourite_sensors = [Int]()


class SocketIOManager: NSObject {
    
    static let sharedInstance = SocketIOManager()
    
    var socket: SocketIOClient = SocketIOClient(socketURL: URL(string: "http://192.168.0.100:90")!, config: [.log(false), .forcePolling(true)])
    
    
    override init() {
        super.init()
    }
    
    
    func establishConnection() {
        socket.connect()
    }
    
    func updateConnection() {
        socket.reconnect()
    }
    
    
    func closeConnection() {
        socket.disconnect()
    }
    
    func loadUnusedDevices(completionHandler: @escaping (_ names: [String], _ ids: [Int]) -> Void) {
        var names = [String]()
        var ids = [Int]()
        
        self.socket.emitWithAck("unused_devices", ["0":0]).timingOut(after: 0) { data in
            if let output_list = data[0] as? [String: Any]  {
                if let results = output_list["results"] as? NSArray {
                    for device in results {
                        if let object = device as? [String: Any] {
                            
                            if let id = object["id"] as? Int {
                                ids.append(id)
                            }
                            if let name = object["name"] as? String {
                                names.append(name)
                            }
                        }
                    }
                }
            }
            completionHandler(names, ids)
        }
    }
    
    func loadUsedDevices(completionHandler: @escaping (_ names: [String], _ ids: [Int]) -> Void) {
        var names = [String]()
        var ids = [Int]()
        
        self.socket.emitWithAck("used_devices", ["0":0]).timingOut(after: 0) { data in
            if let output_list = data[0] as? [String: Any]  {
                if let results = output_list["results"] as? NSArray {
                    for device in results {
                        if let object = device as? [String: Any] {
                            
                            if let id = object["id"] as? Int {
                                ids.append(id)
                            }
                            if let name = object["name"] as? String {
                                names.append(name)
                            }
                        }
                    }
                }
            }
            completionHandler(names, ids)
        }
    }
    
    func activateDevice(id: Int, completionHandler: @escaping (_ messageInfo: Bool) -> Void) {
        
        socket.emitWithAck("add_device", ["id": id]).timingOut(after: 0) {data in
            var status:Bool = false;
            if let object = data[0] as? [String: Any]  {
                if let obj_stat = object["status"] as? Int {
                    if(obj_stat == 1) {
                        status = true
                    } else {
                        status = false
                    }
                }
            }
            completionHandler(status)
        }
    }
    
    func deleteDevice(id: Int, completionHandler: @escaping (_ messageInfo: Bool) -> Void) {
        
        socket.emitWithAck("delete_device", ["id": id]).timingOut(after: 0) {data in
            var status:Bool = false;
            if let object = data[0] as? [String: Any]  {
                if let obj_stat = object["status"] as? Int {
                    if(obj_stat == 1) {
                        status = true
                    } else {
                        status = false
                    }
                }
            }
            completionHandler(status)
        }
    }
    
    
    
    
    func loadRoom(room: Int, room_index: Int, completionHandler: @escaping (_ outputs: [outputsType], _ sensors: [sensorsType], _ events: [eventsType]) -> Void) {
        
        var outputs = [outputsType]()
        var sensors = [sensorsType]()
        var events = [eventsType]()
        
        let myGroup = DispatchGroup()
        
        myGroup.enter()
        
        self.socket.emitWithAck("show_room", ["room_id": room]).timingOut(after: 0) { data in
            
            if let output_list = data[0] as? [String: Any]  {
                if let results = output_list["results"] as? [String: Any] {
                    if let result_outputs = results["outputs"] as? NSArray  {
                        var i : Int = 0
                        for output in result_outputs {
                            if let object = output as? [String: Any] {
                                
                                outputs.append(outputsType())
                                
                                if let id = object["id"] as? Int {
                                    outputs[i].id = id
                                }
                                if let id = object["device"] as? Int {
                                    outputs[i].device = id
                                }
                                
                                outputs[i].room_index = room_index
                                outputs[i].output_index = i
                                
                                if let id = object["name"] as? String {
                                    outputs[i].name = id
                                }
                                if let id = object["output_name"] as? String {
                                    outputs[i].output_name = id
                                }
                                if let id = object["room"] as? Int {
                                    outputs[i].room = id
                                }
                                if let id = object["type"] as? String {
                                    outputs[i].type = id
                                }
                                if let id = object["min_value"] as? Int {
                                    outputs[i].min_value = id
                                }
                                if let id = object["max_value"] as? Int {
                                    outputs[i].max_value = id
                                }
                                if let id = object["byte"] as? Int {
                                    outputs[i].byte = id
                                }
                                if let id = object["length"] as? Int {
                                    outputs[i].length = id
                                }
                                if let id = object["value"] as? Int {
                                    outputs[i].value = id
                                }
                                if let temp = object["temp"] as? CGFloat {
                                    outputs[i].temp = temp
                                }
                                i += 1
                            }
                        }
                    }
                    
                    if let result_sensors = results["sensors"] as? NSArray  {

                        var i : Int = 0
                        for sensor in result_sensors {
                            if let object = sensor as? [String: Any] {
                                
                                var id : Int = 0;
                                var device : Int = 0;
                                var name : String = "";
                                var sensor_name : String = "";
                                var room : Int = 0;
                                var color : Int = 0;
                                var type : String = "";
                                var unit : String = "";
                                var value : Int = 0;
                                
                                if let value = object["id"] as? Int {
                                    id = value
                                }
                                
                                if let value = object["device"] as? Int {
                                    device = value
                                }
                                
                                if let value = object["name"] as? String {
                                    name = value
                                }
                                
                                if let value = object["sensor_name"] as? String {
                                    sensor_name = value
                                }
                                
                                if let value = object["room"] as? Int {
                                    room = value
                                }
                                
                                if let value = object["color"] as? Int {
                                    color = value
                                }
                                
                                if let value = object["type"] as? String {
                                    type = value
                                }
                                
                                if let value = object["unit"] as? String {
                                    unit = value
                                }
                                
                                if let v_value = object["value"] as? Int {
                                    value = v_value
                                }
                                
                                sensors.append(sensorsType(id: id, device: device, room_index: room_index, sensor_index: i, name: name, sensor_name: sensor_name, room: room, color: color, type: type, unit: unit, value: value))
                            }
                            i += 1
                        }
                    }
                    
                    if let result_events = results["events"] as? NSArray  {

                        var i : Int = 0
                        for event in result_events {
                            if let object = event as? [String: Any] {
                                
                                var id : Int = 0;
                                var name : String = ""
                                var room : Int = 0
                                var type : String = ""
                                var sensor : Int = 0
                                var start : String = ""
                                var stop : String = ""
                                var timeout : Int = 0
                                var days : String = ""
                                var event_on : Int = 0
                                var output : Int = 0
                                var value : Int = 0
                                var active : Int = 0
                                
                                
                                if let value = object["id"] as? Int {
                                    id = value
                                }
                                
                                if let value = object["name"] as? String {
                                    name = value
                                }
                                
                                if let value = object["room"] as? Int {
                                    room = value
                                }
                                
                                if let value = object["type"] as? String {
                                    type = value
                                }
                                
                                if let value = object["sensor"] as? Int {
                                    sensor = value
                                }
                                
                                if let value = object["start"] as? String {
                                    start = value
                                }
                                
                                if let value = object["stop"] as? String {
                                    stop = value
                                }
                                
                                if let value = object["timeout"] as? Int {
                                    timeout = value
                                }
                                
                                if let value = object["days"] as? String {
                                    days = value
                                }
                                
                                if let value = object["event_on"] as? Int {
                                    event_on = value
                                }
                                
                                if let value = object["output"] as? Int {
                                    output = value
                                }
                                
                                if let v_value = object["value"] as? Int {
                                    value = v_value
                                }
                                
                                if let value = object["active"] as? Int {
                                    active = value
                                }
                                
                                events.append(eventsType(id: id, room_index: room_index, event_index: i, name: name, room: room, type: type, sensor: sensor, start: start, stop: stop, timeout: timeout, days: days, event_on: event_on, output: output, value: value, active: active))
                            }
                            i += 1
                        }
                    }
                }
            }
            myGroup.leave()
        }
        
        
        myGroup.notify(queue: .main) {
            completionHandler(outputs, sensors, events)
        }
    }
    
    func listRooms(completionHandler: @escaping (_ messageInfo: [Bool]) -> Void) {
        let myGroup = DispatchGroup()
        myGroup.enter()
        
        rooms.removeAll()
        
        socket.emitWithAck("list_rooms","").timingOut(after: 0) {data in
    
            
            if let output_list = data[0] as? [String: Any]  {
                if let results = output_list["results"] as? NSArray {
                    var i : Int = 0
                    for output in results {
                        if let object = output as? [String: Any] {
                            
                            rooms.append(roomsType())
                        
                            if let id = object["id"] as? Int {
                                rooms[i].id = id
                            }
                            
                            if let name = object["name"] as? String {
                                rooms[i].name = name
                            }
                            
                            if let temp = object["temp"] as? Int {
                                rooms[i].temp = temp
                                if(temp != 0) {
                                    if let value = object["temp_value"] as? Int {
                                        rooms[i].temp_value = value
                                    }
                                }
                            }
                            let async_index = i
                            myGroup.enter()
                            self.loadRoom(room: rooms[async_index].id, room_index: async_index)  { (outputs, sensors, events) -> Void in

                                rooms[async_index].outputs = outputs
                                rooms[async_index].sensors = sensors
                                rooms[async_index].events = events
                                myGroup.leave()
                                
                            }
                            i += 1
                        }
                        
                    }
                }
            }
            myGroup.leave()
        }
        
        myGroup.notify(queue: .main) {
            completionHandler([true])
        }
    }
    
    func getTempGraph(id: Int, completionHandler: @escaping (_ messageInfo: [Double]) -> Void) {
        
        var temps = [Double]()
        
        let myGroup = DispatchGroup()
        myGroup.enter()
        socket.emitWithAck("get_temp_graph",["id": id]).timingOut(after: 0) {data in
            
            if let output_list = data[0] as? [String: Any]  {
                if let results = output_list["results"] as? NSArray {
                    for output in results {
                        
                        temps.append(Double(output as! Double))
                        
                    }
                }
            }
            myGroup.leave()
        }
        
        myGroup.notify(queue: .main) {
            completionHandler(temps)
        }
    }
    
    func addRoom(room_name: String, completionHandler: @escaping (_ new_room_id: Int) -> Void) {
        let myGroup = DispatchGroup()
        myGroup.enter()
        var new_room_id : Int = 0
        socket.emitWithAck("add_room", ["name": room_name]).timingOut(after: 0) {data in
            if let object = data[0] as? [String: Any]  {
                if let id = object["new_room_id"] as? Int {
                    new_room_id = id
                }
            }
            myGroup.leave()
        }
        
        myGroup.notify(queue: .main) {
            completionHandler(new_room_id)
        }
    }
    
    func addSensor(id: Int, room: Int, name: String, color: Int, completionHandler: @escaping (_ done: Bool) -> Void) {
        let myGroup = DispatchGroup()
        myGroup.enter()
        socket.emitWithAck("assign_sensor", ["id": id, "room": room, "name" : name, "color": color]).timingOut(after: 0) {data in
            myGroup.leave()
        }
        
        myGroup.notify(queue: .main) {
            completionHandler(true)
        }
    }
    
    func addOutput(id: Int, room: Int, name: String, completionHandler: @escaping (_ done: Bool) -> Void) {
        let myGroup = DispatchGroup()
        myGroup.enter()
        socket.emitWithAck("assign_output", ["id": id, "room": room, "name" : name]).timingOut(after: 0) {data in
            myGroup.leave()
        }
        
        myGroup.notify(queue: .main) {
            completionHandler(true)
        }
    }
    
    func addEvent(room: Int, name: String, type: String, sensor: Int, start: String, stop: String, timeout: Int, days: String, event_on: Int, output: Int, value: Int, temp: Float, completionHandler: @escaping (_ done: Bool,_ id: Int) -> Void) {
        let myGroup = DispatchGroup()
        var id = 0
        myGroup.enter()
        socket.emitWithAck("add_event", ["room": room, "name" : name, "type": type, "sensor": sensor, "start" : start, "stop": stop, "timeout": timeout, "days": days, "event_on": event_on, "output": output, "value" : value, "temp" : temp]).timingOut(after: 0) {data in
            if let object = data[0] as? [String: Any]  {
                if let new_id = object["new_event_id"] as? Int {
                    id = new_id
                }
            }
            myGroup.leave()
            
        }
        
        myGroup.notify(queue: .main) {
            completionHandler(true, id)
        }
    }
    
    func removeSensor(id: Int, completionHandler: @escaping (_ done: Bool) -> Void) {
        let myGroup = DispatchGroup()
        myGroup.enter()
        socket.emitWithAck("unassign_sensor", ["id": id]).timingOut(after: 0) {data in
            myGroup.leave()
        }
        
        myGroup.notify(queue: .main) {
            completionHandler(true)
        }
    }
    
    func removeOutput(id: Int, completionHandler: @escaping (_ done: Bool) -> Void) {
        let myGroup = DispatchGroup()
        myGroup.enter()
        socket.emitWithAck("unassign_output", ["id": id]).timingOut(after: 0) {data in
            myGroup.leave()
        }
        
        myGroup.notify(queue: .main) {
            completionHandler(true)
        }
    }
    
    
    func showUnusedSensors(completionHandler: @escaping (_ sensors: [sensorsType]) -> Void) {
        var sensors = [sensorsType]()
        
        let myGroup = DispatchGroup()
        
        myGroup.enter()
        
        self.socket.emitWithAck("show_unused_sensors", ["room": 0]).timingOut(after: 0) { data in
        
            if let output_list = data[0] as? [String: Any]  {
                if let results = output_list["results"] as? [String: Any] {

                    if let result_sensors = results["sensors"] as? NSArray  {

                        var i : Int = 0
                        for sensor in result_sensors {
                            if let object = sensor as? [String: Any] {
                                
                                var id : Int = 0;
                                var device : Int = 0;
                                var name : String = "";
                                var sensor_name : String = "";
                                var room : Int = 0;
                                var type : String = "";
                                var unit : String = "";
                                var value : Int = 0;
                                
                                if let value = object["id"] as? Int {
                                    id = value
                                }
                                
                                if let value = object["device"] as? Int {
                                    device = value
                                }
                                
                                if let value = object["name"] as? String {
                                    name = value
                                }
                                
                                if let value = object["sensor_name"] as? String {
                                    sensor_name = value
                                }
                                
                                if let value = object["room"] as? Int {
                                    room = value
                                }
                                
                                if let value = object["type"] as? String {
                                    type = value
                                }
                                
                                if let value = object["unit"] as? String {
                                    unit = value
                                }
                                
                                if let v_value = object["value"] as? Int {
                                    value = v_value
                                }
                                
                                sensors.append(sensorsType(id: id, device: device, room_index: 0, sensor_index: 0, name: name, sensor_name: sensor_name, room: room, type: type, unit: unit, value: value))
                            }
                            i += 1
                        }
                    }
                }
            }
            
            myGroup.leave()
        }
        myGroup.notify(queue: .main) {
            completionHandler(sensors)
        }
    }
    
    
    func showUnusedOutputs(completionHandler: @escaping (_ sensors: [outputsType]) -> Void) {
        var outputs = [outputsType]()
        
        let myGroup = DispatchGroup()
        
        myGroup.enter()
        
        self.socket.emitWithAck("show_unused_outputs", ["room": 0]).timingOut(after: 0) { data in
            
            if let output_list = data[0] as? [String: Any]  {
                if let results = output_list["results"] as? [String: Any] {
                    
                    if let result_outputs = results["outputs"] as? NSArray  {

                        var i : Int = 0
                        for output in result_outputs {
                            if let object = output as? [String: Any] {
                                
                                outputs.append(outputsType())
                                
                                if let id = object["id"] as? Int {
                                    outputs[i].id = id
                                }
                                if let id = object["device"] as? Int {
                                    outputs[i].device = id
                                }
                                
                                outputs[i].room_index = 0
                                outputs[i].output_index = 0
                                
                                if let id = object["name"] as? String {
                                    outputs[i].name = id
                                }
                                if let id = object["output_name"] as? String {
                                    outputs[i].output_name = id
                                }
                                if let id = object["room"] as? Int {
                                    outputs[i].room = id
                                }
                                if let id = object["type"] as? String {
                                    outputs[i].type = id
                                }
                                if let id = object["min_value"] as? Int {
                                    outputs[i].min_value = id
                                }
                                if let id = object["max_value"] as? Int {
                                    outputs[i].max_value = id
                                }
                                if let id = object["byte"] as? Int {
                                    outputs[i].byte = id
                                }
                                if let id = object["length"] as? Int {
                                    outputs[i].length = id
                                }
                                if let id = object["value"] as? Int {
                                    outputs[i].value = id
                                }
                                if let temp = object["temp"] as? CGFloat {
                                    outputs[i].temp = temp
                                }
                                i += 1
                            }
                        }
                    }
                }
            }
            
            myGroup.leave()
        }
        myGroup.notify(queue: .main) {
            completionHandler(outputs)
        }
    }
    

    func dismissOutputUpdate() {
        socket.off("output_update")
    }
    
    
    func changeOutput(output: outputsType,value: Int, completionHandler: @escaping (_ messageInfo: Bool) -> Void) {

        socket.emitWithAck("change_output", ["id": output.id, "device": output.device, "name": output.name, "output_name": output.output_name, "room": output.room, "type": output.type, "min_value": output.min_value, "max_value": output.max_value, "byte": output.byte, "length": output.length, "value": value, "temp": output.temp]).timingOut(after: 0) {data in
            var status:Bool = false;
            if let object = data[0] as? [String: Any]  {
                if let obj_stat = object["status"] as? Int {
                    if(obj_stat == 1) {
                        status = true
                    } else {
                        status = false
                    }
                }
            }
            completionHandler(status)
        }
    }
    
    func changeEventActivity(id: Int,value: Int, completionHandler: @escaping (_ messageInfo: Bool) -> Void) {
        
        socket.emitWithAck("change_event_activity", ["id": id, "value": value]).timingOut(after: 0) {data in
            var status:Bool = false;
            if let object = data[0] as? [String: Any]  {
                if let obj_stat = object["status"] as? Int {
                    if(obj_stat == 1) {
                        status = true
                    } else {
                        status = false
                    }
                }
            }
            completionHandler(status)
        }
    }
    
    
    func outputUpdate(ids : [Int], completionHandler: @escaping (_ id: Int, _ value: Int, _ temp: Float) -> Void) {
        socket.on("output_update") {data, ack in
            
            if let object = data[0] as? [String: Any]  {
                if let id = object["id"] as? Int {
                    if(ids.contains(id)) {
                        var value:Int = 0;
                        var temp:Float = 0;
                        if let t_value = object["value"] as? Int {
                            value = t_value
                        }
                        if let t_value = object["temp"] as? Float {
                            temp = t_value
                        }
                        
                        completionHandler(id, value, temp)
                    }
                }
            }
        }
    }
    
    func videoUpdate(completionHandler: @escaping (_ img: UIImage) -> Void) {
        socket.on("image") {data, ack in

            if let object = data[0] as? String  {

                let obj : NSData = NSData(base64Encoded: object, options: NSData.Base64DecodingOptions(rawValue: 0))!
                let decodedimage = UIImage(data: obj as Data)
            
                completionHandler(decodedimage!)
            }
        }
    }
    func video_start() {
        socket.emit("video_on")
    }
    func video_stop() {
        socket.emit("video_off")
    }
    
    func dissVideoUpdate() {
        socket.off("image")
    }
    
    func checkConnect(completionHandler: @escaping (_ messageInfo: [String]) -> Void) {
        socket.on("connect") {data in

            completionHandler(["ok"])
        }
    }
    
    func stopCheckConnect() {
        socket.off("connect")
    }
    
    func checkError(completionHandler: @escaping (_ messageInfo: Bool) -> Void) {
        socket.on("error") {data in
            completionHandler(true)
        }
    }
    
    func stopCheckError() {
        socket.off("error")
    }
    
}

