#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：WebSocketImpl.py
@Description：
@Author：XSH
@Time：2025/11/12 9:32
@Department：研发部
@Copyright：©2025 乐辰生物
"""
from PyQt5                          import Qt
from AppContexts.Contexts           import WebSocketContext
from AppInterfaces.ImplInterfaces   import WebClientInterface
from AppInterfaces.AppTimeLoop      import AppTimeLoop
from AppUtils.utils                 import *
from AppCore.Requests               import Request, Response
from AppSettings.Settings           import (RECODE_FAILURE, RECODE_SUCCEED, SIGNAL_CONNECTION_TYPE)
from AppCore.Levels                 import WebAPILevel

class WebSocketImpl(Qt.QObject, WebClientInterface):
    IOLocker: Qt.QReadWriteLock = Qt.QReadWriteLock()
    ConnectedSignal     : Qt.pyqtSignal = Qt.pyqtSignal(object)
    DisconnectedSignal  : Qt.pyqtSignal = Qt.pyqtSignal(object)
    ReceivedFrame       : Qt.pyqtSignal = Qt.pyqtSignal(object)
    TransmitFrame       : Qt.pyqtSignal = Qt.pyqtSignal(object)

    def __init__(self, parent: Qt.QObject = None):
        super(WebSocketImpl, self).__init__(parent=parent)
        self.Initialization('WebSocketImpl', self, parent)

    @classmethod
    def instance(cls, context: WebSocketContext = None, socket: Qt.QWebSocket = None, parent: Qt.QObject = None) -> 'WebSocketClient':
        result: WebSocketClient = cls.__new__(cls)
        result.__init__(parent=parent)
        result.Parameter = context
        result.initManager(socket=socket)
        return result

    def initValue(self):
        super(WebSocketImpl, self).initValue()


    def initConnect(self):
        super(WebSocketImpl, self).initConnect()
        TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.QueuedConnection | Qt.Qt.UniqueConnection)
        self.TransmitFrame.connect(slot=self.slot_SocketTransmitFrame, type=TYPE)

    def deleConnect(self):
        super(WebSocketImpl, self).deleConnect()

    def initSocketConnect(self, socket: Qt.QWebSocket):
        if not isinstance(socket, Qt.QWebSocket):
            return
        TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.QueuedConnection | Qt.Qt.UniqueConnection)
        socket.textFrameReceived            .connect(slot=self.slot_SocketReadStringFrame, type=TYPE)
        socket.binaryFrameReceived          .connect(slot=self.slot_SocketReadBinaryFrame, type=TYPE)
        socket.stateChanged                 .connect(slot=self.slot_SocketStateChanged   , type=TYPE)
        socket.connected                    .connect(slot=self.slot_SocketConnected      , type=TYPE)
        socket.disconnected                 .connect(slot=self.slot_SocketDisconnected   , type=TYPE)
        pass

    def deleSocketConnect(self, socket: Qt.QWebSocket):
        if not isinstance(socket, Qt.QWebSocket):
            return
        self.Parameter.IsOpen = False
        socket.textFrameReceived            .disconnect()
        socket.binaryFrameReceived          .disconnect()
        socket.error                        .disconnect()
        socket.stateChanged                 .disconnect()
        socket.connected                    .disconnect()
        socket.disconnected                 .disconnect()

    def slot_SocketReadStringFrame  (self, buffer: str      , isLastFrame: bool):
        from AppLog.logHelper import logWEBSERVER, logWARNING
        if not isinstance(isLastFrame, (int, bool)) or not isLastFrame:
            return
        # Locker: Qt.QReadLocker = Qt.QReadLocker(self.IOLocker)
        request: Request = Request()
        try:
            request.asJson(buffer)
            request.Buffer = buffer
        except Exception as e:
            logWARNING << AppUtils.toString(e)
            return
        request.level = WebAPILevel.toValue(request.callback)
        request.Handler = self
        request.Buffer = buffer
        info = f"Address:{self.Manager.peerAddress().toString()} Port: {self.Manager.peerPort()} data: {buffer} {isLastFrame}"
        logWEBSERVER << info
        self.ReceivedFrame.emit(request)
        AppTimeLoop.processGlobalEvents(50)

        pass
    def slot_SocketReadBinaryFrame  (self, buffer: bytes    ):
        from AppLog.logHelper import logWEBSERVER, logWARNING
        request: Request = Request()
        try:
            string = buffer.decode('utf-8')
            request.asJson(string)
            request.Buffer = string
        except Exception as e:
            logWARNING << AppUtils.toString(e)
            return
        request.level = WebAPILevel.toValue(request.callback)
        request.Handler = self
        logWEBSERVER << f"Address:{self.Manager.peerAddress().toString()} Port: {self.Manager.peerPort()} data: {string}"
        self.ReceivedFrame.emit(request)
        AppTimeLoop.processGlobalEvents(50)

        pass
    def slot_SocketCurrentError     (self, error):
        socket: Qt.QWebSocket = self.sender()
        if not isinstance(socket, Qt.QWebSocket):
            return
        from AppLog.logHelper import logWEBSERVER
        logWEBSERVER << AppUtils.current_function_info(f"ADDR: {socket.peerAddress().toString()} Port: {socket.peerPort()} SocketError: {error} errorString: {socket.errorString()}")
        self.Parameter.IsOpen = socket.state() == Qt.QAbstractSocket.SocketState.ConnectedState
        pass

    def slot_SocketStateChanged     (self, state: int):
        socket: Qt.QWebSocket = self.sender()
        if not isinstance(socket, Qt.QWebSocket):
            return
        self.Parameter.IsOpen = state == Qt.QAbstractSocket.SocketState.ConnectedState
        print(f"slot_SocketStateChanged: {state}")
        if not self.Parameter.IsOpen:
            self.DisconnectedSignal.emit(Request.NoneWebAPI(handle=self))
            AppTimeLoop.processGlobalEvents(50)
        pass

    def slot_SocketConnected        (self):
        socket: Qt.QWebSocket = self.sender()
        if not isinstance(socket, Qt.QWebSocket):
            return
        self.Parameter.IsOpen = True
        self.Parameter.Address = socket.localAddress().toString()
        self.Parameter.Port    = socket.localPort()
        self.ConnectedSignal.emit(Request.NoneWebAPI(handle=self))
        AppTimeLoop.processGlobalEvents(50)
        pass

    def slot_SocketDisconnected     (self):
        self.Parameter.IsOpen = False
        self.DisconnectedSignal.emit(Request.NoneWebAPI(handle=self))
        AppTimeLoop.processGlobalEvents(50)

    def slot_SocketTransmitFrame        (self, buffer):
        if isinstance(buffer, str):
            self.TransmitString(buffer)
        elif isinstance(buffer, bytes) or isinstance(buffer, bytearray) or isinstance(buffer, Qt.QByteArray):
            self.TransmitBinary(buffer)
        else:
            self.TransmitBinary(buffer)
        pass


    def Open(self, timeOut: int = 1000, **args) -> int:
        waittimes: int = max(abs(timeOut), SOCKET_MAX_CONNECT_DELAY)
        if not self.isValid:
            self.initManager(self.Parameter)
        if not self.isValid:
            return RECODE_FAILURE
        if self.IsOpen and self.EqualSocket(self.Manager, self.Parameter):
            return RECODE_SUCCEED
        self.Parameter.IsOpen = False
        self.Manager.abort()
        self.Manager.open(Qt.QUrl(self.Parameter.API))
        endtime: Qt.QDateTime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
        while True:
            if Qt.QDateTime.currentDateTime() > endtime or self.Parameter.IsOpen:
                break
            AppTimeLoop.processGlobalEvents(SOCKET_DEFAULT_DELAY)
        return RECODE_SUCCEED if self.Parameter.IsOpen else RECODE_FAILURE


    def Close(self, timeOut: int = 1000, **args) -> int:
        waittimes: int = max(abs(timeOut), SOCKET_MAX_CONNECT_DELAY)
        if not self.isValid:
            return RECODE_SUCCEED
        if not self.IsOpen:
            return RECODE_SUCCEED
        self.Manager.abort()
        endtime: Qt.QDateTime = Qt.QDateTime.currentDateTime().addMSecs(waittimes)
        while True:
            if Qt.QDateTime.currentDateTime() > endtime or not self.Parameter.IsOpen:
                break
            AppTimeLoop.processGlobalEvents(SOCKET_DEFAULT_DELAY)
        self.deleManager()
        return RECODE_SUCCEED if self.Parameter.IsOpen else RECODE_FAILURE


    def initManager(self, **args):
        self.initContext()
        socket: Qt.QWebSocket = args.get('socket', None)
        if isinstance(socket, Qt.QWebSocket):
            if self.hasManager:
                if id(self.Manager) != id(socket):
                    self.Manager            .close()
                    self.deleSocketConnect(self.Manager)
                    try:
                        del self.Manager
                    except Exception as e:
                        AppUtils.print_Exception(e)
                    self.Manager            = socket
                    self.initSocketConnect(self.Manager)
            else:
                self.deleSocketConnect(self.manager)
                self.Manager = socket
                self.initSocketConnect(self.Manager)
        else:
            if not self.hasManager:
                self.Manager            = Qt.QWebSocket(parent=self)
                self.initSocketConnect(self.manager)


    def deleManager(self):
        self.initContext()
        if self.hasManager:
            try:
                self.Manager.abort()
                self.Manager.deleteLater()
            except Exception as e:
                AppUtils.print_Exception(e)
            self.Parameter.IsOpen = False
            self.Parameter.Descriptor = 0
            self.Manager = None



    def initContext(self):
        if not self.hasParameter:
            from AppSettings.AppSetting import AppConfig, AppSetting
            self.Parameter = AppConfig.WebSocket.copy()
        if not self.hasParameter:
            self.Parameter = WebSocketContext()

    def TransmitString(self, buffer, **args) -> int:
        sendBuffer: str
        if isinstance(buffer, str):
            sendBuffer = buffer
        elif isinstance(buffer, bytes) or isinstance(buffer, bytearray):
            sendBuffer = buffer.decode('utf-8')
        elif isinstance(buffer, Qt.QByteArray):
            sendBuffer = buffer.data().decode('utf-8')
        elif isinstance(buffer, Response):
            sendBuffer = buffer.toJson()
        elif isinstance(buffer, Request):
            sendBuffer = buffer.toJson()
        else:
            sendBuffer = f"{buffer}"
        self.SocketSendString(self.Manager, sendBuffer)

    def TransmitBinary(self, buffer, **args) -> int:
        sendBuffer: bytes
        if isinstance(buffer, str):
            sendBuffer = buffer.encode('utf-8')
        elif isinstance(buffer, bytes) or isinstance(buffer, bytearray):
            sendBuffer = buffer
        elif isinstance(buffer, Qt.QByteArray):
            sendBuffer = buffer.data()
        elif isinstance(buffer, Response):
            sendBuffer = buffer.toJson().encode('utf-8')
        elif isinstance(buffer, Request):
            sendBuffer = buffer.toJson().encode('utf-8')
        else:
            sendBuffer = f"{buffer}".encode('utf-8')
        self.SocketSendBinary(self.Manager, sendBuffer)


    def initIOLocker(self):
        if not self.hasLocker:
            self.IOLocker = Qt.QReadWriteLock()

    @property
    def hasLocker(self) -> bool:
        return hasattr(self, 'IOLocker') and isinstance(self.IOLocker, Qt.QReadWriteLock)