import sys
sys.path.append('../../')

import struct
from enum import Enum

from utils.DBModels.DeviceParModels import *
from protocol.ModBus.DPV1Master import DPV1Master
from model.ProjectModel.ParmManage import Parm

import concurrent.futures

import threading

from PyQt5.QtCore import pyqtSignal, pyqtSlot, QThread

class LoadDataThread(QThread):
    loadDataSignal = pyqtSignal(int, int)
    finished = pyqtSignal(list)
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        
 
    def run(self):
        valueList = []
        for index, parm in enumerate(self.parent.parms.values()):
            self.loadDataSignal.emit(len(self.parent.parms), index + 1)
            valueList.append(parm.readValue())
        self.finished.emit(valueList)

class BlockType(Enum):
    TB = 1
    PB = 0
    FB = 2

class TBType(Enum):
    flowTB = -1
    tempTB = -2
    levelTB = -3 # 物位转换块
    pressureTB = -4

class BlockManage():
    _instance = None
    _dpv1Master = None

    def __new__(cls, address):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, address = None):
        self.address = address
        if address:
            self.initBlocks()

    @property
    def DPV1Master(self):
        if not self._dpv1Master:
            self._dpv1Master = DPV1Master('192.168.3.10', 502)
        return self._dpv1Master
    
    def initBlocks(self):
        if not self.DPV1Master.judgeSlave(self.address):
            raise RuntimeError(f"连接从站{self.address}失败.")
        self.blockDict = {
        BlockType.PB : [],
        BlockType.TB : [],
        BlockType.FB : [],
        }
        dirHeadDatas = self.DPV1Master.readParm(address = self.address, slot = 1, index = 0, length = 12)
        dirHeadDatas = struct.unpack('>6h', dirHeadDatas)
        DirID = dirHeadDatas[0]       # 目录id
        DirRevNum = dirHeadDatas[1]   # 目录版本号
        NumDirObj = dirHeadDatas[2]   # 目录对象的个数  (如果整个目录使用多于一个目录对象， 
                                      # 则这此元素被连续定义，就如同使用一个较大的对象。
                                      # 多个目录对象都被连续地列在该目录中。该对象计数整个目录所需的对象。Header对象不计人其)
        NumDirEntry = dirHeadDatas[3] # 目录登录项的总数 (应计算复合列表目录登录项和复合目录登录项的总个数；实际块数量为NumDirEntry - 3)
        FirstCompListDirEntry = dirHeadDatas[4] # 第1个复合列表目录登录项的目录登录项个数 (该数用来计数目录内的登录项， 而不包含该登
                                                # 录项的参数地址。第1 个目录登录项是在复合列表目录登录项中的物理块引用。在计数登录
                                                # 项时，复合列表目录登录项与复合目录登录项之间无间隔)
        NumCompListDirEntry = dirHeadDatas[5] # 功能块类型数量 复合列表目录登录项的个数 计数设备内的不同块类型(物理块、转换块和功能块)和对象类型
                                              #（在本标准范围内仅针对链接对象）

        dirLength = 4 * NumDirEntry
        dirDatas = self.DPV1Master.readParm(address = self.address, slot = 1, index = 1, length = dirLength)
        dirDatas = struct.unpack('>{}h'.format(int(dirLength/2)), dirDatas)
        entryTuples = [(dirDatas[i], dirDatas[i+1]) for i in range(0, len(dirDatas), 2)]

        for typ in [BlockType.PB, BlockType.TB, BlockType.FB]:
            typeIndex = typ.value if type(typ.value) == int else 1
            blkDirMesByte = struct.pack('>h', entryTuples[typeIndex][0])
            blkIndex = int(blkDirMesByte[0])  # 目录对象编号
            blkoffect = int(blkDirMesByte[1]) # 块从第几个Dir_Entry开始
            numBlk = entryTuples[typeIndex][1] # 块数量
            # print(blkIndex, blkoffect, numBlk)
            for i in range(numBlk):
                blkEntryListIndex = blkoffect - 1 + i
                # print(blkEntryListIndex, entryTuples)
                blkPointerByte = struct.pack('>h', entryTuples[blkEntryListIndex][0])
                numBlkParms = entryTuples[blkEntryListIndex][1]
                blkSlot = int(blkPointerByte[0])
                blkStartIndex = int(blkPointerByte[1])
                # print(111, blkStartIndex, )
                block = Block(typ, self.DPV1Master)
                block.slot = blkSlot
                block.startIndex = blkStartIndex
                block.numParms = numBlkParms
                block.blockIndex = i
                block.address = self.address
                block.addParms()
                if not block.getTBType():
                    self.blockDict[typ].append(block)
                # print(blkSlot, blkIndex)
        # print(self.blockDict)

    def getBlockValues(self, blockType, blockIndex, callback, callback2):
        block = self.resetBlockType(blockType, blockIndex)
        return block.getParmsValue(callback, callback2)

    def getBlockParmValue(self, blockType, blockIndex, parmIndex):
        block = self.resetBlockType(blockType, blockIndex)
        parm = block.parms[int(parmIndex)]
        return parm.readValue()

    def writeParmValue(self, blockType, blockIndex, parmIndex, values):
        block = self.resetBlockType(blockType, blockIndex)
        parm = block.parms[int(parmIndex)]
        result = parm.writeParm(values)
        return result

    def resetBlockType(self, blockType, blockIndex):
        if not isinstance(blockType, BlockType):
            typ = BlockType.TB
            block = self.blockDict[typ][blockIndex]
            block.blockType = blockType
            block.addParms()
        else:
            block = self.blockDict[blockType][blockIndex]
        return block

    def getBlockNums(self):
        return [len(self.blockDict[BlockType.PB]), len(self.blockDict[BlockType.TB]), len(self.blockDict[BlockType.FB])]

    @classmethod
    def searchSlave(self, callback):
        self.DPV1Master.searchSlave(callback)


class Block():
    slot = None
    startIndex = None
    numParms = None
    blockIndex = None
    address = None
    loadDataSignal = pyqtSignal(int, int)
    def __init__(self, blockType, DPV1Master):
        self.parms = {}
        self.blockType = blockType
        self.DPV1Master = DPV1Master
        # self.addParms()
    
    def addParms(self):
        self.parms = {}
        getParmsFunc = {
            BlockType.PB: PhysicalBlock.getallParame,
            BlockType.TB: PressureTranslationBlock.getallParame,
            BlockType.FB: AIFunctionBlock.getallParame,
            TBType.flowTB: FlowTranslationBlock.getallParame,
            TBType.pressureTB: PressureTranslationBlock.getallParame,
            TBType.tempTB: TemperatureTranslationBlock.getallParame,
            TBType.levelTB: LevelTranslationBlock.getallParame,
        }.get(self.blockType, lambda: [])
        
        parmsData = getParmsFunc()
        for parmData in parmsData:
            # print(parmData)
            # print(self.startIndex, 3214)
            parm = Parm(parmData, self.slot, self.startIndex, self)
            self.parms[parm.relativeIndex] = parm
            # self.parms.append(parm)

    def getParmsValue(self, callback, callback2):
        valueList = []
        # self.loadDataSignal.connect(callback.loadData)
        self.thread = LoadDataThread(self)
        self.thread.loadDataSignal.connect(callback)
        self.thread.start()
        self.thread.finished.connect(callback2)
        # thread.join()
        # print(valueList)
        # return valueList

    def getParmValue(self, index, callback, callback2):
        return

    def getTBType(self):
        return


if __name__ == '__main__':
    b = BlockManage(address = 55)
    c = BlockManage(address = 77)
    # b.initBlock()