from argparse import ArgumentTypeError
from DGSDK_constants import *
import ctypes
import struct
import time
import os


DLLPATH = "C:\\Users\\User\\Desktop\\DGSDK_Python\\x64\\DgRun64.dll"
DLL = None
FILEPATH = os.getcwd()
os.chdir(FILEPATH)
print (FILEPATH)
VERBOSE = False

# Hardware Info Structure Params
dwModel      = 0
szSerialNo   = bytearray(map(ord, ""))
iUsbSpeed    = 0
iPods        = 0
iChannels    = 0
iMemDepth    = 0
iMaxFreq     = 0
iMinFreq     = 0

# Pod Info Structure Params
# 0: POD unplugged, 1: POD plugged, 2: POD plugged but wrong type
iStatus      = [0, 0, 0, 0] 
i64PodHwVer  = [0, 0, 0, 0]
i64PodPcbVer = [0, 0, 0, 0]
# return POD name
szPodName    = [bytearray(map(ord, "")), bytearray(map(ord, "")), bytearray(map(ord, "")), bytearray(map(ord, ""))]

# Status of DG
pdwStatus    = 0

# Function from DLL
def _dg64Open() -> bool:
    return DLL.dg64Open()

def _dg64HwInfo(hwInfo):
    global DLL

    hwInfoStruct = struct.pack("I32siiiiii", *hwInfo)
    DLL.dg64HwInfo(hwInfoStruct)
    return struct.unpack("I32siiiiii", hwInfoStruct)

def _dg64Close():
    global DLL

    return DLL.dg64Close()

def _dg64RunDGW(filename):
    global DLL

    DLL.dg64RunDGW.argtypes = [ctypes.c_char_p]
    return DLL.dg64RunDGW(filename.encode('utf-8'))

def _dg64RunDGV(filename):
    global DLL

    DLL.dg64RunDGV.argtypes = [ctypes.c_char_p]
    return DLL.dg64RunDGV(filename.encode('utf-8'))

def _dg64StopRun():
    global DLL

    return DLL.dg64StopRun()

def _dg64Status(deviceStatus):
    global DLL

    # 创建ctypes的DWORD指针
    deviceStatusPtr = ctypes.c_uint(deviceStatus[0])
    
    # 设置函数参数类型和返回类型
    DLL.dg64Status.argtypes = [ctypes.POINTER(ctypes.c_uint)]
    DLL.dg64Status.restype = ctypes.c_bool
    
    # 调用C++ API
    result = DLL.dg64Status(ctypes.byref(deviceStatusPtr))
    
    # 更新deviceStatus列表中的值
    deviceStatus[0] = deviceStatusPtr.value
    
    return result

def _dg64LastError():
    pass

def _dg64PodSetup(podInfo):
    global DLL

    podInfoStruct = struct.pack("4i4q4q32s32s32s32s", *podInfo)
    DLL.dg64PodSetup(podInfoStruct)
    return struct.unpack("4i4q4q32s32s32s32s", podInfoStruct)

def _dg64KeyEvent():
    global DLL
    
    return DLL.dg64KeyEvent() 


#####################################
# Function exposed to main function #
#####################################

def dg64SetDLL(dllPath):
    global DLL, DLLPATH
    print (dllPath)
    DLLPATH = dllPath

    print (DLLPATH)
    DLL = ctypes.CDLL(DLLPATH, winmode=0x0)


def dg64GetHardware(verbose=False):
    global dwModel, szSerialNo, iUsbSpeed, iPods, iChannels, iMemDepth, iMaxFreq, iMinFreq
    global iStatus, i64PodHwVer, i64PodPcbVer, szPodName
    global VERBOSE

    VERBOSE = verbose

    rc = _dg64Open()
    print (rc)
    
    hwInfo = [dwModel, szSerialNo, iUsbSpeed, iPods, iChannels, iMemDepth, iMaxFreq, iMinFreq]
    dwModel, szSerialNo, iUsbSpeed, iPods, iChannels, iMemDepth, iMaxFreq, iMinFreq = _dg64HwInfo(hwInfo)
    
    if (VERBOSE): 
        print("=========================", flush=True)
        print("Hardware Info", flush=True)
        print(f"Model Name: {ModelInfo[dwModel]}", flush=True)
        # print(f"Serial Number: {szSerialNo}", flush=True)
        if iUsbSpeed:
            print("USB Speed: USB3.0", flush=True)
        else:
            print("USB Speed: USB2.0", flush=True)
        print(f"Available Pod Number: {iPods}", flush=True)
        print(f"Channels: {iChannels}", flush=True)
        print(f"Maximum Memory Depth: {iMemDepth}", flush=True)
        print(f"Maximum Freq: {iMaxFreq}", flush=True)
        print(f"Minimum Freq: {iMinFreq}", flush=True)
        print("==========================", flush=True)
    

    podInfo = [*iStatus, *i64PodHwVer, *i64PodPcbVer, *szPodName]
    podInfo = _dg64PodSetup(podInfo)
    iStatus, i64PodHwVer, i64PodPcbVer, szPodName = podInfo[:4], podInfo[4:8], podInfo[8:12], podInfo[12:] 

    if (VERBOSE): 
        print("Pod Info", flush=True)
        print(f"iStatus: {iStatus}", flush=True)
        print(f"i64PodHwVer: {i64PodHwVer}", flush=True)
        print(f"i64PodPcbVer: {i64PodPcbVer}", flush=True)
        # print(f"szPodName: {szPodName}", flush=True)
        print("==========================", flush=True)


def dg64LoadFile(filename, verbose=False):
    global FILEPATH
    global VERBOSE
    
    VERBOSE = verbose

    FILEPATH = filename

    if not FILEPATH.strip().lower().endswith((".dgw", ".dgv", ".tdw")):
        raise ArgumentTypeError(f"{FILEPATH} is not valid file format.")

    if not os.path.exists(FILEPATH):
        raise FileNotFoundError(f"File {FILEPATH} not found.")



def dg64Start(verbose=False):
    _, sFileExt = os.path.splitext(FILEPATH)
    # print(sFileExt)
    if sFileExt == '.dgw' or sFileExt == '.tdw':
        iErrCode = _dg64RunDGW(FILEPATH)
    else: # .dgv
        iErrCode = _dg64RunDGV(FILEPATH)
    
    print ("Error: ", iErrCode)

    if iErrCode == ERR_DERR_DG_FILE_NOT_FOUND:
        raise FileNotFoundError(f"File {FILEPATH} not found.")
    elif iErrCode == ERR_DG_FILE_OPEN:
        raise IOError(f"File {FILEPATH} cannot open.")
    elif iErrCode == ERR_DG_FILE_TYPE:
        raise IOError(f"Unknown file type.")
    elif iErrCode == ERR_DG_FILE_FORMAT:
        raise IOError(f"File format is invalid.")
    elif iErrCode == ERR_DG_FILE_VERSION:
        raise IOError(f"File format is out of date.")
    elif iErrCode == ERR_DG_MEMORY_NOT_ENOUGH:
        raise IOError(f"System memory not enough.")
    elif iErrCode == ERR_DG_SIGNAL_INPUT_NAME:
        raise IOError(f"Signal name duplicated.")
    elif iErrCode == ERR_DG_CHANNEL_NUMBER:
        raise IOError(f"Too many channels.")
    elif iErrCode == ERR_DG_TIME_STAMP:
        raise IOError(f"Timestamp error.")
    elif iErrCode == ERR_DG_FREQ_COLLISION:
        raise IOError(f"Frequency Collision.")
    elif iErrCode == ERR_DG_NO_INPUT_NAME:
        raise IOError(f"No input channel name.")
    elif iErrCode == ERR_DG_DATA_LENGTH:
        raise IOError(f"Device memory not enough.")
    elif iErrCode == ERR_DG_NO_TIME_MARK:
        raise IOError(f"No time mark.")
    elif iErrCode == ERR_DG_NO_CHANNEL_NUMBER:
        raise IOError(f"No input channel number.")
    elif iErrCode == ERR_DG_MEMORY:
        raise IOError(f"Insufficient memory buffer.")
    elif iErrCode == ERR_DG_FILE_READ:
        raise IOError(f"Read File error.")
    elif iErrCode == ERR_DG_NO_HARDWARE:
        raise IOError(f"No hardware found.")
    elif iErrCode == ERR_DG_FREQ_NOT_MATCH:
        raise IOError(f"Warning: Use close frequency for system clock") 
    elif iErrCode == WARN_DG_DEV_MEMORY_LIMIT:
        raise IOError(f"File is turncated by device memory limitation.")
    elif iErrCode == WARN_DG_PC_MEMORY_LIMIT:
        raise IOError(f"File is turncated by PC memory limitation.")
    elif iErrCode == 0:

        # Normal
        # Send Key Event
        dg64sendKeyEvent()
        # Set Timer
    else:
        raise Exception(f"Critical Error.")

def dg64Stop():
    return _dg64StopRun()

def dg64Shutdown():
    return _dg64Close()


def dg64sendKeyEvent():
    global pdwStatus
    deviceStatus = [pdwStatus]
    
    for _ in range(10):
        result = _dg64Status(deviceStatus)
        pdwStatus = deviceStatus[0]  # 更新全局变量
        # print(pdwStatus, flush=True)
        if (pdwStatus & DG_WAITING_EXT_EVENT):
            return _dg64KeyEvent()
        time.sleep(0.2)
    
    result = _dg64Status(deviceStatus)
    pdwStatus = deviceStatus[0]  # 更新全局变量
    if (pdwStatus & DG_WAITING_EXT_EVENT):
        return _dg64KeyEvent()
    else:
        return False