#!D:\IDE\Anaconda3\envs\Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName   : SlideScanCodeBusiness.py
@Account    : Administrator
@Time       : 2025/9/14 15:11
@Author     : xsh
@Description: 
"""
from PyQt5                                  import Qt
from abc                                    import abstractmethod, abstractclassmethod, abstractstaticmethod
from AppInterfaces.utils                    import *
from AppSettings.utils                      import *
from AppCore.Levels                         import *
from AppCore.Managers                       import *
from AppCore.Impls                          import *
from AppCore.Requests                       import *
from AppCore.DBModels                       import *
from AppUtils.utils                         import *
from AppCore.Events                         import *
from AppBackEnd.AppEventBusBusiness         import *
from AppCore.Readers                        import *
from AppSettings.AppGlobalStatus            import AppGlobalStatus, AppStatus
from AppSettings.AppGlobalAlgorithm         import AppGlobalAlgorithm
from AppBackEnd.AppBusinessController       import AppBusinessController, AppBusinessInterface
class SlideScanCodeBusiness(Qt.QObject, AppBusinessController):

    def __init__(self, parent: AppBusinessInterface = None):
        super(SlideScanCodeBusiness, self).__init__(parent=parent if parent is not None else parent.m_Control)
        self.initialize()
        self.ScanLevel = ScanStatusLevel.SlideScanCode_Status
        self.initModules(parent)

    def initModules(self, parent: AppBusinessInterface = None, **args):
        if isinstance(parent, AppBusinessInterface):
            self.Config                             = parent.config                       #
            self.SystemConfig                       = parent.system                       #
            self.HardWare                           = parent.devices                      #
            self.GlobalStatus                       = parent.status                       #
            self.Application                        = parent.application                  #
            self.Algorithm                          = parent.algorithm                    #
            self.EventBus                           = parent.eventBus                     #
            self.Process                            = parent.process                      #
            self.HitBotConfig                       = parent.hitbotConfig                 #


    def isValid(self) -> bool:
        return self.hasHitBot and self.hasGlobalStatus and self.hasSystemConfig and self.hasConfig

    @property
    def isAvailable(self) -> bool:
        return self.hasHitBot and self.hitbot.isAvailable

    def Done(self, ChamberNumber: int, *args, **kwargs):
        isWrong: int = RECODE_FAILURE
        self.status.setScanningStatus(self.ScanLevel)
        isSafe      : bool  = AppConvert.ObjectConvert(kwargs.get('isSafe'      , True), bool)
        RowNumber   : int   = AppConvert.ObjectConvert(kwargs.get('RowNumber'   , None), int)
        try:
            if isinstance(ChamberNumber, int) and isinstance(RowNumber, int):
                isWrong = self.SlideCodeScanning(ChamberNumber, RowNumber, isSafe)
        except Exception as e:
            AppUtils.print_Exception(e)
        if self.status.isExit:
            self.status.setScanningStatus(ScanStatusLevel.FinishScanning_Status)
        elif RECODE_SUCCEED == isWrong:
            self.status.setScanningStatus(self.ScanLevel)
        elif not ScanStatusLevel.isHitBotWarning(self.status.scanning_level):
            self.status.changedChamberScanningStatus(ChamberNumber, RowNumber, ScanStatusLevel.SlideScanCode_Warning_Status)
            self.status.setScanningStatus(ScanStatusLevel.SlideScanCode_Warning_Status)
        return

    def WaitDone(self, ChamberNumber: int, *args, **kwargs) -> int:
        isWrong: int = RECODE_FAILURE
        self.status.setScanningStatus(self.ScanLevel)
        isSafe      : bool  = AppConvert.ObjectConvert(kwargs.get('isSafe'      , True), bool)
        RowNumber   : int   = AppConvert.ObjectConvert(kwargs.get('RowNumber'   , None), int)
        try:
            if isinstance(ChamberNumber, int) and isinstance(RowNumber, int):
                isWrong = self.SlideCodeScanning(ChamberNumber, RowNumber, isSafe)
        except Exception as e:
            AppUtils.print_Exception(e)
        if self.status.isExit:
            self.status.setScanningStatus(ScanStatusLevel.FinishScanning_Status)
        elif RECODE_SUCCEED == isWrong:
            self.status.setScanningStatus(self.ScanLevel)
        elif not ScanStatusLevel.isHitBotWarning(self.status.scanning_level):
            self.status.changedChamberScanningStatus(ChamberNumber, RowNumber, ScanStatusLevel.SlideScanCode_Warning_Status)
            self.status.setScanningStatus(ScanStatusLevel.SlideScanCode_Warning_Status)
        return isWrong

    def SlideCodeScanning(self, chamber_number: int, row_number: int, isSafe: bool = True) -> int:
        ScanCount           : int
        MaxScanCodeCount    : int
        isWrong             : int                           = RECODE_FAILURE
        CodeString          : str
        SpecimenNumber      : str
        SlideNumber         : str
        ScanCodeActions     : list[HitBotCodeScanAction]

        isWrong, ScanCodeActions = self.readyScanCode(chamber_number, row_number)
        if RECODE_SUCCEED != isWrong or not isinstance(ScanCodeActions, list):
            return RECODE_FAILURE
        if len(ScanCodeActions) <= 0:
            return RECODE_FAILURE

        """处理暂停 退出"""
        while self.status.waitForPauseRunning():
            AppTimeLoop.processEvents(self.system.WarningWaitTimes())
        if self.status.waitForExitRunning():
            return RECODE_SUCCEED
        CodeString          = None
        if chamber_number == 5 or chamber_number == 10:
            ScanCodeActions[0].speed = self.system.HitBotSpeed5
        elif chamber_number == 4 or chamber_number == 9:
            ScanCodeActions[0].speed = self.system.HitBotSpeed4
        isWrong = self.waitForScanCodeOpen()

        if RECODE_SUCCEED == isWrong:
            for Action in ScanCodeActions:
                self.waitForSucceedMoving(Action)
                CodeString = self.scancode.ReadScanningCode(500)
                isWrong = RECODE_SUCCEED if isinstance(CodeString, str) and len(CodeString) > 0 else RECODE_FAILURE
                if RECODE_SUCCEED == isWrong:
                    break
            isWrong = RECODE_SUCCEED if isinstance(CodeString, str) and len(CodeString) > 0 else RECODE_FAILURE
            if RECODE_SUCCEED != isWrong:
                CodeString = self.scancode.ReadScanningCode(500)
        if isinstance(CodeString, str) and len(CodeString) > 0:
            CodeString = CodeString.replace('\r\n', '')
            CodeString = CodeString.replace('\n\r', '')
        CodeString, SpecimenNumber, SlideNumber = self.SetChamberCodeString(chamber_number, row_number, CodeString)
        if not isinstance(SlideNumber, str):
            SlideNumber = f'{row_number}'
        if not isinstance(SpecimenNumber, str):
            SpecimenNumber = CodeString

        isWrong = RECODE_SUCCEED if isinstance(SpecimenNumber, str) and len(SpecimenNumber) > 0 and isinstance(SlideNumber, str) else RECODE_FAILURE

        return isWrong

    def waitForScanCodeOpen(self) -> int:
        if not self.hasDevices:
            self.initDevices()
        if not self.hasDevices:
            return RECODE_FAILURE
        if not self.hasScanCode:
            self.initScanCode()
        if not self.hasScanCode:
            return RECODE_FAILURE
        if not self.scancode.isAvailable:
            self.scancode.ReOpen()
        if not self.scancode.isAvailable:
            return RECODE_FAILURE
        return RECODE_SUCCEED if self.scancode.isAvailable else RECODE_FAILURE

    def SetChamberCodeString(self, ChamberNumber: int, RowNumber: int, CodeString: str):
        SpecimenNumber  : str = None
        SlideNumber     : str = None
        CustomCodeString: str = None
        from AppLog.logHelper import logSERIAL
        if not isinstance(CodeString, str):
            return CustomCodeString, SpecimenNumber, SlideNumber
        try:
            Template: ScanTemplate
            """判断当前玻片是否为自定义模板"""
            IsAutoScanCode = CustomScanningModel.IsAutoScanCode(ChamberNumber, RowNumber)
            if IsAutoScanCode:
                """自动匹配模板"""
                Template = ScanTemplate.getTemplate(CodeString)
            else:
                """自定义模板"""
                Template = CustomScanningModel.getTemplate(ChamberNumber, RowNumber)
            if Template is None:
                if IsAutoScanCode:
                    logSERIAL << f'片仓{ChamberNumber}{RowNumber}行玻片样本号{CodeString}自动匹配模板失败'
                else:
                    logSERIAL << f'片仓{ChamberNumber}{RowNumber}行玻片样本号{CodeString}没有选择自定义模板'
                return CustomCodeString, SpecimenNumber, SlideNumber

            separator_no: str = f'{Template.separator_no}'
            specimen: str

            if isinstance(CodeString, str) and len(CodeString) > 0:
                CustomCodeString = f'{CodeString}'
            else:
                if not IsAutoScanCode:
                    CustomSpecimen: str = CustomScanningModel.getCustomSpecimen(ChamberNumber, RowNumber)
                    CustomCodeString = CustomSpecimen if isinstance(CustomSpecimen, str) and len(
                        CustomSpecimen > 0) else None

            if isinstance(CustomCodeString, str) and len(CustomCodeString) > 0:
                """根据模板解析出样本号 玻片号"""
                Buffer: list[str] = CustomCodeString.split(separator_no)
                SlideNumber = f'{Buffer[-1]}' if len(Buffer) > 1 else None
                SlideNumber_Number = AppConvert.ObjectConvert(SlideNumber, int)

                """玻片号为二维码最后一位 如果二维码不能分割 则玻片号为行号"""
                """样本号为 二维码号中除了玻片号的字符串"""
                if SlideNumber_Number is None:
                    SlideNumber = f'{RowNumber}'
                    SpecimenNumber = f'{CustomCodeString}'
                elif len(Buffer) == 1:
                    SlideNumber = f'{RowNumber}'
                    SpecimenNumber = f'{Buffer[0]}'
                else:
                    SlideNumber = f'{SlideNumber_Number}'
                    maxlength: int = len(Buffer) - 1
                    for index in range(0, maxlength):
                        if index == maxlength - 1:
                            SpecimenNumber += f'{Buffer[index]}'
                        else:
                            SpecimenNumber += f'{Buffer[index]}{separator_no}'

            logSERIAL << f"解析样本号: {CustomCodeString}  {SpecimenNumber} {SlideNumber}"
            CustomScanningModel.setChamberCodeString(ChamberNumber, RowNumber, CustomCodeString, SpecimenNumber, SlideNumber)
            ChamberStatusModel.setChamberCodeString(ChamberNumber, RowNumber, CustomCodeString, SpecimenNumber, SlideNumber)
        except Exception as e:
            AppUtils.print_Exception(e)
            SpecimenNumber  = None
            SlideNumber     = None

        return CustomCodeString, SpecimenNumber, SlideNumber

    def readyScanCode(self, chamber_number: int, row_number: int) -> (int, list):
        isWrong             : int                           = RECODE_FAILURE
        ScanCodeActions     : list[HitBotCodeScanAction]    = []
        MinChamberNumber    : int
        MaxChamberNumber    : int
        MinRowNumber        : int
        MaxRowNumber        : int
        MinRowNumber    , MaxRowNumber       = self.system.getRowRange      ()
        MinChamberNumber, MaxChamberNumber   = self.system.getChamberRange  ()
        if MinChamberNumber <= chamber_number <= MaxChamberNumber and MinRowNumber <= row_number <= MaxRowNumber:
            if not self.isValid():
                return RECODE_FAILURE, ScanCodeActions
            if not self.isAvailable:
                return RECODE_FAILURE, ScanCodeActions
            if not self.hitbotConfig.hitbotIsClamp():
                return RECODE_FAILURE, ScanCodeActions
            ScanCodeActions = HitBotCodeScanAction.getCodeScanActions(chamber_number, row_number)

            isWrong = RECODE_SUCCEED if isinstance(ScanCodeActions, list) and len(ScanCodeActions) > 0 else RECODE_FAILURE

        return isWrong, ScanCodeActions