import os
from ctypes import *
import struct
import platform
from enum import Enum, auto
from functools import wraps


def pause():
    input("Press Enter to continue ...")

def get_lib_path():
    lib_base = os.path.join(os.path.dirname(
        os.path.abspath(__file__)), '../../SDK')

    if platform.system() == 'Windows':
        if struct.calcsize("P") == 4:
            lib_name = "windows/SECG40x86.dll"
        else:
            lib_name = "windows/SECG40x64.dll"
    elif platform.system() == 'Linux':
        if platform.machine() == 'aarch64':
            lib_name = "linux/libsecgrpix64.so"
        elif platform.machine() == 'armv7l':
            lib_name = "linux/libsecgrpix86.so"
        elif platform.machine() == 'x86_64':
            lib_name = "linux/libsecgx64.so"
        else:
            lib_name = "linux/libsecgx86.so"
    elif platform.system() == 'Darwin':
        lib_name = "macos/libsecg.a"
    return os.path.join(lib_base, lib_name)

ConnectedCallback = CFUNCTYPE(None, c_bool)
TimeAmpCallback = CFUNCTYPE(None, c_double, c_double)


class OutputLead_E (Enum):
    Lead_RA = 0
    Lead_LA = 1
    Lead_LL = 2
    Lead_V1 = 3
    Lead_V2 = 4
    Lead_V3 = 5
    Lead_V4 = 6
    Lead_V5 = 6
    Lead_V6 = 7


class OutputFunction_E (Enum):
    Output_Off = 0
    Output_Sine = 1
    Output_Triangle = 2
    Output_Square = 3
    Output_RectanglePulse = 4
    Output_TrianglePulse = 5
    Output_Exponential = 6
    Output_ECG2_27 = 7
    Output_IEC227W = 8
    Output_IEC251W = 9
    Output_JJG1041 = 10
    Output_JJG1041_HR = 11
    Output_JJG_Hysteresis = 12
    Output_ECG_File = 13
    Output_InputDynamicRange = 14
    Output_ANE20000 = 15



class BaselineTest_E (Enum):
    Baseline_Off = 0
    Baseline_50Hz = 1
    Baseline_60Hz = 2
    Baseline_80Hz = 3
    Baseline_100Hz = 4


class ECG2_27_NoiseFreq_E (Enum):
    Noise_None = 0
    Noise_50Hz = 1
    Noise_60Hz = 2


def sdk_fn(cfn_name, args_type=None, return_type=None):
    def decorator_function(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            cfn = getattr(self.sdk, cfn_name)
            if args_type:
                cfn.argtypes = args_type
            if return_type:
                cfn.restype = return_type
            return cfn(*args, **kwargs)

        return wrapper

    return decorator_function


class SECG40:
    def __init__(self, lib_file_path):        
        self.sdk = cdll.LoadLibrary(lib_file_path)       

    @sdk_fn("Init", [ConnectedCallback], c_bool)
    def init(self, cb) -> c_bool:
        pass

    @sdk_fn("Connect", [c_uint, c_uint], c_bool)
    def connect(self, port_number, milliseconds_timeout) -> c_bool:
        pass

    @sdk_fn("ResetSECG", [], None)
    def reset(self) -> None:
        pass

    @sdk_fn("ResetSECG_AllLeadOff", [], None)
    def read_all_lead_off(self) -> None:
        pass

    @sdk_fn("Close", [], None)
    def close(self) -> None:
        pass

    #
    # Device Configurations
    #
    def get_serial_number(self):
        self.sdk.GetSerialNumber.restype = c_char_p
        return self.sdk.GetSerialNumber().decode('ascii')

    def get_fw_version(self):
        self.sdk.GetFWVersion.restype = c_char_p
        return self.sdk.GetFWVersion().decode('ascii')

    def get_hw_version(self):
        self.sdk.GetHWVersion.restype = c_char_p
        return self.sdk.GetHWVersion().decode('ascii')

    def get_sdk_version(self):
        self.sdk.GetSDKVersion.restype = c_char_p
        return self.sdk.GetSDKVersion().decode('ascii')

    @sdk_fn("RegisterTimeAmpCB", [TimeAmpCallback], None)
    def register_time_amplitude_callback(self, cb) -> None:
        pass

    @sdk_fn("SetOutputFunc", [c_int], c_int)
    def set_output_function(self, func) -> c_int:
        pass

    @sdk_fn("GetOutputFunc", [], c_int)
    def get_output_function(self) -> c_int:
        pass

    @sdk_fn("SetAmplitude", [c_double], c_int)
    def set_amplitude(self, amplitude) -> c_int:
        pass

    @sdk_fn("GetAmplitude", [], c_double)
    def get_amplitude(self, amplitude) -> c_double:
        pass

    @sdk_fn("SetFrequency", [c_double], c_int)
    def set_frequency(self, frequency) -> c_int:
        pass

    @sdk_fn("GetFrequency", [], c_double)
    def get_frequency(self, amplitude) -> c_double:
        pass

    @sdk_fn("SetDCOffset", [c_int], c_int)
    def set_dc_offset(self, dc_offset) -> c_int:
        pass

    @sdk_fn("GetDCOffset", [], c_int)
    def get_dc_offset(self) -> c_int:
        pass

    @sdk_fn("SetDCOffsetVariableMode", [c_bool], c_int)
    def set_dc_offset_variable_mode(self, on_off) -> c_int:
        pass

    @sdk_fn("GetDCOffsetVariableMode", [], c_bool)
    def get_dc_offset_variable_mode(self) -> c_bool:
        pass

    @sdk_fn("SetDCOffsetCommonMode", [c_bool], c_int)
    def set_dc_offset_common_mode(self, on_off) -> c_int:
        pass

    @sdk_fn("SetDCOffsetCommoneMode", [c_bool], c_int)
    def set_dc_offset_common_mode(self, on_off) -> c_int:
        pass

    @sdk_fn("GetDCOffsetCommonMode", [], c_bool)
    def get_dc_offset_common_mode(self) -> c_bool:
        pass

    @sdk_fn("SetOutputLead", [c_int, c_bool], c_int)
    def set_output_lead(self, lead, on_off) -> c_int:
        pass

    @sdk_fn("GetOutputLead", [c_int], c_bool)
    def get_output_lead(self, lead) -> c_bool:
        pass

    @sdk_fn("SetPulseWidth", [c_double], c_int)
    def set_pulse_width(self, pulse_width) -> c_int:
        pass

    @sdk_fn("GetPulseWidth", [], c_double)
    def get_pulse_width(self) -> c_double:
        pass

    @sdk_fn("SetInputImpedanceMode", [c_bool], c_int)
    def set_input_impedance_mode(self, on_off) -> c_int:
        pass

    @sdk_fn("GetInputImpedanceMode", [], c_bool)
    def get_input_impedance_mode(self) -> c_bool:
        pass

    @sdk_fn("SetPacingAmplitude", [c_double], c_int)
    def set_pacing_amplitude(self, amplitude) -> c_int:
        pass

    @sdk_fn("GetPacingAmplitude", [], c_double)
    def get_pacing_amplitude(self, amplitude) -> c_double:
        pass

    @sdk_fn("SetBaselineResetTest", [c_int], c_int)
    def set_baseline_reset_test(self, mode) -> c_int:
        pass

    @sdk_fn("SetECG2_27_Noise", [c_int, c_double], c_int)
    def set_ecg_2_27_noise(self, frequency, amplitude) -> c_int:
        pass

    @sdk_fn("LoadECGtxt", [c_wchar_p], c_int)
    def load_ecg_txt(self, file_path) -> c_int:
        pass

    @sdk_fn("LoadECGbin", [c_wchar_p], c_int)
    def load_ecg_bin(self, file_path) -> c_int:
        pass
