#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：CellFocusingPool.py
@Description：
@Author：XSH
@Time：2025/11/15 11:52
@Department：研发部
@Copyright：©2025 乐辰生物
"""
import gc
import cv2
import numpy as np
import math
import time
import os
import cv2
from AppInterfaces.Interfaces                               import *
from PyQt5                                                  import Qt
from AppInterfaces.utils                                    import *
from AppUtils.utils                                         import *
from AppCore.Requests                                       import *
from AppCore.AppDBModels.DBModels                           import *
from AppCore.Levels                                         import *
from AppSettings.Settings                                   import *
from AppInterfaces.EventInterfaces                          import AppEventInterface
from AppUtils.FocusingUtil                                  import FocusingUtil
from AppUtils.AppDBUtils                                    import AppDBUtils
from AppBackEnd.ProcessManager                              import ImageSaveProcess
from AppBackEnd.BackEndEventBus.AppTaskPool                 import AppTaskPool
from AppCore.AppAlgorithms.ImageAlgorithm                   import ImageAlgorithm
from AppCore.AppAlgorithms.ObbDetectorModel                 import ObbDetectorModel         #
from AppLog.logHelper import logSCANNING
class CellFocusingPool(Qt.QThread, AppEventInterface, SingletonInterface):
    AppendItem          : Qt.pyqtSignal         = Qt.pyqtSignal(object)
    _IOLocker           : Qt.QReadWriteLock     = Qt.QReadWriteLock()
    Locker              : Qt.QMutex             = Qt.QMutex()
    SendUi              : AppEventInterface         # 图像显示线程
    SendWeb             : AppEventInterface         # 网络发送线程
    CollectClassifier   : AlgorithmModelInterface  #

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

    def __init__(self, parent: Qt.QObject = None):
        super(CellFocusingPool, self).__init__(parent=parent)
        self.Initialization('CellFocusingPool', self, parent)
        self.Mode = EventLevel.QualityMode
        self.isShowFocusing     = True
        self.CollectClassifier  = None
        self.Start()



    def initConnect(self):
        super(CellFocusingPool, 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(CellFocusingPool, self).initValue()
        self.initPools()

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

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

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

    def appendItem(self, item: HigObjectiveCellModel, *args, **kwargs):
        if not self.isRunning():
            self.is_running = True
            self.start(Qt.QThread.HighestPriority)
        if isinstance(item, HigObjectiveCellModel):
            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.HighPriority)


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

    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.05)
                    continue
                item = self.DataBuffer.get()
                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, *args, **kwargs):
        """发送图像到界面 融合图像 保存图像 """
        if not isinstance(item, HigObjectiveCellModel) or not item.isAvailable:
            return
        start_datetime: Qt.QDateTime = Qt.QDateTime.currentDateTime()

        ExposureTime  : int
        imageParameter: ImageAnalysisModel = item.ImageParameter
        if not isinstance(imageParameter, ImageAnalysisModel):
            imageParameter = ImageAnalysisModel.getConfig(item.Classifier)
        if not isinstance(imageParameter, ImageAnalysisModel):
            return

        if imageParameter.is_focusing_hdr:
            if not item.hasImage:
                return
            if not item.hasIntegrationImages:
                item.IntegrationImages = []
            if len(item.ExposureTimes) != len(item.IntegrationImages):
                for ExposureTime in item.ExposureTimes:
                    if imageParameter.isFocusingHDR:
                        if isinstance(ExposureTime, int) and int(ExposureTime) == 0:
                            item.IntegrationImages.append(item.Image.copy())
                        elif isinstance(ExposureTime, float) and Float(ExposureTime) == 0.0:
                            item.IntegrationImages.append(item.Image.copy())
                        else:
                            item.IntegrationImages.append(ImageAlgorithm.shadow(item.Image.copy(), float(ExposureTime)))
                    else:
                        item.IntegrationImages.append(item.Image.copy())
                    AppTimeLoop.processGlobalEvents(1000)
        else:
            if not item.hasIntegrationImages or not item.hasExposureTimes:
                return
        if len(item.IntegrationImages) <= 0:
            return
        if not item.hasCaseID:
            if not item.hasCases:
                return

        self.initClassifier()
        Focusing : FocusingConfigModel  = item.Cell.Focusing
        MinStripCount : int = Focusing.MinStripCount
        MaxStripCount : int = Focusing.MaxStripCount
        StripCount    : int = None
        ImageScore    : int = None
        image         : np.ndarray
        """判断是否有条带"""
        for image in item.IntegrationImages:
            ImageScore, StripCount, _ = self.Classifier.process(item.Classifier, image.copy())
            if isinstance(StripCount, (int, float)) and isinstance(ImageScore, (int, float)):
                if MinStripCount <= StripCount <= MaxStripCount:
                    item.Cell.ImageScore = ImageScore
                    break
        if not isinstance(StripCount, (int, float)) or not isinstance(ImageScore, (int, float)):
            return
        item.Cell.ImageScore = ImageScore
        step1: int = start_datetime.msecsTo(Qt.QDateTime.currentDateTime())
        print(f'1 处理图像时间为:{step1}ms')
        if ImageScore <= 0 or StripCount < MinStripCount or StripCount > MaxStripCount:
            image = self.getIntegrationImage(item.IntegrationImages, imageParameter)
            if isinstance(image, np.ndarray):
                name: str = AppConvert.QDateTimeMsecToPath(Qt.QDateTime.currentDateTime(), '_') + f'_{StripCount}.jpg'
                savePath: str = f'{item.Cell.collect.save_address}/Other/{name}'
                info: Qt.QFileInfo = Qt.QFileInfo(savePath)
                self.SaveImage(info.absoluteFilePath(), image.copy())
            print(f'检测到核型条带数量为{StripCount} 不在阀值范围内 {StripCount} < {MinStripCount} or {StripCount} > {MaxStripCount} ')
            return
        """发送图像到界面"""
        item.CaseID = len(item.Cases) + 1
        AnalysisIntegrationImages: list[np.ndarray] = self.SendCellsToHeadEndAndSaveCell(item.CaseID, item.Cell, item.ExposureTimes, item.IntegrationImages, imageParameter)
        """融合图像"""
        if not isinstance(AnalysisIntegrationImages, list) or len(AnalysisIntegrationImages) <= 0:
            AnalysisIntegrationImages = item.IntegrationImages
        image = self.getIntegrationImage(AnalysisIntegrationImages, imageParameter)
        if not isinstance(image, np.ndarray):
            return
        drawImage: np.ndarray = ImageAlgorithm.processImage(im=image, parm=imageParameter, flag=False)

        if imageParameter.hasBigAnalysis and imageParameter.isBigAnalysis:
            image = drawImage.copy()

        step2: int = start_datetime.msecsTo(Qt.QDateTime.currentDateTime()) - step1
        print(f'2 处理图像时间为:{step2}ms')

        """发送图像到界面"""
        self.SendCellToHeadEnd(item.CaseID, image.copy())
        """保存图像"""
        self.SaveCellModel(item.Cell, item.CaseID, image, True)
        item.Cell.setImageSource(image)
        item.Cases.append(item.Cell)
        item.deleteLater()
        step3: int = start_datetime.msecsTo(Qt.QDateTime.currentDateTime()) - step2 - step1
        print(f'3 处理图像时间为:{step3}ms')
        gc.collect()
        # AppTimeLoop.processGlobalEvents(1000)
        return

    def getIntegrationImage(self, AnalysisIntegrationImages: list[np.ndarray], imageParameter: ImageAnalysisModel) -> np.ndarray:
        isGaussi: int = imageParameter.isGaussi
        image: np.ndarray
        if imageParameter.hasIntegration and imageParameter.isIntegration:
            image = ImageAlgorithm.ProcessIntegrationImages(images=AnalysisIntegrationImages, parm=imageParameter, isGaussi=isGaussi)
        elif imageParameter.hasFocusingHDR and imageParameter.isFocusingHDR:
            image = ImageAlgorithm.ProcessIntegrationImages(images=AnalysisIntegrationImages, parm=imageParameter, isGaussi=isGaussi)
        elif imageParameter.hasCameraHDR and imageParameter.isCameraHDR:
            image = ImageAlgorithm.ProcessIntegrationImages(images=AnalysisIntegrationImages, parm=imageParameter, isGaussi=isGaussi)
        else:
            image = AnalysisIntegrationImages[0]
        return image

    def SendCellsToHeadEndAndSaveCell(self, CaseID: int, item: SlideModelInterface, exposureTimes: list[int], images: list[np.ndarray], imageParameter: ImageAnalysisModel) -> list[np.ndarray]:
        slide               : CellModel
        index               : int
        image               : np.ndarray
        status              : int = ScanStatusLevel.Low_Four_Focus_Status
        draw_image          : np.ndarray
        AnalysisIntegrationImages: list[np.ndarray] = []
        try:
            for index, image in enumerate(images):
                slide = item.copy()
                slide.Level = status
                slide.Index = index + 1
                slide.ImageScore = exposureTimes[index]
                slide.FocusingSavePath = self.getCellSavePath(CaseID, slide)
                if imageParameter is not None:
                    """如果小图优化"""
                    if imageParameter.isAnalysis:
                        draw_image = ImageAlgorithm.processImage(image.copy(), imageParameter)
                    else:
                        draw_image = image
                else:
                    draw_image = image
                AnalysisIntegrationImages.append(draw_image)
                """保存图像"""
                if slide.hasPreview and slide.Preview.is_save:
                    if slide.has_FocusingSavePath:
                        try:
                            filename: str = slide.FocusingSavePath
                            self.SaveImage(filename, draw_image.copy())
                        except Exception as focusing_ex:
                            AppUtils.print_Exception(focusing_ex)
                self.sendweb.SendEvent(Response.VideoStream(draw_image.copy()))

                AppTimeLoop.processGlobalEvents(1000)

            pass
        except Exception as e:
            AppUtils.print_Exception(e)

        return AnalysisIntegrationImages


    def SendCellToHeadEnd(self, CaseID: int, drawImage: np.ndarray):
        try:
            self.sendweb.SendEvent(Response.PreviewImage(drawImage.copy()))
            self.sendweb.SendEvent(Response.SrcImageList(buffer=[drawImage.copy()]))
            # if HeadEndShowMode.get('DEBUG_SHOW', False):
            #     _image_ui = ImageUtils.cv2_to_QImage(drawImage.copy(), -1, -1)
            #     if isinstance(_image_ui, Qt.QImage) and not _image_ui.isNull():
            #         AppSlot.CameraUiSignal.emit(f'cell{CaseID}', _image_ui)
            #         AppTimeLoop.processGlobalEvents(1000)
        except Exception as e:
            AppUtils.print_Exception(e)


    @staticmethod
    def SaveCellModel(model: SlideModelInterface, CaseID: int, image: np.ndarray, isSaveYaml: bool = True):
        try:
            model.FocusingSavePath  = CellFocusingPool.getCellSavePath(CaseID, model)
            case_model      : SlideCaseModel    = SlideCaseModel    .copyInstance   (model)
            LoggerModel     : SlideLoggerModel  = SlideLoggerModel  .copyInstance   (model)
            case_model.ID = None
            case_model.Level = ScanStatusLevel.HigKaryotypePicture_Status
            case_model.Index = CaseID
            case_model.SavePath, _ = AppDBUtils.save_filename(case_model)
            case_model.InfoPath, _ = AppDBUtils.info_filename(case_model)
            case_model.setImageSource(None)
            SlideCaseModel.append_all([case_model])
            item: CellFocusingModel = CellFocusingModel()
            item.as_field(case_model.to_field(False))
            item.Level = ScanStatusLevel.HigKaryotypePicture_Status
            item.SavePath, _ = AppDBUtils.save_filename(item)
            item.InfoPath, _ = AppDBUtils.info_filename(item)
            CellFocusingModel.append_all([item])
            LoggerModel.ID          = None
            LoggerModel.Index       = CaseID
            LoggerModel.Case_ID     = case_model.ID
            LoggerModel.CaseModel   = case_model
            SlideLoggerModel.append_all([LoggerModel])

            AppEventInterface.SaveSlideModel(case_model, image.copy(), False, isSaveYaml)
            isWrong = RECODE_SUCCEED
        except Exception as e:
            AppUtils.print_Exception(e)
            isWrong = RECODE_FAILURE
        return isWrong

        pass

    """获取定焦图像保存路径"""
    @staticmethod
    def getCellSavePath(CaseID: int, model: SlideModelInterface) -> str:
        from AppSettings.AppSetting import AppSetting, AppConfig
        SpecimenPath: str
        try:
            if not model.has_SpecimenNumber:
                SpecimenPath: str = f'{model.CodeString}/'
            else:
                SpecimenPath: str = f'{model.SpecimenNumber}/slide{model.SlideNumber}/100X/'
            savePath: str = f'{AppConfig.PathConfig.absoluteFocusing_SavePath(SpecimenPath)}/cell{CaseID}/'
            filename: str = f'{savePath}/{model.index}_{model.ImageScore}.png'
        except Exception as e:
            AppUtils.print_Exception(e)
            filename = None
        return filename

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

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


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

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


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




    def initClassifier(self):
        if not self.hasClassifier:
            self.initCollectClassifier()

    def deleClassifier(self):
        self.deleCollectClassifier()
        self.CollectClassifier = None

    def initCollectClassifier   (self):
        if self.hasClassifier:
            return
            # 细胞识别模型 责任链 模式
        Classifiers: list[str] = CollectClassifierModel.Classifiers()
        models: list[ObbDetectorModel] = []
        self.CollectClassifier = ObbDetectorModel(level=Classifiers[0], parent=self)
        for index, name in enumerate(Classifiers):
            if index == 0:
                pass
            else:
                if self.isMicronucleus(name):
                    pass
                else:
                    models.append(ObbDetectorModel(level=name, parent=self.CollectClassifier))
        header: ObbDetectorModel = self.CollectClassifier
        for model in models:
            header.NextModel = model
            model.LastModel = header
            header = model

    def deleCollectClassifier   (self):
        if not self.hasClassifier:
            return
        try:
            models: list[ObbDetectorModel] = []
            Classifier: ObbDetectorModel = self.CollectClassifier
            while True:
                if Classifier is None:
                    break
                models.append(Classifier)
                Classifier = Classifier.NextModel

            for Classifier in models:
                if Classifier is not None:
                    Classifier.unloadModel()
                    Classifier.deleteLater()
        except Exception as e:
            pass
        self.CollectClassifier = None
        gc.collect()

    @property
    def Classifier(self) -> AlgorithmModelInterface:
        return None if not self.hasClassifier else self.CollectClassifier


    @property
    def hasClassifier(self) -> bool:
        return hasattr(self, 'CollectClassifier') and isinstance(self.CollectClassifier, AlgorithmModelInterface)