import ctypes
import threading
from ctypes import CFUNCTYPE, c_int64, c_int, c_void_p, c_char_p
from ctypes.wintypes import POINT


def decodeString(cString):
    pString = cString.decode("utf-8")
    return pString

# Initialize thread support (Must be called in the main thread)
def initialize_python_threads():
    pythonapi = ctypes.pythonapi
    
    # Ensure threads are initialized
    PyEval_InitThreads = pythonapi.PyEval_InitThreads
    PyEval_InitThreads()
    
    # Save GIL state function
    global PyGILState_Ensure, PyGILState_Release
    PyGILState_Ensure = pythonapi.PyGILState_Ensure
    PyGILState_Ensure.restype = ctypes.c_void_p
    
    PyGILState_Release = pythonapi.PyGILState_Release
    PyGILState_Release.argtypes = [ctypes.c_void_p]

# Create a thread-safe callback decorator.
def thread_safe_callback(callback_type):
    def decorator(func):
        def wrapper(*args):
            gstate = PyGILState_Ensure()
            try:
                return func(*args)
            finally:
                PyGILState_Release(gstate)
        return callback_type(wrapper)
    return decorator

lib = ctypes.cdll.LoadLibrary("./dubbing_sdk/dubbing_sdk_cpp.dll")

lib.createDubbingEngineByPool.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_bool, ctypes.c_bool, ctypes.c_bool, ctypes.c_bool]
lib.createDubbingEngineByPool.restype = ctypes.c_int64
def createDubbingEngineByPool(resourcesPath, token, samplerate, channel, format, enableLog, enableTransformLog, muteOnFail, isSync,  downloadCallback, completeCallback, errorCallback, actionResultCallback):
    result = lib.createDubbingEngineByPool(resourcesPath, token, samplerate, channel, format, enableLog, enableTransformLog, muteOnFail, isSync, downloadCallback, completeCallback, errorCallback, actionResultCallback)
    return result

lib.destroyDubbingEngineByPool.argtypes = [ctypes.c_int64]
def destroyDubbingEngineByPool(dubbingEngineId):
    result = lib.destroyDubbingEngineByPool(dubbingEngineId)
    return result

lib.checkResources.argtypes = [ctypes.c_int64]
lib.checkResources.outpop = ctypes.c_int
def checkResources(dubbingEngineId):
    result = lib.checkResources(dubbingEngineId)
    return result

lib.prepare.argtypes = [ctypes.c_int64]
lib.prepare.outpop = ctypes.c_int
def prepare(dubbingEngineId):
    result = lib.prepare(dubbingEngineId)
    return result

lib.setVoice.argtypes = [ctypes.c_int64, ctypes.c_int]
lib.setVoice.outpop = ctypes.c_int
def setVoice(dubbingEngineId, voiceId):
    result = lib.setVoice(dubbingEngineId, voiceId)
    return result

lib.transform.argtypes = [ctypes.c_int64, ctypes.POINTER(ctypes.c_byte), ctypes.c_int]
lib.transform.restype = ctypes.c_int
def transform(dubbingEngineId, data_in, nlen):
    result = lib.transform(dubbingEngineId, data_in, nlen)
    return result

lib.getVoiceList.argtypes = [ctypes.c_int64]
lib.getVoiceList.restype = ctypes.c_char_p
def getVoiceList(dubbingEngineId):
    result = lib.getVoiceList(dubbingEngineId)
    result = decodeString(result)
    return result

lib.getCurrentVoice.argtypes = [ctypes.c_int64]
lib.getCurrentVoice.restype = ctypes.c_int
def getCurrentVoice(dubbingEngineId):
    result = lib.getCurrentVoice(dubbingEngineId)
    return result

lib.setMode.argtypes = [ctypes.c_int64, ctypes.c_int, ctypes.c_float, ctypes.c_float]
def setMode(dubbingEngineId, mode, p1, p2):
    lib.setMode(dubbingEngineId, mode, p1, p2)

lib.getMode.argtypes = [ctypes.c_int64]
lib.getMode.restype = ctypes.c_int
def getMode(dubbingEngineId):
    result = lib.getMode(dubbingEngineId)
    return result

lib.getIntonation.argtypes = [ctypes.c_int64]
lib.getIntonation.restype = ctypes.c_float
def getIntonation(dubbingEngineId):
    result = lib.getIntonation(dubbingEngineId)
    return result

lib.getPitch.argtypes = [ctypes.c_int64]
lib.getPitch.restype = ctypes.c_float
def getPitch(dubbingEngineId):
    result = lib.getPitch(dubbingEngineId)
    return result

lib.proCalibration.argtypes = [ctypes.c_int64]
lib.proCalibration.restype = ctypes.c_bool
def proCalibration(dubbingEngineId):
    result = lib.proCalibration(dubbingEngineId)
    return result

lib.getCalibrationFile.argtypes = [ctypes.c_int64]
lib.getCalibrationFile.restype = ctypes.c_char_p
def getCalibrationFile(dubbingEngineId):
    result = lib.getCalibrationFile(dubbingEngineId)
    return result

lib.getSupportIntonationMin.argtypes = [ctypes.c_int64]
lib.getSupportIntonationMin.restype = ctypes.c_float
def getSupportIntonationMin(dubbingEngineId):
    result = lib.getSupportIntonationMin(dubbingEngineId)
    return result

lib.getSupportIntonationMax.argtypes = [ctypes.c_int64]
lib.getSupportIntonationMax.restype = ctypes.c_float
def getSupportIntonationMax(dubbingEngineId):
    result = lib.getSupportIntonationMax(dubbingEngineId)
    return result

lib.getSupportPitchMin.argtypes = [ctypes.c_int64]
lib.getSupportPitchMin.restype = ctypes.c_float
def getSupportPitchMin(dubbingEngineId):
    result = lib.getSupportPitchMin(dubbingEngineId)
    return result

lib.getSupportPitchMax.argtypes = [ctypes.c_int64]
lib.getSupportPitchMax.restype = ctypes.c_float
def getSupportPitchMax(dubbingEngineId):
    result = lib.getSupportPitchMax(dubbingEngineId)
    return result

lib.getDelayMillis.argtypes = [ctypes.c_int64]
lib.getDelayMillis.restype = ctypes.c_int
def getDelayMillis(dubbingEngineId):
    result = lib.getDelayMillis(dubbingEngineId)
    return result

lib.start.argtypes = [ctypes.c_int64]
def start(dubbingEngineId):
    lib.start(dubbingEngineId)

lib.stop.argtypes = [ctypes.c_int64]
def stop(dubbingEngineId):
    lib.stop(dubbingEngineId)

lib.engineRelease.argtypes = [ctypes.c_int64]
def engineRelease(dubbingEngineId):
    lib.engineRelease(dubbingEngineId)

lib.getEngineStatus.argtypes = [ctypes.c_int64]
lib.getEngineStatus.restype = ctypes.c_int
def getEngineStatus(dubbingEngineId):
    result = lib.getEngineStatus(dubbingEngineId)
    return result


