#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：SlideFocusingPool.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 PyQt5                                                  import Qt
from AppInterfaces.utils                                    import *
from AppSettings.Settings                                   import *
from AppUtils.utils                                         import *
from AppInterfaces.EventInterfaces                          import *
from AppCore.AppDBModels.DBModels                           import *
from AppCore.Requests                                       import *
from AppCore.Levels                                         import *
from AppUtils.AppUtils import AppUtils
from AppUtils.FocusingUtil                                  import FocusingUtil
from AppUtils.AppDBUtils                                    import AppDBUtils
from AppBackEnd.ProcessManager                              import ImageSaveProcess
from AppBackEnd.BackEndEventBus.AppTaskPool                 import AppTaskPool
class SlideFocusingPool(Qt.QThread, AppEventInterface, SingletonInterface):
    AppendItem      : Qt.pyqtSignal = Qt.pyqtSignal(object  )
    FocusingComplete: Qt.pyqtSignal = Qt.pyqtSignal(        )
    _IOLocker: Qt.QReadWriteLock = Qt.QReadWriteLock()
    Locker: Qt.QMutex = Qt.QMutex()
    Focusing            : list[SlideModelInterface]     #
    ImageAvailable      : list[int]                     #
    IsCell              : bool                          #
    IsGrab              : bool                          #
    IsComplete          : int                           #
    IsShowFocusing      : bool                          #
    SendUi              : AppEventInterface             # 图像显示线程
    SendWeb             : AppEventInterface             # 网络发送线程
    IsSecond            : bool                          #

    @classmethod
    def GetInstance(cls, *args, **kwargs) -> 'SlideFocusingPool':
        return super(SlideFocusingPool, cls).GetInstance(*args, **kwargs)

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

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

    def FocusingCompleteConnect(self, callback):
        if callback is not None:
            self.FocusingComplete.connect(callback)
    def FocusingCompleteDisconnect(self, callback):
        if callback is not None:
            self.FocusingComplete.disconnect(callback)

    def initValue(self):
        super(SlideFocusingPool, self).initValue()
        self.initFocusModels()
        self.IsShowFocusing = True
        self.IsSecond       = False
        self.is_async       = True

        self.initPools()

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

    def StartWork(self):
        self.initPools  ()
        self.initFocusModels()
        self.IsShowFocusing = True
        self.IsSecond = False
        self.Start      ()

    def StopWork(self):
        self.initFocusModels()
        self.delePools      ()


    def StartFocusing(self, isShowFocusing: bool = True, **args):
        self.initPools()
        self.initFocusModels()
        self.IsSecond = False
        self.IsShowFocusing = True if isinstance(isShowFocusing, (bool, int)) and isShowFocusing else False
        self.Start()


    def StopFocusing(self, isShowFocusing: bool = True, **args):
        self.initFocusModels()
        self.IsShowFocusing = True if isinstance(isShowFocusing, (bool, int)) and isShowFocusing else False
        self.IsSecond = False
        self.delePools      ()

    def initFocusModels(self):
        self.Focusing            = []
        self.ImageAvailable      = []
        self.IsCell              = False
        self.IsGrab              = False
        self.IsSecond            = False
        self.IsComplete          = 0


    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()


    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.1)
                    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))

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


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

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

    async  def async_WaitDone(self, item, *args, **kwargs):
        CellCount: int
        MinCellCount: int
        CellThreshold: int
        ImageScore: int
        FocusingThreshold: int
        MinFocusCount: int
        FocusCompleteCount: int
        _FocusCompleteCount: int
        MaxImageCount: int
        FocusingColorThreshold: int
        src_image: np.ndarray
        image: np.ndarray

        if not self.hasIsSecond:
            self.IsSecond = False
        try:
            if isinstance(item, SlideModelInterface):
                if not item.hasImageSource:
                    return
                if item.hasFocusing:
                    CellThreshold = item.Focusing.CellThreshold  #
                    MinCellCount = item.Focusing.MinCellCount  #
                    FocusingThreshold = item.Focusing.FocusingThreshold  #
                    FocusCompleteCount = item.Focusing.FocusingFinishCount  #
                    MaxImageCount = item.Focusing.MaxImageCount  #
                    MinContourArea = item.Focusing.MinContourArea  #
                    FocusingColorThreshold = item.Focusing.FocusingColorThreshold  #
                else:
                    CellThreshold, MinCellCount, FocusingThreshold, FocusingColorThreshold, FocusCompleteCount, MaxImageCount, MinContourArea = FocusingConfigModel.getFocusingConfig(
                        item.ObjectiveLevel, item.Level)
                isMicronucleus = ObjectiveLevel.isMicronucleus(item.ObjectiveLevel)
                MinCompleteNumber: int = max(20, int(MaxImageCount * 0.25))
                CompleteNumber: int = max(20, int(MaxImageCount * 0.25))
                if ScanStatusLevel.isLowFirstFocusing(item.Level):
                    CompleteNumber = max(20, int(MaxImageCount * 0.9))
                elif ScanStatusLevel.isHigFirstFocusing(item.Level):
                    CompleteNumber = max(20, int(MaxImageCount * 0.45))

                ImageScore = 0
                src_image = item.toImage()
                CellCount = FocusingUtil.get_cell_count(src_image, CellThreshold, MinContourArea)
                if isMicronucleus:
                    image = src_image[:, ::4, :]
                else:
                    image = src_image[:, ::2, :]
                self.IsGrab = CellCount >= MinCellCount
                if not self.isCell:
                    self.IsCell = self.isGrab
                if self.isCell and not self.isGrab:
                    if not self.IsSecond:
                        self.IsSecond = self.image_size >= MinCompleteNumber

                if self.isGrab:
                    ImageScore = FocusingUtil.sq_bre_14(image if isMicronucleus else image[:, :, 1], FocusingThreshold,
                                                        FocusingColorThreshold)
                    # print(f"细胞数量: {CellCount} {CellThreshold} {MinContourArea} ImageScore: {ImageScore} FocusingThreshold: {FocusingThreshold}")
                item.ImageScore = ImageScore

                self.Focusing.append(item)
                self.ImageAvailable.append(ImageScore)
                """判断当前评分是上升 还是下降"""
                isUpWard = self.isUpWard()
                if self.isComplete >= FocusCompleteCount:
                    """如果当前还在上升"""
                    if isUpWard > 0:
                        self.IsComplete = 0
                    elif self.image_size >= MinCompleteNumber:
                        self.FocusingComplete.emit()
                    else:
                        self.IsComplete += 1
                else:
                    if isUpWard < 0:
                        self.IsComplete += 1
                    elif isUpWard == 0:
                        if self.isCell:
                            if self.IsSecond:
                                self.IsComplete += 1
                            else:
                                if self.image_size >= CompleteNumber:
                                    self.IsComplete += 1
                        elif self.image_size >= CompleteNumber:
                            self.IsComplete += 1
                    else:
                        if self.isCell:
                            if self.IsSecond:
                                self.IsComplete += 1
                            elif self.image_size >= CompleteNumber:
                                self.IsComplete += 1
                        elif self.image_size >= CompleteNumber:
                            self.IsComplete += 1

                self.ItemSlot(item, image, {})
                if self.length > MaxImageCount:
                    self.IsComplete = FocusCompleteCount + 1
        except Exception as e:
            AppUtils.print_Exception(e)

    def WaitDone(self, item, *args, **kwargs):
        CellCount               : int
        MinCellCount            : int
        CellThreshold           : int
        ImageScore              : int
        FocusingThreshold       : int
        MinFocusCount           : int
        FocusCompleteCount      : int
        _FocusCompleteCount     : int
        MaxImageCount           : int
        FocusingColorThreshold  : int
        src_image               : np.ndarray
        image                   : np.ndarray

        if not self.hasIsSecond:
            self.IsSecond = False
        try:
            if isinstance(item, SlideModelInterface):
                if not item.hasImageSource:
                    return
                if item.hasFocusing:
                    CellThreshold           = item.Focusing.CellThreshold              #
                    MinCellCount            = item.Focusing.MinCellCount               #
                    FocusingThreshold       = item.Focusing.FocusingThreshold          #
                    FocusCompleteCount      = item.Focusing.FocusingFinishCount        #
                    MaxImageCount           = item.Focusing.MaxImageCount              #
                    MinContourArea          = item.Focusing.MinContourArea             #
                    FocusingColorThreshold  = item.Focusing.FocusingColorThreshold     #
                else:
                    CellThreshold, MinCellCount, FocusingThreshold, FocusingColorThreshold, FocusCompleteCount, MaxImageCount, MinContourArea = FocusingConfigModel.getFocusingConfig(item.ObjectiveLevel, item.Level)
                isMicronucleus = ObjectiveLevel.isMicronucleus(item.ObjectiveLevel)
                MinCompleteNumber: int = max(20, int(MaxImageCount * 0.25))
                CompleteNumber: int = max(20, int(MaxImageCount * 0.25))
                if ScanStatusLevel.isLowFirstFocusing(item.Level):
                    CompleteNumber = max(20, int(MaxImageCount * 0.9))
                elif ScanStatusLevel.isHigFirstFocusing(item.Level):
                    CompleteNumber = max(20, int(MaxImageCount * 0.45))

                ImageScore  = 0
                src_image   = item.toImage()
                CellCount   = FocusingUtil.get_cell_count(src_image, CellThreshold, MinContourArea)
                if isMicronucleus:
                    image = src_image[:, ::4, :]
                else:
                    image = src_image[:, ::2, :]
                self.IsGrab = CellCount >= MinCellCount
                if not self.isCell:
                    self.IsCell = self.isGrab
                if self.isCell and not self.isGrab:
                    if not self.IsSecond:
                        self.IsSecond = self.image_size >= MinCompleteNumber

                if self.isGrab:
                    ImageScore = FocusingUtil.sq_bre_14(image if isMicronucleus else image[:, :, 1], FocusingThreshold, FocusingColorThreshold)
                    # print(f"细胞数量: {CellCount} {CellThreshold} {MinContourArea} ImageScore: {ImageScore} FocusingThreshold: {FocusingThreshold}")
                item.ImageScore = ImageScore

                self.Focusing.append(item)
                self.ImageAvailable.append(ImageScore)
                """判断当前评分是上升 还是下降"""
                isUpWard = self.isUpWard()
                if self.isComplete >= FocusCompleteCount:
                    """如果当前还在上升"""
                    if isUpWard > 0:
                        self.IsComplete = 0
                    elif self.image_size >= MinCompleteNumber:
                        self.FocusingComplete.emit()
                    else:
                        self.IsComplete += 1
                else:
                    if isUpWard < 0:
                        self.IsComplete += 1
                    elif isUpWard == 0:
                        if self.isCell:
                            if self.IsSecond:
                                self.IsComplete += 1
                            else:
                                if self.image_size >= CompleteNumber:
                                    self.IsComplete += 1
                        elif self.image_size >= CompleteNumber:
                            self.IsComplete += 1
                    else:
                        if self.isCell:
                            if self.IsSecond:
                                self.IsComplete += 1
                            elif self.image_size >= CompleteNumber:
                                self.IsComplete += 1
                        elif self.image_size >= CompleteNumber:
                            self.IsComplete += 1

                self.ItemSlot(item, image, {})
                if not self.isAsync:
                    AppTimeLoop.processGlobalEvents(1000)
                if self.length > MaxImageCount:
                    self.IsComplete = FocusCompleteCount + 1
        except Exception as e:
            AppUtils.print_Exception(e)



    def ItemSlot(self, item: SlideModelInterface, image: np.ndarray, buffer: dict):

        try:
            if item.hasPreview and item.preview.is_save:
                if ObjectiveLevel.isHigObjective(item.ObjectiveLevel):
                    self.SaveSlideModel(item, image.copy(), False)

            if self.IsShowFocusing:
                if HeadEndShowMode.get('DEBUG_SHOW', False):
                    if self.hasSendUi:
                        self.sendUi.SendEvent(ImageEvent.instance(image=image.copy()), isAsync=self.isAsync)
                if self.hasSendWeb:
                    self.wendWeb.SendEvent(Response.VideoStream(buffer=image.copy()), isAsync=self.isAsync)
        except Exception as e:
            AppUtils.print_Exception(e)


    def getMaxScoreItem(self) -> SlideModelInterface:
        if self.hasFocusing and self.length > 0 and self.image_size > 0:
            MaxScoreIndex: int = int(np.argmax(self.ImageAvailable))
            item = self.Focusing[MaxScoreIndex].copy()
            return item
        return None

    def getMaxFocusingItem(self) -> (int, SlideModelInterface):
        if self.hasFocusing and self.length > 0 and self.image_size > 0:
            MaxScoreIndex: int = int(np.argmax(self.ImageAvailable))
            item = self.Focusing[MaxScoreIndex].copy()
            return MaxScoreIndex, item
        return None, None

    @property
    def isAvailable(self) -> bool:
        try:
            return self.isRunning() and self.hasSendWeb
        except Exception as e:
            return False

    def isUpWard(self) -> int:
        isUpWard: int = 1
        step    : int = 15
        try:
            isUpWard = FocusingUtil.isUpWard(self.imageAvailable, step)
        except Exception as e:
            AppUtils.print_Exception(e)
        return isUpWard


    @property
    def image_size(self) -> int:
        return len(self.ImageAvailable) if self.hasImageAvailable else 0

    @property
    def length(self) -> int:
        return len(self.Focusing) if self.hasFocusing else 0


    def initFocusing            (self):
        if not self.hasFocusing            :
            self.Focusing             = []

    def initImageAvailable      (self):
        if not self.hasImageAvailable      :
            self.ImageAvailable       = []

    def initIsCell              (self):
        if not self.hasIsCell              :
            self.IsCell               = False

    def initIsGrab              (self):
        if not self.hasIsGrab              :
            self.IsGrab               = False

    def initIsComplete          (self):
        if not self.hasIsComplete          :
            self.IsComplete           = 0

    def initIsSendUi      (self):
        if not self.hasIsSendUi      :
            self.IsSendUi       = True

    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 setFocusing            (self, value):
        if isinstance(value, list             ):
            self.Focusing             = value         #

    def setImageAvailable      (self, value):
        if isinstance(value, list             ):
            self.ImageAvailable       = value         #

    def setIsCell              (self, value):
        if isinstance(value, bool             ):
            self.IsCell               = value         #

    def setIsGrab              (self, value):
        if isinstance(value, bool             ):
            self.IsGrab               = value         #

    def setIsComplete          (self, value):
        if isinstance(value, int              ):
            self.IsComplete           = value         #

    def setIsShowFocusing      (self, value):
        if isinstance(value, bool             ):
            self.IsShowFocusing       = value         #

    def setSendUi        (self, value):
        if isinstance(value, AppEventInterface):
            self.SendUi         = value         #

    def setSendWeb         (self, value):
        if isinstance(value, AppEventInterface):
            self.SendWeb          = value         #


    @property
    def focusing            (self) -> list[SlideModelInterface] :
        return None if not self.hasFocusing             else self.Focusing                   #

    @property
    def imageAvailable      (self) -> list[int]                 :
        return None if not self.hasImageAvailable       else self.ImageAvailable             #

    @property
    def isCell              (self) -> bool                      :
        return None if not self.hasIsCell               else self.IsCell                     #

    @property
    def isGrab              (self) -> bool                      :
        return None if not self.hasIsGrab               else self.IsGrab                     #

    @property
    def isComplete          (self) -> int                       :
        return None if not self.hasIsComplete           else self.IsComplete                 #

    @property
    def isShowFocusing      (self) -> bool                      :
        return True if not self.hasIsShowFocusing       else self.IsShowFocusing             #

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

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

    @property
    def hasFocusing            (self) -> bool:
        return hasattr(self, 'Focusing'         ) and isinstance(self.Focusing            , list             )

    @property
    def hasImageAvailable      (self) -> bool:
        return hasattr(self, 'ImageAvailable'   ) and isinstance(self.ImageAvailable      , list             )

    @property
    def hasIsCell              (self) -> bool:
        return hasattr(self, 'IsCell'           ) and isinstance(self.IsCell              , bool             )

    @property
    def hasIsGrab              (self) -> bool:
        return hasattr(self, 'IsGrab'           ) and isinstance(self.IsGrab              , bool             )

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

    @property
    def hasIsSendUi      (self) -> bool:
        return hasattr(self, 'IsShowFocusing'   ) and isinstance(self.IsShowFocusing      , bool             )

    @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 hasIsSecond(self) -> bool:
        return hasattr(self, 'IsSecond') and isinstance(self.IsSecond, bool)