from ctypes import *

class API:
    api = None

    def __init__(self):
        # Load API module
        try:
            # 64bit API
            self.api = windll.LoadLibrary("c:\\matrix\\api\\dll\\matrix64.dll")

            # 32bit API
            #self.api = windll.LoadLibrary("c:\\matrix\\api\\dll\\matrix32.dll")

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

        # define argument types for Matrix API functions from matrix64.dll
        self.api.Dongle_Count.argtypes = [c_short]
        self.api.Dongle_MemSize.argtypes = [c_short, c_short]
        self.api.Dongle_Model.argtypes = [c_short, c_short]
        self.api.Dongle_Version.argtypes = [c_short, c_short]
        self.api.Dongle_ReadData.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short]
        self.api.Dongle_ReadDataEx.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short, c_short]
        self.api.Dongle_WriteData.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short]
        self.api.Dongle_WriteDataEx.argtypes = [c_int, POINTER(c_int), c_short, c_short, c_short, c_short]
        self.api.Dongle_ReadSerNr.argtypes = [c_int, c_short, c_short]
        self.api.Dongle_WriteKey.argtypes = [c_int, POINTER(c_int), c_short, c_short]
        self.api.Dongle_GetKeyFlag.argtypes = [c_int, c_short, c_short]
        self.api.Dongle_EncryptData.argtypes = [c_int, POINTER(c_int), c_short, c_short]
        self.api.Dongle_DecryptData.argtypes = [c_int, POINTER(c_int), c_short, c_short]
        self.api.Dongle_SetDriverFlag.argtypes = [c_int, c_short, c_short, c_short]
        self.api.Dongle_GetDriverFlag.argtypes = [c_int, c_short, c_short]
        self.api.SetConfig_MatrixNet.argtypes = [c_short, c_char_p]
        self.api.GetConfig_MatrixNet.argtypes = [c_short]
        self.api.LogIn_MatrixNet.argtypes = [c_int, c_short, c_short]
        self.api.LogOut_MatrixNet.argtypes = [c_int, c_short, c_short]

        # 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, port_no):
        if self.api is None:
            return -1
        return self.api.Dongle_MemSize(port_no, 85)

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

    def version(self, port_no):
        if self.api is None:
            return -1
        return self.api.Dongle_Version(port_no, 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, port_no):
        if self.api is None:
            return -1
        return self.api.Dongle_ReadSerNr(user_code, port_no, 85)

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

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

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

        return ret, []

    def read_data_ex(self, user_code, pos, count, port_no):
        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, port_no, 85)
        if ret == count:
            return ret, list(data)

        return ret, []

    def write_data(self, user_code, fields, port_no):
        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), port_no, 85)

    def write_data_ex(self, user_code, fields, pos, port_no):
        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), port_no, 85)

    def write_key(self, user_code, keys, port_no):
        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, port_no, 85)

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

    def encrypt_data(self, user_code, data, port_no):
        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, port_no, 85)
        if ret > 0:
            return ret, list(plain)

        return ret, []

    def decrypt_data(self, user_code, data, port_no):
        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, port_no, 85)
        if ret > 0:
            return ret, list(enc_data)

        return ret, []

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

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

    def setconfig_matrixnet(self, access, server_file):
        if self.api is None:
            return -1

        file = bytes(server_file, 'utf-8')
        return self.api.SetConfig_MatrixNet(access, file)

    def getconfig_matrixnet(self, category):
        if self.api is None:
            return -1

        return self.api.GetConfig_MatrixNet(category)

    def login(self, user_code, app_slot, port_no):
        if self.api is None:
            return -1

        return self.api.LogIn_MatrixNet( user_code, app_slot, port_no)

    def logout(self, user_code, app_slot, port_no):
        if self.api is None:
            return -1

        return self.api.LogOut_MatrixNet( user_code, app_slot, port_no)