#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：SlideScanningPool.py
@Description：
@Author：XSH
@Time：2025/11/15 11:52
@Department：研发部
@Copyright：©2025 乐辰生物
"""
import asyncio
from qasync import QEventLoop, asyncSlot
import numpy as np
import cv2
from AppUtils.utils                                         import *
from AppCore.AppDBModels.DBModels                           import *
from AppCore.Levels                                         import *
from AppCore.Requests                                       import *
from AppSettings.Settings                                   import *
from AppInterfaces.AppAlgorithmInterfaces.AlgorithmManagerInterface import AlgorithmManagerInterface
from AppInterfaces.Interfaces import *
from AppSettings.SystemSetting import SystemConfig


class SlideScanningPool(Qt.QThread, AppEventInterface, SingletonInterface):
    AppendItem: Qt.pyqtSignal = Qt.pyqtSignal(object)
    _IOLocker: Qt.QReadWriteLock = Qt.QReadWriteLock()
    Locker: Qt.QMutex = Qt.QMutex()
    SendUi              : AppEventInterface         # 图像显示线程
    SendWeb             : AppEventInterface         # 网络发送线程
    Identify            : AlgorithmManagerInterface # 识别算法
    Index               : int                       #
    ImageW              : int                       #
    ImageH              : int                       #
    DrawW               : int                       #
    DrawH               : int                       #
    @classmethod
    def GetInstance(cls, *args, **kwargs) -> 'SlideScanningPool':
        return super(SlideScanningPool, cls).GetInstance(*args, **kwargs)

    def __init__(self, parent: Qt.QObject = None):
        super(SlideScanningPool, self).__init__(parent=parent)
        self.Initialization('SlideScanningPool', self, parent)
        self.Mode = EventLevel.OnlyDrawMode
        # self.Start()

    def initConnect(self):
        super(SlideScanningPool, self).initConnect()
        TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.AutoConnection | Qt.Qt.UniqueConnection)
        self.AppendItem.connect(slot=self.SlotAppendItem, type=TYPE)

    def initValue(self):
        super(SlideScanningPool, self).initValue()
        self.initPools()
        self.is_async = False


    def SendEvent(self, value, **args):
        self.AppendItem.emit(value)
        AppTimeLoop.processGlobalEvents(200)

    def StartWork(self):
        self.initPools      ()
        self.initDetector   ()
        self.initImageSize  ()
        self.Start          ()

    def StopWork(self):
        self.delePools      ()

    def StartFocusing(self, isShowFocusing: bool = True, **args):
        self.initPools()
        self.initDetector()
        self.initImageSize()
        self.Start()


    def StopFocusing(self, isShowFocusing: bool = True, **args):
        self.delePools      ()

    def appendItem(self, item, *args, **kwargs):
        if not self.isRunning():
            self.is_running = True
            self.start(Qt.QThread.NormalPriority)
        if isinstance(item, SlideModelInterface):
            with self.condition:
                self.DataBuffer.put(item)
                self.condition.notify_all()

    def Start(self):
        if self.isRunning():
            return
        self.initPools()
        self.is_running = True
        self.start(Qt.QThread.NormalPriority)

    def run(self) -> None:
        if self.isAsync:
            asyncio.run(self.async_process())
        else:
            self.process()

    async def async_process(self):
        self.process()

    def process(self):
        self.is_running = True
        self.is_finish = False
        while self.is_running:
            with self.condition:
                if not self.is_running:
                    break
                if self.DataBuffer.empty() and self.is_running:
                    self.condition.wait(0.01)
                    continue
                item = self.DataBuffer.get()
                # print("%-20s \t线程\tID:%-20s" % (self.__class__.__name__, threading.get_ident()), item)
                try:
                    if item is not None:
                        self.WaitDone(item)
                except Exception as e:
                    AppUtils.print_Exception(e)
                self.DataBuffer.task_done()

        self.is_running = False
        self.is_finish = True
        if isinstance(self, Qt.QThread):
            print("%-20s \t线程结束\tID:%-20s\n" % (self.__class__.__name__, self.currentThreadId().__int__()))
        elif isinstance(self, threading.Thread):
            print("%-20s \t线程结束\tID:%-20s\n" % (self.__class__.__name__, self.ident))

    def WaitDone(self, item: SlideModelInterface, *args, **kwargs):
        if not self.isAvailable:
            self.initPools()
        if isinstance(item, SlideModelInterface):
            self.SlideDetector(item)


    def SlideDetector(self, item: SlideModelInterface):
        image_w         : int
        image_h         : int
        draw_w          : int
        draw_h          : int
        image           : np.ndarray
        save_image      : np.ndarray
        empty           : np.ndarray
        filename        : str
        fmt             : str
        cellfilename    : str
        items           : list[CellModel    ] = []
        images          : list[np.ndarray   ] = []
        log_items       : list[AllCellModel ] = []
        cell_data       : dict[str, object  ]
        count           : int = 0
        roi_image       : np.ndarray
        in_situ_cultivation: int = None

        if not isinstance(item, SlideModelInterface):
            return
        if not item.hasImageSource:
            return
        if not self.hasSendWeb:
            self.initSendWeb()
        if not self.hasSendUi:
            self.initSendUi()
        image = item.toImage()

        if HeadEndShowMode.get('DEBUG_SHOW', False) and self.hasSendUi:
            self.sendui.SendEvent(ImageEvent.instance(image=image.copy()), isAsync=self.isAsync)
        if not self.ImageH or not self.hasImageW:
            self.flushImageSize()
        if not self.hasDetector:
            self.initDetector()
        if not self.hasDetector:
            return
        try:

            video_image = image.copy()
            empty       = np.zeros(image.shape, dtype=np.uint8)
            cell_data   = item.to_field()
            if not item.hasPreview:
                self.sendweb.SendEvent(Response.SlideAreaImage(buffer=empty), isAsync=self.isAsync)
                if item.hasPreview and item.preview.isMicronucleus():
                    self.sendweb.SendEvent(Response.MVideoStream(buffer=video_image), isAsync=self.isAsync)
                else:
                    self.sendweb.SendEvent(Response.VideoStream(buffer=video_image), isAsync=self.isAsync)
                return
            if not isinstance(in_situ_cultivation, (bool, int)):
                in_situ_cultivation = item.preview.in_situ_cultivation if item.preview.has_in_situ_cultivation else None
            if not item.preview.has_classifier:
                self.sendweb.SendEvent(Response.SlideAreaImage(buffer=empty), isAsync=self.isAsync)
                if item.hasPreview and item.preview.isMicronucleus():
                    self.sendweb.SendEvent(Response.MVideoStream(buffer=video_image), isAsync=self.isAsync)
                else:
                    self.sendweb.SendEvent(Response.VideoStream(buffer=video_image), isAsync=self.isAsync)
                return

            classifier: str = item.preview.classifier
            if not isinstance(classifier, str):
                self.websocket_pool.SendEvent(Response.SlideAreaImage(buffer=empty), isAsync=self.isAsync)
                if item.hasPreview and item.preview.isMicronucleus():
                    self.sendweb.SendEvent(Response.MVideoStream(buffer=video_image), isAsync=self.isAsync)
                else:
                    self.sendweb.SendEvent(Response.VideoStream(buffer=video_image), isAsync=self.isAsync)
                return

            save_image = image.copy()
            empty           = np.zeros(save_image.shape, dtype=np.uint8)
            boxs            : list = self.detector.process(level=classifier, image=image)
            if boxs is not None and len(boxs) > 0:
                if self.isMicronucleus(classifier):
                    roi_image = image.copy()
                else:
                    roi_image = image[:, :, 0]
                for index, box in enumerate(boxs):
                    cell    : CellModel     = CellModel()
                    cell    .as_field(cell_data, False)
                    cell    .setCellBox(box)
                    cell    .Level = ScanStatusLevel.Low_Identification_Cell_Status
                    ROW_W: int
                    ROW_H: int
                    image_size = int(max(cell.ImageCellWidth, cell.ImageCellHeight)) // 2
                    ROW_W = image_size + 50
                    roi_: np.ndarray = ImageUtils.image_ROI(roi_image, cell.ImageCellX, cell.ImageCellY, ROW_W, ROW_W)
                    if roi_ is not None:
                        roi = ImageUtils.image_resize(roi_, self.ImageW, self.ImageH)
                    else:
                        roi = None
                    x1: int = cell.ImageCellX - image_size
                    y1: int = cell.ImageCellY - image_size
                    x2: int = cell.ImageCellX + image_size
                    y2: int = cell.ImageCellY + image_size
                    imageScore: int = 0
                    if in_situ_cultivation:
                        cv2.rectangle(save_image, (x1, y1), (x2, y2), (0, 0, 255), 5)
                    cv2.rectangle(empty, (x1, y1), (x2, y2), (255, 255, 255), thickness=-1)
                    if roi is not None:
                        roi = ImageUtils.image_resize(roi, self.ImageW, self.ImageH)
                        if item.hasPreview and item.preview.is_save:
                            if self.isMicronucleus(classifier):
                                self.SaveSlideModel(cell, roi.copy (), True)
                            else:
                                self.SaveSlideModel(cell, roi.copy (), False)
                        cell.Camera.ImageSource = roi
                        cell.ImageScore         = imageScore
                        cell.ImageCellScore     = imageScore
                        cell.ID = None
                        images.append(roi)
                        items.append(cell)
                        log_items.append(AllCellModel.instance(**cell.to_field(False)))
                        count += 1
                    AppTimeLoop.processGlobalEvents(200)
                if len(items) > 0:
                    self.AppendCellModel(items, images)
                AppTimeLoop.processGlobalEvents(1000)
            empty = ImageUtils.image_resize(empty, SystemConfig.image.VideoStream_W, SystemConfig.image.VideoStream_H)
            """前端界面左边模板画面"""
            self.sendweb.SendEvent(Response.SlideAreaImage(buffer=empty), isAsync=self.isAsync)
            # """前端界面低部画面"""
            if item.hasPreview and item.preview.isMicronucleus():
                self.sendweb.SendEvent(Response.MVideoStream(buffer=video_image.copy()), isAsync=self.isAsync)
                """前端界面大屏画面"""
                self.sendweb.SendEvent(Response.MPreviewImage(buffer=video_image.copy()), isAsync=self.isAsync)
            else:
                self.sendweb.SendEvent(Response.VideoStream(buffer=video_image.copy()), isAsync=self.isAsync)
                """前端界面大屏画面"""
                self.sendweb.SendEvent(Response.PreviewImage(buffer=video_image.copy()), isAsync=self.isAsync)

            if item.hasPreview and item.preview.is_save:
                if ObjectiveLevel.isMicronucleus(item.ObjectiveLevel):
                    self.SaveSlideModel(item, image.copy(), True, True)
                elif item.preview.in_situ_cultivation:
                    """如果是训练模式 则不保存画框的图 保存原图"""
                    self.SaveSlideModel(item, save_image.copy(), False)
                else:
                    self.SaveSlideModel(item, image.copy(), False)
            AppTimeLoop.processGlobalEvents(1000)
        except Exception as e:
            AppUtils.print_Exception(e)
        self.Index = self.Index + count


    def initPools(self):
        self.initIdentify     ()
        self.initSendUi       ()
        self.initSendWeb      ()


    def delePools(self):
        self.SendUi        = None
        self.SendWeb       = None

    @property
    def isAvailable(self) -> bool:
        return self.isRunning() and self.hasSendUi and self.hasSendUi


    def initImageSize(self):
        self.initDetector()
        self.Index = 1
        self.ImageW , self.ImageH   = ImageConfigModel.ImageListSize()
        self.DrawW  , self.DrawH    = ImageConfigModel.DrawImageSize()

    def flushImageSize(self):
        self.initDetector()
        if not self.hasIndex:
            self.Index = 1
        self.ImageW , self.ImageH   = ImageConfigModel.ImageListSize()
        self.DrawW  , self.DrawH    = ImageConfigModel.DrawImageSize()

    def initSendUi              (self):
        if not self.hasSendUi              :
            from AppBackEnd.BackEndEventBus.OnlyDrawPool import OnlyDrawPool
            self.SendUi               = OnlyDrawPool.GetInstance()                    #

    def initSendWeb             (self):
        if not self.hasSendWeb             :
            from AppBackEnd.BackEndEventBus.WebSocketPool import WebSocketPool
            self.SendWeb              = WebSocketPool.GetInstance()                    #

    def initIdentify            (self):
        if not self.hasIdentify            :
            from AppSettings.AppGlobalAlgorithm import AppGlobalAlgorithm
            self.Identify             = AppGlobalAlgorithm.GetInstance()                    #

    def initIndex               (self):
        if not self.hasIndex               :
            self.Index                = 1                    #



    def initDetector(self):
        if not self.hasIdentify:
            self.initIdentify()
        if not self.hasIdentify:
            return
        if not self.hasDetector:
            self.Identify.initDetector()

    def deleDetector(self):
        if self.hasIdentify:
            self.Identify.deleDetector()


    @property
    def sendui              (self) -> AppEventInterface         :
        return None if not self.hasSendUi               else self.SendUi              #

    @property
    def sendweb             (self) -> AppEventInterface         :
        return None if not self.hasSendWeb              else self.SendWeb             #

    @property
    def identify            (self) -> AlgorithmManagerInterface :
        return None if not self.hasIdentify             else self.Identify            #

    @property
    def index               (self) -> int                       :
        return None if not self.hasIndex                else self.Index               #

    @property
    def imageW              (self) -> int                       :
        return None if not self.hasImageW               else self.ImageW              #

    @property
    def imageH              (self) -> int                       :
        return None if not self.hasImageH               else self.ImageH              #

    @property
    def drawW               (self) -> int                       :
        return None if not self.hasDrawW                else self.DrawW               #

    @property
    def drawH               (self) -> int                       :
        return None if not self.hasDrawH                else self.DrawH               #

    @property
    def detector(self) -> AlgorithmModelInterface:
        return None if not self.hasIdentify else self.Identify.Detector

    @property
    def hasDetector(self) -> bool:
        return self.hasIdentify and self.identify.hasPreviewClassifier

    @property
    def hasSendUi              (self) -> bool:
        return hasattr(self, 'SendUi'   ) and isinstance(self.SendUi              , AppEventInterface         )

    @property
    def hasSendWeb             (self) -> bool:
        return hasattr(self, 'SendWeb'  ) and isinstance(self.SendWeb             , AppEventInterface         )

    @property
    def hasIdentify            (self) -> bool:
        return hasattr(self, 'Identify' ) and isinstance(self.Identify            , AlgorithmManagerInterface )

    @property
    def hasIndex               (self) -> bool:
        return hasattr(self, 'Index'    ) and isinstance(self.Index               , int                       )

    @property
    def hasImageW              (self) -> bool:
        return hasattr(self, 'ImageW'   ) and isinstance(self.ImageW              , int                       )

    @property
    def hasImageH              (self) -> bool:
        return hasattr(self, 'ImageH'   ) and isinstance(self.ImageH              , int                       )

    @property
    def hasDrawW               (self) -> bool:
        return hasattr(self, 'DrawW'    ) and isinstance(self.DrawW               , int                       )

    @property
    def hasDrawH               (self) -> bool:
        return hasattr(self, 'DrawH'    ) and isinstance(self.DrawH               , int                       )

