import Foundation
import Network

protocol SocketDelegate: AnyObject {
    func didChangeState(socket: String, state: SocketState)
    func didReceiveMessage(socket: String, message: String)
}

public class Socket: NSObject {
    
    var id: String
    var host: String
    var port: Int
    var useTLS: Bool
    var acceptInvalidCertificates: Bool
    
    var connection: NWConnection?
    
    weak var delegate: SocketDelegate?
    
    public init(id: String, host: String, port: Int, useTLS: Bool, acceptInvalidCertificates: Bool) {
        self.id = id
        self.host = host
        self.port = port
        
        self.useTLS = useTLS
        self.acceptInvalidCertificates = acceptInvalidCertificates
    }
    
    public func connect() {
        let connection = NWConnection(host: NWEndpoint.Host(self.host), port: NWEndpoint.Port(String(self.port))!, using: .tcp)
        connection.stateUpdateHandler = self.stateDidChange(to:)
        self.receive(on: connection)
        connection.start(queue: .main)
        self.connection = connection
    }
    
    public func write(_ message: String) {
        if let data = message.data(using: .utf8) {
            connection?.send(content: data, completion: .idempotent)
        }
    }
    
    public func disconnect() {
        connection?.forceCancel()
    }
    
    func stateDidChange(to state: NWConnection.State) {
        switch state {
            case .setup:
                print("connection setup")
                break
                
            case .waiting(let error):
                print("connection waiting: \(error)")
                break
                
            case .preparing:
                self.delegate?.didChangeState(socket: self.id, state: .connecting)
                break
                
            case .ready:
                self.delegate?.didChangeState(socket: self.id, state: .connected)
                break
                
            case .failed(let error):
                print("connection failed: \(error)")
                break
                
            case .cancelled:
                self.delegate?.didChangeState(socket: self.id, state: .disconnected)
                break
            
            default:
                print("other")
                break
        }
    }
    
    func receive(on connection: NWConnection) {
        connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { (data, contentContext, isComplete, error) in
            if let data = data, !data.isEmpty {
                if let message = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) {
                    self.delegate?.didReceiveMessage(socket: self.id, message: message)
                }
            }
            self.receive(on: connection)
        }
    }
    
}