#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：FocusingOperation.py
@Description：
@Author：XSH
@Time：2025/12/22 10:11
@Department：研发部
@Copyright：©2025 乐辰生物
"""
from abc                                    import abstractmethod                       #
from PyQt5                                  import Qt                                   #
from AppInterfaces.UiInterface              import UiInterface                          #
from AppInterfaces.globalSlot               import globalSlot, AppSlot                  #
from AppCore.Contexts                       import *                                    #
from AppCore.DBModels                       import *                                    #
from AppCore.Levels                         import *                                    #
from AppUtils.utils                         import *                                    #
from AppSettings.Settings                   import *                                    #
from AppHeadEnd.DBModelViews.ActionMenuBar  import ActionMenuBar                        #
# 263步是1um 263158 是1mm 131579 0.5mm 2630是10um
MIC_MAX_MOVE_VELOCITY   : float = 263158
MIC_MAX_MOVE_LENGTH     : int   = 263158
class FocusingOperation(Qt.QTableWidget, UiInterface):
    PositionChangedSignal: Qt.pyqtSignal = Qt.pyqtSignal(object)
    Menus       : ActionMenuBar                #
    Items       : list[FocusingContext]        #
    Context     : FocusingContext              #
    Objective   : ObjectiveModel               #
    Offset      : ObjectiveOffsetModel         #
    Camera      : CameraModel                  #
    Anointing   : AnointingModel               #

    def __init__(self, parent: Qt.QWidget = None):
        super(FocusingOperation, self).__init__(parent=parent)
        self.Initialization('FocusingOperation', self, parent)

    def initConnect(self):
        super(FocusingOperation, self).initConnect()
        TYPE: Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.AutoConnection | Qt.Qt.UniqueConnection)
        self.Menus      .ActionSignal   .connect(self.SlotActionSignal  , TYPE)
        self.model()    .dataChanged    .connect(self.SlotDataChanged   , TYPE)


    def setObjective(self, objective: int):
        if not self.hasItems:
            return
        data: FocusingContext = None
        row : int = -1
        for index, item in enumerate(self.Items):
            if isinstance(item, FocusingContext) and item.Level == objective:
                data = item
                row = index
                break
        if not isinstance(data, FocusingContext) or row < 0:
            return
        item: Qt.QTableWidgetItem = self.item(row, 0)
        self.setCurrentCell(row, 0)
        self.setCurrentItem(item)

    def changedCalibrate(self, objective: int, enable: (bool, int)):
        if not self.hasItems:
            return
        data: FocusingContext = None
        row: int = -1
        for index, item in enumerate(self.Items):
            if isinstance(item, FocusingContext) and item.Level == objective:
                data = item
                row = index
                break
        if not isinstance(data, FocusingContext) or row < 0:
            return
        data = self.Items[row]
        data.IsCalibrate = 1 if isinstance(enable, (bool, int)) and enable else 0
        self.flushItems()

    def setCalibrate(self, enable: (bool, int)):
        if not self.hasItems:
            return
        for row, data in enumerate(self.Items):
            data = self.Items[row]
            data.IsCalibrate = 1 if isinstance(enable, (bool, int)) and enable else 0
        self.flushItems()

    def changedPosition(self, objective: int, x: (int, float), y: (int, float), z: (int, float)):
        if not self.hasItems:
            return
        data: FocusingContext = None
        row: int = -1
        for index, item in enumerate(self.Items):
            if isinstance(item, FocusingContext) and item.Level == objective:
                data = item
                row = index
                break
        if not isinstance(data, FocusingContext) or row < 0:
            return
        data = self.Items[row]
        data.XPosition = x
        data.YPosition = y
        data.ZPosition = z
        self.flushItems()

    def changedOffset(self, objective: int, x: (int, float), y: (int, float), z: (int, float)):
        if not self.hasItems:
            return
        data: FocusingContext = None
        row: int = -1
        for index, item in enumerate(self.Items):
            if isinstance(item, FocusingContext) and item.Level == objective:
                data = item
                row = index
                break
        if not isinstance(data, FocusingContext) or row < 0:
            return
        data = self.Items[row]
        data.OffsetX = x
        data.OffsetY = y
        data.OffsetZ = z
        self.flushItems()

    def mouseReleaseEvent(self, ev: Qt.QMouseEvent) -> None:
        if ev.button() == Qt.Qt.MouseButton.RightButton:
            self.Menus.draw(ev.globalPos())
            ev.accept()
        else:
            return super(FocusingOperation, self).mouseReleaseEvent(ev)

    def SlotActionSignal(self, signal: str):
        """['刷新数据', '移动焦点', '保存数据']"""
        if signal == '刷新数据':
            self.initItems()
            self.flushItems()
        elif signal == '移动焦点':
            items: list[Qt.QTableWidgetItem]  = self.selectedItems()
            if len(items) <= 0:
                return
            item: Qt.QTableWidgetItem = items[0]
            data        ,Objective   ,Offset      ,Camera      ,Anointing    = self.getItemProperty(item)
            if not isinstance(data, FocusingContext) or not isinstance(Objective, ObjectiveModel):
                return
            self.PositionChangedSignal.emit(data)
        elif signal == '保存数据':
            self.updateItems()
            self.saveItems()
        pass

    def SlotDataChanged(self, topLeft: Qt.QModelIndex, bottomRight: Qt.QModelIndex, roles: list[int]):
        """数据改变"""
        if topLeft.isValid():
            row: int = topLeft.row()
            col: int = topLeft.column()

            self.updateItem(row, col)
            return

        if bottomRight.isValid():
            row: int = bottomRight.row()
            col: int = bottomRight.column()
            self.updateItem(row, col)
            return
    def saveItems(self):
        if not self.hasItems:
            return
        Objective   : ObjectiveModel               #
        Offset      : ObjectiveOffsetModel         #
        Camera      : CameraModel                  #
        Anointing   : AnointingModel               #
        for row, data in enumerate(self.Items):
            ObjectiveModel.changed({ObjectiveModel.Focus_Lower_Limit: int(data.ZPosition - MIC_MAX_MOVE_LENGTH),
                                    ObjectiveModel.Focus_Upper_Limit: int(data.ZPosition + MIC_MAX_MOVE_LENGTH),
                                    }, ObjectiveModel.Level == data.Level)
            ObjectiveOffsetModel.changed(
                {
                    ObjectiveOffsetModel.XPosition    : data.XPosition      ,
                    ObjectiveOffsetModel.YPosition    : data.YPosition      ,
                    ObjectiveOffsetModel.ZPosition    : data.ZPosition      ,
                    ObjectiveOffsetModel.ExposureTime : data.ExposureTime   ,
                    ObjectiveOffsetModel.OffsetX      : data.OffsetX        ,
                    ObjectiveOffsetModel.OffsetY      : data.OffsetY        ,
                    ObjectiveOffsetModel.OffsetZ      : data.OffsetZ        ,
                    ObjectiveOffsetModel.IsCalibrate  : 1                   ,
                }, ObjectiveOffsetModel.Level == data.Level
            )
            CameraModel.changed(
                {
                    CameraModel.ExposureTime                : data.ExposureTime ,
                    CameraModel.AberrationExposureTime      : data.ExposureTime ,
                    CameraModel.KaryotypingExposureTime     : data.ExposureTime ,
                }, CameraModel.Level == data.Level
            )
            # Anointing


    def updateItems(self):
        if not self.hasItems:
            return
        for row, data in enumerate(self.Items):
            for col, slot in enumerate(FocusingContext.__slots__):
                self.updateItemData(row, col, data)


    def updateItemData(self, row: int, column: int, data: FocusingContext):
        item: Qt.QTableWidgetItem = self.item(row, column)
        if isinstance(item, Qt.QTableWidgetItem):
            try:
                value = AppConvert.ObjectConvert(item.text(), FocusingContext.__types__[column])
                setattr(data, FocusingContext.__slots__[column], value)
            except Exception as e:
                AppUtils.print_Exception(e)
                return

    def updateItem(self, row: int, column: int):
        item: Qt.QTableWidgetItem = self.item(row, column)
        if isinstance(item, Qt.QTableWidgetItem):
            try:
                data: FocusingContext = self.Items[row]
                property_name: str = FocusingContext.__slots__[column]          # 属性名称
                value = AppConvert.ObjectConvert(item.text(), FocusingContext.__types__[column])
                setattr(data, FocusingContext.__slots__[column], value)
                if property_name == 'ExposureTime' and isinstance(value, (int, float)):
                    CameraModel.changed(
                        {
                            CameraModel.ExposureTime            : value,
                            CameraModel.AberrationExposureTime  : value,
                            CameraModel.KaryotypingExposureTime : value,
                            CameraModel.MicronucleusExposureTime: value,
                        }, CameraModel.Level == data.Level)
                    from AppCore.Readers import CameraReader
                    CameraReader.GetInstance().setModel(data.Level)

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

    def initValue(self):
        super(FocusingOperation, self).initValue()
        self.initMenus()
        self.initItems()

    def setHeaderLabels(self):
        max_length: int = len(FocusingContext.__slots__)
        self.setColumnCount(max_length)
        self.setHorizontalHeaderLabels(FocusingContext.__slots__)
        ColumnWidth = [200, 50, 100, 100, 100, 80, 80, 80, 80, 50]
        for index in range(max_length):
            self.setColumnWidth(index, ColumnWidth[index])
        # for index in range(max_length):
        #     self.horizontalHeader().setSectionResizeMode(index, Qt.QHeaderView.Interactive)

    def initProperty(self):
        super(FocusingOperation, self).initProperty()
        # 设置全部单元格可编辑
        self.setHeaderLabels()
        header = self.horizontalHeader()
        header.setSectionResizeMode(Qt.QHeaderView.Stretch)  # Make columns stretch to fill width
        # header.setSectionResizeMode(0, Qt.QHeaderView.Interactive)

        header.setStretchLastSection(True)  # Stretch last column
        header.setSectionResizeMode(Qt.QHeaderView.Custom)
        # 设置表头字体大小
        headerFont = header.font()
        headerFont.setPixelSize(10)
        header.setFont(headerFont)

        self.setHorizontalHeader(header)
        self.verticalHeader().setDefaultSectionSize(40)
        self.setSelectionBehavior(Qt.QTableWidget.SelectRows)  # Select entire rows
        self.setSelectionMode(Qt.QTableWidget.SingleSelection)  # Single row selection
        # self.setEditTriggers(Qt.QTableWidget.NoEditTriggers)  # Read-only
        self.setAlternatingRowColors(False)  # 禁用隔行变色
        self.setShowGrid(True)  # Show grid lines
        self.verticalHeader().setVisible(False)  # Hide row numbers
        header.setSectionResizeMode(Qt.QHeaderView.Fixed)


    def setItemData(self, column: int, item: Qt.QTableWidgetItem, data: FocusingContext):
        if not isinstance(item, Qt.QTableWidgetItem):
            return
        if isinstance(data, FocusingContext):
            try:
                value = getattr(data, data.__slots__[column], None)
            except Exception as e:
                AppUtils.print_Exception(e)
                value = None
            item.setText(f'{value}' if value is not None else '')
            setattr(item, 'data', data)

    def getItemData(self, column: int, item: Qt.QTableWidgetItem):
        value = None
        if not isinstance(item, Qt.QTableWidgetItem):
            return value
        data: FocusingContext = getattr(item, 'data', None)  #
        if isinstance(data, FocusingContext):
            try:
                value = AppConvert.ObjectConvert(getattr(data, data.__slots__[column], None), data.__types__[column])
            except Exception as e:
                AppUtils.print_Exception(e)
                value = None
        return value

    def setItemProperty(self, column: int, item: Qt.QTableWidgetItem, data: FocusingContext, Objective: ObjectiveModel, Offset: ObjectiveOffsetModel, Camera: CameraModel, Anointing: AnointingModel):
        if not isinstance(item, Qt.QTableWidgetItem):
            return
        if isinstance(data, FocusingContext):
            self.setItemData(column, item, data)
            data.Objective      = Objective     #
            data.Offset         = Offset        #
            data.Camera         = Camera        #
            data.Anointing      = Anointing     #
        setattr(item, 'data'        , data      )
        setattr(item, 'Objective'   , Objective )
        setattr(item, 'Offset'      , Offset    )
        setattr(item, 'Camera'      , Camera    )
        setattr(item, 'Anointing'   , Anointing )


    def getItemProperty(self, item: Qt.QTableWidgetItem) -> (FocusingContext, ObjectiveModel, ObjectiveOffsetModel, CameraModel, AnointingModel):
        data        : FocusingContext              = None           #
        Objective   : ObjectiveModel               = None           #
        Offset      : ObjectiveOffsetModel         = None           #
        Camera      : CameraModel                  = None           #
        Anointing   : AnointingModel               = None           #
        if not isinstance(item, Qt.QTableWidgetItem):
            return data, Objective, Offset, Camera, Anointing
        data        = getattr(item, 'data'        , None)
        Objective   = getattr(item, 'Objective'   , None)
        Offset      = getattr(item, 'Offset'      , None)
        Camera      = getattr(item, 'Camera'      , None)
        Anointing   = getattr(item, 'Anointing'   , None)
        return data, Objective, Offset, Camera, Anointing








    def initMenus(self):
        if self.hasMenus:
            return
        self.Menus = ActionMenuBar(['刷新数据', '移动焦点', '保存数据'])

    def getCalibrateData(self, objective: int):
        data: FocusingContext = None
        if not self.hasItems:
            return data
        for item in self.Items:
            if isinstance(item, FocusingContext) and item.Level == objective:
                data = item
                break
        return data

    def setCalibrateData(self, objective: int, data: FocusingContext):
        if not self.hasItems:
            return
        for item in self.Items:
            if isinstance(item, FocusingContext) and item.Level == objective:
                item.deserialize(data.serialize())
                break

    def initItems(self):
        objectives  : list[int]                     #
        objective   : int                           #
        data        : FocusingContext               #
        Objective   : ObjectiveModel                #
        Offset      : ObjectiveOffsetModel          #
        Camera      : CameraModel                   #
        Anointing   : AnointingModel                #
        if not self.hasItems:
            self.Items = []
            objectives = ObjectiveLevel.all()
            tray_model: TrayModel = TrayModel.getConfig(TrayLevel.TraySlide3)
            for objective in objectives:
                Objective   = ObjectiveModel        .getConfig(objective)   #
                Offset      = ObjectiveOffsetModel  .getConfig(objective)   #
                Camera      = CameraModel           .getConfig(objective)   #
                Anointing   = AnointingModel        .getConfig(objective)   #
                data        = FocusingContext()
                data.Information                     = Objective.Information     #
                data.Level                           = objective                 #
                if not data.hasZPosition:
                    if Offset.hasZPosition:
                        data.ZPosition = Offset.ZPosition
                    else:
                        data.ZPosition                       = Objective.Focus_Lower_Limit + (Objective.Focus_Upper_Limit - Objective.Focus_Lower_Limit) // 2      #
                if not data.hasXPosition:
                    if Offset.hasXPosition:
                        data.XPosition = Offset.XPosition
                    else:
                        data.XPosition                       = tray_model.Position_X  #
                if not data.hasYPosition:
                    if Offset.hasYPosition:
                        data.YPosition                       = Offset.YPosition
                    else:
                        data.YPosition                       = tray_model.Position_Y  #

                data.ExposureTime                    = Camera.ExposureTime       #
                data.OffsetZ                         = Offset.OffsetZ            #
                data.OffsetX                         = Offset.OffsetX            #
                data.OffsetY                         = Offset.OffsetY            #
                data.IsCalibrate                     = Offset.IsCalibrate        #
                data.Offset                          = Offset                    #
                data.Camera                          = Camera                    #
                data.Anointing                       = Anointing                 #
                self.Items.append( data)


    def flushItems(self):
        if not self.hasItems:
            self.clearContents()
            return
        Objective   : ObjectiveModel                #
        Offset      : ObjectiveOffsetModel          #
        Camera      : CameraModel                   #
        Anointing   : AnointingModel                #
        data        : FocusingContext
        rowCount    : int = self.rowCount   ()
        columnCount : int = self.columnCount()
        maxLength   : int = len(self.Items)
        self.setHeaderLabels()
        if rowCount > maxLength:
            for row in range(maxLength, rowCount):
                self.removeRow(row)
        for row in range(rowCount):
            data: FocusingContext = self.Items[row]
            for column in range(columnCount):
                self.flushItem(row, column, data)

        for row in range(rowCount, maxLength):
            data: FocusingContext = self.Items[row]
            Objective   = ObjectiveModel        .getConfig(data.Level)    if not data.hasObjective else data.Objective #
            Offset      = ObjectiveOffsetModel  .getConfig(data.Level)    if not data.hasOffset    else data.Offset    #
            Camera      = CameraModel           .getConfig(data.Level)    if not data.hasCamera    else data.Camera    #
            Anointing   = AnointingModel        .getConfig(data.Level)    if not data.hasAnointing else data.Anointing #
            self.insertRow(row)
            for column in range(columnCount):
                item: Qt.QTableWidgetItem = Qt.QTableWidgetItem()
                self.setItemProperty(column, item, data, Objective, Offset, Camera, Anointing)
                self.setItem(row, column, item)



    def flushItem(self, row: int, column: int, data: FocusingContext):
        item: Qt.QTableWidgetItem = self.item(row, column)
        if not isinstance(item, Qt.QTableWidgetItem):
            return
        self.setItemData(column, item, data)









    @property
    def hasMenus       (self) -> bool:
        return self.has_field('Menus'     , ActionMenuBar             )

    @property
    def hasItems       (self) -> bool:
        return self.has_field('Items'     , list                      )

    @property
    def hasContext     (self) -> bool:
        return self.has_field('Context'   , FocusingContext           )

    @property
    def hasObjective   (self) -> bool:
        return self.has_field('Objective' , ObjectiveModel            )

    @property
    def hasOffset      (self) -> bool:
        return self.has_field('Offset'    , ObjectiveOffsetModel      )

    @property
    def hasCamera      (self) -> bool:
        return self.has_field('Camera'    , CameraModel               )

    @property
    def hasAnointing   (self) -> bool:
        return self.has_field('Anointing' , AnointingModel            )
