//
//  TCPSendOperation.swift
//  MpAccDemo
//

import UIKit
import CocoaAsyncSocket

class TCPSendOperation: Operation, @unchecked Sendable {
    
    let sendMsg: String
    var tcpSocket: GCDAsyncSocket!
    let tag: Int
    
    let logFunc: (String) -> Void
    
    init(msg: String, socketFd: Int32, tag: Int, workqueue: DispatchQueue, logFunc:@escaping (_ logStr: String) -> Void) throws {
        self.sendMsg = msg
        self.tag = tag
        self.logFunc = logFunc
        
        super.init()
        
        self.tcpSocket = try GCDAsyncSocket(
            fromConnectedSocketFD: socketFd,
            delegate: self,
            delegateQueue: workqueue
        )
    }
    
    init(msg: String, host: String, port: UInt16, tag: Int, workqueue: DispatchQueue, logFunc:@escaping (_ logStr: String) -> Void) throws {
        self.sendMsg = msg
        self.tag = tag
        self.logFunc = logFunc
        
        super.init()
        
        self.tcpSocket = GCDAsyncSocket(delegate: self, delegateQueue: workqueue)
        try tcpSocket.connect(toHost: host.trimmingCharacters(in: .whitespaces),
                               onPort: port)
    }
    
    deinit {
        self.logFunc("[TCP][tag:\(self.tag)] deinit")
    }
    
    private var _executing: Bool = false {
        willSet {
            willChangeValue(forKey: #keyPath(isExecuting))
        }
        didSet {
            didChangeValue(forKey: #keyPath(isExecuting))
        }
    }
    
    private var _finished: Bool = false {
        willSet {
            willChangeValue(forKey: #keyPath(isFinished))
        }
        didSet {
            didChangeValue(forKey: #keyPath(isFinished))
        }
    }
    
    override func start() {
        _executing = true
        if isCancelled {
            done()
            return
        }
        let data = self.sendMsg.data(using: .utf8)
        self.tcpSocket.write(data, withTimeout: -1, tag: self.tag)
        self.logFunc("[TCP][tag:\(self.tag)] startSendMsg: \(self.sendMsg)")
    }

    override func cancel() {
        objc_sync_enter(self)
        done()
        objc_sync_exit(self)
    }
    
    func done() {
        super.cancel()
        if _executing {
            _finished = true
            _executing = false
        }
    }
}

extension TCPSendOperation {
    override var isExecuting: Bool {
        _executing
    }

    override var isFinished: Bool {
        _finished
    }

    override var isAsynchronous: Bool {
        true
    }
}

// MARK: GCDAsyncSocketDelegate
extension TCPSendOperation: GCDAsyncSocketDelegate {
    func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) {
        self.logFunc("[TCP][tag:\(self.tag)] host: \(host) port: \(port)")
    }
    func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) {
        let msg = String(data: data, encoding: .utf8)!
        self.logFunc("[TCP][tag:\(self.tag)] receive: \(msg)")
    }
    func socket(_ sock: GCDAsyncSocket, didWriteDataWithTag tag: Int) {
        self.logFunc("[TCP][tag:\(self.tag)] didSendMsg: \(self.sendMsg)")
        self.tcpSocket.readData(withTimeout: -1, tag: tag)
    }
    func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Error?) {
        self.logFunc("[TCP][tag:\(self.tag)] disconnect err: \(err?.localizedDescription ?? "")")
        done()
    }
}
