from ctypes import *
import os


class tm(Structure):
    _fields_ = [
        ("tm_sec", c_int),
        ("tm_min", c_int),
        ("tm_hour", c_int),
        ("tm_mday", c_int),
        ("tm_mon", c_int),
        ("tm_year", c_int),
        ("tm_wday", c_int),
        ("tm_yday", c_int),
        ("tm_isdst", c_int),
            ]

class API:
    api = None

    def __init__(self):
        # Load API module
        try:
            current_file_path = os.path.abspath(__file__)

            # load Genji API
            self.api = cdll.LoadLibrary("/usr/local/lib/libgenji.dylib")

        except Exception as e:
            print(e)
            self.api = None
            return

        # define argument types for Genji API functions
        self.api.Dongle_Count.argtypes = [c_short]
        self.api.Dongle_Count.restype = c_short
        self.api.Dongle_MemSize.argtypes = [c_short, c_short]
        self.api.Dongle_MemSize.restype = c_short
        self.api.Dongle_Model.argtypes = [c_short, c_short]
        self.api.Dongle_Model.restype = c_short
        self.api.Dongle_Version.argtypes = [c_short, c_short]
        self.api.Dongle_Version.restype = c_int
        self.api.Dongle_ReadData.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short]
        self.api.Dongle_ReadData.restype = c_short
        self.api.Dongle_ReadDataEx.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short, c_short]
        self.api.Dongle_ReadDataEx.restype = c_short
        self.api.Dongle_WriteData.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short]
        self.api.Dongle_WriteData.restype = c_short
        self.api.Dongle_WriteDataEx.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short, c_short]
        self.api.Dongle_WriteDataEx.restype = c_short
        self.api.Dongle_ReadSerNr.argtypes = [c_int, c_short, c_short]
        self.api.Dongle_ReadSerNr.restype = c_int
        self.api.Dongle_WriteKey.argtypes = [c_int, POINTER(c_int), c_short, c_short]
        self.api.Dongle_WriteKey.restype = c_short
        self.api.Dongle_GetKeyFlag.argtypes = [c_int, c_short, c_short]
        self.api.Dongle_GetKeyFlag.restype = c_short
        self.api.Dongle_EncryptData.argtypes = [c_int, POINTER(c_int), c_short, c_short]
        self.api.Dongle_EncryptData.restype = c_short
        self.api.Dongle_DecryptData.argtypes = [c_int, POINTER(c_int), c_short, c_short]
        self.api.Dongle_DecryptData.restype = c_short
        self.api.Dongle_SetDriverFlag.argtypes = [c_int, c_short, c_short, c_short]
        self.api.Dongle_SetDriverFlag.restype = c_short
        self.api.Dongle_GetDriverFlag.argtypes = [c_int, c_short, c_short]
        self.api.Dongle_GetDriverFlag.restype = c_short

        self.api.Dongle_ReadGUSN.argtypes = [c_int, c_char_p, c_short, c_short]
        self.api.Dongle_ReadGUSN.restype = c_short
        self.api.Dongle_GetRand.argtypes = [c_int, c_short, c_char_p, c_short, c_short]
        self.api.Dongle_GetRand.restype = c_short
        self.api.Dongle_SetLED.argtypes = [c_short,c_short,c_short]
        self.api.Dongle_SetLED.restype = c_short
        self.api.Dongle_GetTime.argtypes = [c_int, POINTER(tm), c_short, c_short]
        self.api.Dongle_GetTime.restype = c_short

        self.api.Dongle_CreateRSAKeyPair.argtypes = [c_int, c_short, c_short, c_short]
        self.api.Dongle_CreateRSAKeyPair.restype = c_short
        self.api.Dongle_WriteRSAKey.argtypes = [c_int, c_short, c_int, c_short, c_char_p, c_short, c_short]
        self.api.Dongle_WriteRSAKey.restype = c_short
        self.api.Dongle_LockRSAKeyPair.argtypes = [c_int, c_short, c_int, c_int, c_short, c_short ];
        self.api.Dongle_LockRSAKeyPair.restype = c_short
        self.api.Dongle_GetRSAPubKey.argtypes = [ c_int, c_short, c_char_p, POINTER(c_short), c_short, c_short]
        self.api.Dongle_GetRSAPubKey.restype = c_short
        self.api.EncryptByCOSRsaPubKey.argtypes = (c_char_p, c_short, c_char_p, c_short, c_char_p, POINTER(c_short));
        self.api.EncryptByCOSRsaPubKey.restype = c_short
        self.api.Dongle_EncryptDataRSA.argtypes = [c_int, c_short, c_char_p, c_short, c_char_p, POINTER(c_short), c_short, c_short]
        self.api.Dongle_EncryptDataRSA.restype = c_short
        self.api.Dongle_DecryptDataRSA.argtypes = [c_int, c_short,c_char_p, c_short, c_char_p, POINTER(c_short), c_short,c_short]
        self.api.Dongle_DecryptDataRSA.restype = c_short
        self.api.Dongle_SignDataRSA.argtypes = [c_int, c_short,c_char_p,c_short, c_char_p, POINTER(c_short), c_short,c_short]
        self.api.Dongle_SignDataRSA.restype = c_short
        self.api.Dongle_VerifySigRSA.argtypes = [c_int, c_short, c_char_p, c_short, c_char_p, c_short, c_short, c_short]
        self.api.Dongle_VerifySigRSA.restype = c_short

        self.api.Dongle_MemSize2.argtypes = [c_short,c_short]
        self.api.Dongle_MemSize2.restype = c_short
        self.api.Dongle_WriteData2.argtypes = [c_int, c_char_p, c_short, c_short, c_short, c_short ]
        self.api.Dongle_WriteData2.restype = c_short
        self.api.Dongle_ReadData2.argtypes = [c_int, c_char_p, c_short, c_short, c_short, c_short]
        self.api.Dongle_ReadData2.restype = c_short
        self.api.Dongle_LockData2.argtypes = [c_int, c_int, c_int, c_short, c_short ]
        self.api.Dongle_LockData2.restype = c_short
        self.api.Dongle_LockData.argtypes = [c_int, c_int, c_int, c_short, c_short ]
        self.api.Dongle_LockData.restype = c_short

        self.api.Dongle_SetTimer.argtypes = [c_int, c_short, c_short, c_short]
        self.api.Dongle_SetTimer.restype = c_short
        self.api.Dongle_StartTimer.argtypes = [c_int,c_short, c_short]
        self.api.Dongle_StartTimer.restype = c_short
        self.api.Dongle_GetTimer.argtypes = [c_int, POINTER(c_int),  c_short, c_short]
        self.api.Dongle_GetTimer.restype = c_short
        self.api.Dongle_StopTimer.argtypes = [c_int, c_short, c_short]
        self.api.Dongle_StopTimer.restype = c_short

        self.api.RegisterGenjiCallbackPort.argtypes = [c_int]

        # Initialize API
        self.api.Init_MatrixAPI()

    def __del__(self):
        if self.api is None:
            return -1

        return self.api.Release_MatrixAPI()

    def count(self):
        if self.api is None:
            return -1

        return self.api.Dongle_Count(85)

    def mem_size(self, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_MemSize(dng_nr, 85)

    def model(self, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_Model(dng_nr, 85)

    def version(self, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_Version(dng_nr, 85)

    def api_version(self):
        if self.api is None:
            return -1
        return self.api.GetVersionAPI()

    def drv_version(self):
        if self.api is None:
            return -1
        return self.api.GetVersionDRV()

    def ser_no(self, user_code, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_ReadSerNr(user_code, dng_nr, 85)

    def read_data(self, user_code, count, dng_nr):
        if self.api is None:
            return -1, []

        fields = [0] * count
        data = (c_int * count)(*fields)

        ret = self.api.Dongle_ReadData(user_code, data, count, dng_nr, 85)
        if ret == count:
            return ret, list(data)

        return ret, []

    def read_data_ex(self, user_code, pos, count, dng_nr):
        if self.api is None:
            return -1, []

        fields = [0] * count
        data = (c_int * count)(*fields)

        ret = self.api.Dongle_ReadDataEx(user_code, data, pos, count, dng_nr, 85)
        if ret == count:
            return ret, list(data)

        return ret, []

    def write_data(self, user_code, fields, dng_nr):
        if self.api is None:
            return -1

        if not isinstance(fields, list):
            return -9

        data = (c_int * len(fields))(*fields)
        return self.api.Dongle_WriteData(user_code, data, len(fields), dng_nr, 85)

    def write_data_ex(self, user_code, fields, pos, dng_nr):
        if self.api is None:
            return -1

        if not isinstance(fields, list):
            return -9

        data = (c_int * len(fields))(*fields)
        return self.api.Dongle_WriteDataEx(user_code, data, pos, len(fields), dng_nr, 85)

    def write_key(self, user_code, keys, dng_nr):
        if self.api is None:
            return -1

        if not isinstance(keys, list) or len(keys) != 4:
            return -9

        enc_key = (c_int * len(keys))(*keys)
        return self.api.Dongle_WriteKey(user_code, enc_key, dng_nr, 85)

    def get_key_flag(self, user_code, dng_nr):
        return self.api.Dongle_GetKeyFlag(user_code, dng_nr, 85)

    def encrypt_data(self, user_code, data, dng_nr):
        if self.api is None:
            return -1, []

        if not isinstance(data, list) or len(data) != 2:
            return -1, []

        plain = (c_int * len(data))(*data)
        ret = self.api.Dongle_EncryptData(user_code, plain, dng_nr, 85)
        if ret > 0:
            return ret, list(plain)

        return ret, []

    def decrypt_data(self, user_code, data, dng_nr):
        if self.api is None:
            return -1, []

        if not isinstance(data, list) or len(data) != 2:
            return -1, []

        enc_data = (c_int * len(data))(*data)
        ret = self.api.Dongle_DecryptData(user_code, enc_data, dng_nr, 85)
        if ret > 0:
            return ret, list(enc_data)

        return ret, []

    def set_driver_flag(self, user_code, flag, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_SetDriverFlag(user_code, flag, dng_nr, 85)

    def get_driver_flag(self, user_code, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_GetDriverFlag(user_code, dng_nr, 85)


    def read_gusn(self, user_code, gusn, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_ReadGUSN(user_code, gusn, dng_nr, 85)
       
    def get_rand(self, user_code, siz, rand, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_GetRand(user_code, siz, rand, dng_nr, 85)

    def get_time(self, user_code, dng_nr):
        if self.api is None:
            return -1
        cur_time = tm(0,0,0,0,0,0,0,0,0)
        ret = self.api.Dongle_GetTime(user_code, byref(cur_time), dng_nr, 85)
        return ret, cur_time

    def set_led(self, mode, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_SetLED( mode, dng_nr, 85)

    def mem_size2( self, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_MemSize2( dng_nr, 85)

    def read_data2( self, user_code, data, pos, len, dng_nr):
        if self.api is None:
            return -1   
        return self.api.Dongle_ReadData2(user_code, data, pos, len, dng_nr, 85)

    def write_data2( self, user_code, data, pos, len, dng_nr):
        if self.api is None:
            return -1   
        return self.api.Dongle_WriteData2(user_code, data, pos, len, dng_nr, 85)

    def lock_data( self, user_code, lock_flag, lock_key, dng_nr):
        if self.api is None:
            return -1   
        return self.api.Dongle_LockData( user_code, lock_flag, lock_key, dng_nr, 85)

    def lock_data2( self, user_code, lock_flag, lock_key, dng_nr):
        if self.api is None:
            return -1   
        return self.api.Dongle_LockData2( user_code, lock_flag, lock_key, dng_nr, 85)

    def create_rsa_key(self,user_code, idx, dng_nr):
        if self.api is None:
            return -1
        return self.api.Dongle_CreateRSAKeyPair(user_code, idx, dng_nr, 85)
        
    def write_rsa_key(self,user_code, idx, priv_key, siz, key_data, dng_nr):
        if self.api is None:
            return -1

        return self.api.Dongle_WriteRSAKey( user_code, idx, priv_key, siz, key_data, dng_nr, 85)

    def get_rsa_pubkey( self, user_code, idx, pubkey, pubkey_len, dng_nr):
        if self.api is None:
            return -1

        len = c_short(pubkey_len[0])
        pLen = pointer(len)

        ret = self.api.Dongle_GetRSAPubKey(user_code, idx, pubkey, pLen, dng_nr, 85)
        if ret == 1:
            pubkey_len[0] = len.value

        return ret

    def encrypt_cos_rsa_pubkey( self, pubkey, pubkey_len, plain, plain_len, cipher, cipher_len, dng_nr):
        if self.api is None:
            return -1

        len = c_short(cipher_len[0])
        pLen = pointer(len)

        ret = self.api.EncryptByCOSRsaPubKey( pubkey, pubkey_len, plain, plain_len, cipher, pLen)
        if ret == 1:
            cipher_len[0] = len.value

        return ret

    def encrypt_data_rsa( self, user_code, idx, plain, plain_len, cipher, cipher_len, dng_nr):
        if self.api is None:
            return -1

        len = c_short(cipher_len[0])
        pLen = pointer(len)

        ret = self.api.Dongle_EncryptDataRSA(user_code, idx, plain, plain_len, cipher, pLen, dng_nr, 85)
        if ret == 1:
            cipher_len[0] = len.value

        return ret
        
    def decrypt_data_rsa( self, user_code, idx, cipher, cipher_len, plain, plain_len, dng_nr):
        if self.api is None:
            return -1

        len = c_short(plain_len[0])
        pLen = pointer(len)

        ret = self.api.Dongle_DecryptDataRSA(user_code, idx, cipher, cipher_len, plain, pLen, dng_nr, 85)
        if ret == 1:
            plain_len[0] = len.value

        return ret

    def sign_data( self, user_code, idx, msg, msg_len, sig, sig_len, dng_nr):
        if self.api is None:
            return -1

        len = c_short(sig_len[0])
        pLen =pointer(len)

        ret = self.api.Dongle_SignDataRSA( user_code, idx, msg, msg_len, sig, pLen, dng_nr, 85);
        if ret == 1:
            sig_len[0] = len.value

        return ret
    
    def verify_sig( self, user_code, idx, msg, msg_len, sig, sig_len, dng_nr):
        if self.api is None:
            return -1

        return self.api.Dongle_VerifySigRSA(user_code, idx, msg, msg_len, sig, sig_len, dng_nr, 85)

    def set_timer( self, user_code, mode, dng_nr):
        if self.api is None:
            return -1

        return self.api.Dongle_SetTimer(user_code, mode, dng_nr, 85)

    def start_timer( self, user_code, dng_nr):
        if self.api is None:
            return -1

        return self.api.Dongle_StartTimer(user_code, dng_nr, 85)

    def get_timer( self, user_code, counter, dng_nr):
        if self.api is None:
            return -1

        Cntr = c_int(counter[0])
        pCntr = pointer(Cntr)
        ret = self.api.Dongle_GetTimer(user_code, pCntr, dng_nr, 85)
        if ret == 1:
            counter[0] = Cntr.value

        return ret

    def stop_timer( self, user_code, dng_nr):
        if self.api is None:
            return -1

        return self.api.Dongle_StopTimer( user_code, dng_nr, 85)

    def registerCallbackPort(self, port):
        if self.api is None:
            return -1

        return self.api.RegisterGenjiCallbackPort(port)
