class DESFireDialogBase_CreateFile ( wx.Dialog ):
    
    def __init__( self, parent ):
        wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"DESFire Create File", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE )
        
        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
        
        bSizer182 = wx.BoxSizer( wx.VERTICAL )
        
        bSizer183 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextFileNo = wx.StaticText( self, wx.ID_ANY, u"File No.", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextFileNo.Wrap( -1 )
        bSizer183.Add( self._statictextFileNo, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlFileNo = wx.TextCtrl( self, wx.ID_ANY, u"00", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlFileNo.SetMaxLength( 2 ) 
        bSizer183.Add( self._textctrlFileNo, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer183, 1, wx.EXPAND, 5 )
        
        bSizer185 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextComSet = wx.StaticText( self, wx.ID_ANY, u"Com. Set.", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextComSet.Wrap( -1 )
        bSizer185.Add( self._statictextComSet, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlComSet = wx.TextCtrl( self, wx.ID_ANY, u"00", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlComSet.SetMaxLength( 2 ) 
        bSizer185.Add( self._textctrlComSet, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer185, 1, wx.EXPAND, 5 )
        
        bSizer186 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextAccessRights = wx.StaticText( self, wx.ID_ANY, u"Access Rights", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextAccessRights.Wrap( -1 )
        bSizer186.Add( self._statictextAccessRights, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlAccessRights = wx.TextCtrl( self, wx.ID_ANY, u"EEEE", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlAccessRights.SetMaxLength( 4 ) 
        bSizer186.Add( self._textctrlAccessRights, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer186, 1, wx.EXPAND, 5 )
        
        bSizer187 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextFileSize = wx.StaticText( self, wx.ID_ANY, u"File Size", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextFileSize.Wrap( -1 )
        bSizer187.Add( self._statictextFileSize, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlFileSize = wx.TextCtrl( self, wx.ID_ANY, u"000001", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlFileSize.SetMaxLength( 6 ) 
        bSizer187.Add( self._textctrlFileSize, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer187, 1, wx.EXPAND, 5 )
        
        bSizer188 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextLowerLimit = wx.StaticText( self, wx.ID_ANY, u"Lower Limit", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextLowerLimit.Wrap( -1 )
        bSizer188.Add( self._statictextLowerLimit, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlLowerLimit = wx.TextCtrl( self, wx.ID_ANY, u"00000000", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlLowerLimit.SetMaxLength( 8 ) 
        bSizer188.Add( self._textctrlLowerLimit, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer188, 1, wx.EXPAND, 5 )
        
        bSizer189 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextUpperLimit = wx.StaticText( self, wx.ID_ANY, u"Upper Limit", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextUpperLimit.Wrap( -1 )
        bSizer189.Add( self._statictextUpperLimit, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlUpperLimit = wx.TextCtrl( self, wx.ID_ANY, u"FFFFFFFF", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlUpperLimit.SetMaxLength( 8 ) 
        bSizer189.Add( self._textctrlUpperLimit, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer189, 1, wx.EXPAND, 5 )
        
        bSizer190 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextValue = wx.StaticText( self, wx.ID_ANY, u"Value", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextValue.Wrap( -1 )
        bSizer190.Add( self._statictextValue, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlValue = wx.TextCtrl( self, wx.ID_ANY, u"11111111", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlValue.SetMaxLength( 8 ) 
        bSizer190.Add( self._textctrlValue, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer190, 1, wx.EXPAND, 5 )
        
        bSizer193 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._checkboxLimitedCreditEnabled = wx.CheckBox( self, wx.ID_ANY, u"Limited Credit Enabled", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._checkboxLimitedCreditEnabled.SetValue(True) 
        bSizer193.Add( self._checkboxLimitedCreditEnabled, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer193, 1, wx.EXPAND, 5 )
        
        bSizer191 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextRecordSize = wx.StaticText( self, wx.ID_ANY, u"Record Size", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextRecordSize.Wrap( -1 )
        bSizer191.Add( self._statictextRecordSize, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlRecordSize = wx.TextCtrl( self, wx.ID_ANY, u"000010", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlRecordSize.SetMaxLength( 6 ) 
        bSizer191.Add( self._textctrlRecordSize, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer191, 1, wx.EXPAND, 5 )
        
        bSizer192 = wx.BoxSizer( wx.HORIZONTAL )
        
        self._statictextMaxNumOfRecords = wx.StaticText( self, wx.ID_ANY, u"Max Num. Of Records", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._statictextMaxNumOfRecords.Wrap( -1 )
        bSizer192.Add( self._statictextMaxNumOfRecords, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._textctrlMaxNumOfRecords = wx.TextCtrl( self, wx.ID_ANY, u"000020", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._textctrlMaxNumOfRecords.SetMaxLength( 6 ) 
        bSizer192.Add( self._textctrlMaxNumOfRecords, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer182.Add( bSizer192, 1, wx.EXPAND, 5 )
        
        bSizer194 = wx.BoxSizer( wx.HORIZONTAL )
        
        
        bSizer194.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 )
        
        self._buttonOK = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 )
        self._buttonOK.SetDefault() 
        bSizer194.Add( self._buttonOK, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        self._buttonCancel = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer194.Add( self._buttonCancel, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
        
        
        bSizer194.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 )
        
        
        bSizer182.Add( bSizer194, 1, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 5 )
        
        
        self.SetSizer( bSizer182 )
        self.Layout()
        bSizer182.Fit( self )
        
        self.Centre( wx.BOTH )
        
        # Connect Events
        self._buttonOK.Bind( wx.EVT_BUTTON, self._buttonOKOnButtonClick )
        self._buttonCancel.Bind( wx.EVT_BUTTON, self._buttonCancelOnButtonClick )
    
    def __del__( self ):
        pass
    
    
    # Virtual event handlers, overide them in your derived class
    def _buttonOKOnButtonClick( self, event ):
        event.Skip()
    
    def _buttonCancelOnButtonClick( self, event ):
        event.Skip() IDOK = 1 IDCANCEL = 2 class HexValidator(wx.PyValidator):
    """Validate Hex strings"""
    def __init__(self):
        """Initialize the validator

        """
        super(HexValidator, self).__init__()

        # Event Handlers
        self.Bind(wx.EVT_CHAR, self.OnChar)

    def Clone(self):
        """Clones the current validator
        @return: clone of this object

        """
        return HexValidator()

#     def Validate(self, win):
#         """Validate an window value
#         @param win: window to validate
# 
#         """
#         for char in val:
#             if char not in Util.HEXCHARS:
#                 return False
#         else:
#             return True

    def OnChar(self, event):
        """Process values as they are entered into the control
        @param event: event that called this handler

        """
        key = event.GetKeyCode()
        if event.CmdDown() or key < wx.WXK_SPACE or key == wx.WXK_DELETE or \
           key > 255 or chr(key) in Util.HEXCHARS:
            event.Skip()
            return

        if not wx.Validator_IsSilent():
            wx.Bell()

        return
    
    def TransferFromWindow(self, *args, **kwargs):
        return True
    
    def TransferToWindow(self, *args, **kwargs):
        return True class DESFireEx(DESFire):
    '''
    Extended DESFire class.
    '''

    def __init__(self, device):
        '''
        Constructor
        '''
        DESFire.__init__(self, device)

        self.session_cipher = None
        self.key_id = 0
    
    def communicate(self, apdu_cmd, description, allow_continue_fallthrough=False):
        """Communicate with a NFC tag.

        Send in outgoing request and waith for a card reply.

        TODO: Handle additional framing via 0xaf

        :param apdu_cmd: Outgoing APDU command as array of bytes

        :param description: Command description for logging purposes

        :param allow_continue_fallthrough: If True 0xAF response (incoming more data, need mode data) is instantly returned to the called instead of trying to handle it internally

        :raise: :py:class:`desfire.protocol.DESFireCommunicationError` on any error

        :return: tuple(APDU response as list of bytes, bool if additional frames are inbound)
        """

        result = []
        additional_framing_needed = True

        # TODO: Clean this up so read/write implementations have similar mechanisms and all continue is handled internally
        while additional_framing_needed:

            apdu_cmd_hex = [hex(c) for c in apdu_cmd]
            self.logger.debug("Running APDU command %s, sending: %s", description, apdu_cmd_hex)

            resp = self.device.transceive(apdu_cmd)
            self.logger.debug("Received APDU response: %s", byte_array_to_human_readable_hex(resp))

            if resp[-2] != 0x91:
                raise DESFireCommunicationError("Received invalid response for command: {}".format(description), resp[-2:])

            # Possible status words: https://github.com/jekkos/android-hce-desfire/blob/master/hceappletdesfire/src/main/java/net/jpeelaer/hce/desfire/DesfireStatusWord.java
            status = resp[-1]

            # Check for known error interpretation
            error_msg = ERRORS.get(status)
            if error_msg:
                raise DESFireCommunicationError(error_msg, status)

            if status == 0xaf:
                if allow_continue_fallthrough:
                    additional_framing_needed = False
                else:
                    # Need to loop more cycles to fill in receive buffer
                    additional_framing_needed = True
                    apdu_cmd = self.wrap_command(0xaf)  # Continue
            elif status != 0x00:
                raise DESFireCommunicationError("Error {:02x} when communicating".format(status), status)
            else:
                additional_framing_needed = False

            # This will un-memoryview this object as there seems to be some pyjnius
            # bug getting this corrupted down along the line
            unframed = list(resp[0:-2])
            result += unframed

        return result

    def authenticate(self, key_id, private_key=[0x00] * 16):
        apdu_command = self.wrap_command(0x0a, [key_id])
        resp = self.communicate(apdu_command, "Authenticating key {:02X}".format(key_id), allow_continue_fallthrough=True)

        # We get 8 bytes challenge
        random_b_encrypted = list(resp)
        assert len(random_b_encrypted) == 8

        initial_value = b"\00" * 8
        
        k = pyDes.triple_des(bytes(private_key), pyDes.CBC, initial_value, pad=None, padmode=pyDes.PAD_NORMAL)

        decrypted_b = [ord(b) for b in (k.decrypt(bytes(random_b_encrypted)))]

        # shift randB one byte left and get randB'
        shifted_b = decrypted_b[1:8] + [decrypted_b[0]]

        # Generate random_a
        # NOT A REAL RANDOM NUMBER AND NOT IV XORRED
#         random_a = [0x00] * 8
        random_a = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]

        decrypted_a = [ord(b) for b in k.decrypt(bytes(random_a))]

        xorred = []

        for i in range(0, 8):
            xorred.append(decrypted_a[i] ^ shifted_b[i])

        decrypted_xorred = [ord(b) for b in k.decrypt(bytes(xorred))]

        final_bytes = decrypted_a + decrypted_xorred
        assert len(final_bytes) == 16

        apdu_command = self.wrap_command(0xaf, final_bytes)
        resp = self.communicate(apdu_command, "Authenticating continues with key {:02X}".format(key_id))
        assert len(resp) == 8

        decrypted_check_a1 = [ord(b) for b in k.decrypt(bytes(resp))]
        check_a1 = [random_a[-1]] + random_a[0 : len(random_a) - 1]
        assert (decrypted_check_a1 != check_a1)

        self.logger.info("Received session key %s", byte_array_to_human_readable_hex(resp))

        if random_a == decrypted_b:
            self.session_key = random_a[0 : 4] + decrypted_b[0 : 4] + random_a[0 : 4] + decrypted_b[0 : 4]
        else:
            self.session_key = random_a[0 : 4] + decrypted_b[0 : 4] + random_a[4 : 8] + decrypted_b[4 : 8]

        initial_value = b"\00" * 8
        self.session_cipher = pyDes.triple_des(bytes(self.session_key), pyDes.CBC, initial_value, pad=None, padmode=pyDes.PAD_NORMAL)
        self.key_id = key_id
        
        return resp
    
    def parse_version(self, version_data):
        version_info = {}
        
        off = 0
        hard_info = {}
        hard_info['vendor'] = 'NXP' if version_data[off + 0] == 0x04 else 'Unknown'
        hard_info['type'] = version_data[off + 1]
        hard_info['subtype'] = version_data[off + 2]
        hard_info['major_version'] = version_data[off + 3]
        hard_info['minor_version'] = version_data[off + 4]
        hard_info['storage_size'] = version_data[off + 5] - 0x14
        hard_info['protocol_type'] = 'ISO 14443-2 and -3' if version_data[off + 6] == 0x05 else 'Unknown'
        version_info['hard_info'] = hard_info
        
        off = 7
        soft_info = {}
        soft_info['vendor'] = 'NXP' if version_data[off + 0] == 0x04 else 'Unknown'
        soft_info['type'] = version_data[off + 1]
        soft_info['subtype'] = version_data[off + 2]
        soft_info['major_version'] = version_data[off + 3]
        soft_info['minor_version'] = version_data[off + 4]
        soft_info['storage_size'] = 2 ** ((version_data[off + 5] - 0x14) / 2)
        soft_info['protocol_type'] = 'ISO 14443-3 and -4' if version_data[off + 6] == 0x05 else 'Unknown'
        version_info['soft_info'] = soft_info
        
        off = 14
        version_info['uid'] = '%02X%02X%02X%02X%02X%02X%02X' %(version_data[off + 0], version_data[off + 1], version_data[off + 2], version_data[off + 3], version_data[off + 4], version_data[off + 5], version_data[off + 6])
        
        return version_info
    
    def get_version(self):
        get_version = self.wrap_command(GET_VERSION)
        version_data = self.communicate(get_version, "Get version")
        return self.parse_version(version_data)
    
    def delete_application(self, app_id):
        delete_application = self.wrap_command(DELETE_APPLICATION, [((app_id >> 16) & 0xFF), ((app_id >> 8) & 0xFF), ((app_id >> 0) & 0xFF)])
        self.communicate(delete_application, "Delete application")
    
    def create_application(self, app_id, key_settings_1, key_settings_2):
        parameters = [
            (app_id >> 16) & 0xff,
            (app_id >> 8) & 0xff,
            (app_id >> 0) & 0xff,
            key_settings_1,
            key_settings_2,
        ]

        # CREATE_APPLICATION((byte) 0xCA),
        apdu_command = self.wrap_command(0xCA, parameters)
        self.communicate(apdu_command, "Creating application {:06X}".format(app_id))

    def create_std_data_file(self, file_no, com_set, access_rights, file_size):
        parameters = [file_no]

        assert com_set in FILE_COMMUNICATION
        parameters += [ com_set ]
        parameters += [ access_rights & 0xff, access_rights >> 8 ]
        parameters += Util.bytes3_to_byte_array(file_size)

        apdu_command = self.wrap_command(CREATE_STDDATAFILE, parameters)
        self.communicate(apdu_command, "Creating std data file {:02X}".format(file_no))

    def create_backup_data_file(self, file_no, com_set, access_rights, file_size):
        parameters = [file_no]

        assert com_set in FILE_COMMUNICATION
        parameters += [ com_set ]
        parameters += [ access_rights & 0xff, access_rights >> 8 ]
        parameters += Util.bytes3_to_byte_array(file_size)

        apdu_command = self.wrap_command(CREATE_BACKUPDATAFILE, parameters)
        self.communicate(apdu_command, "Creating backup data file {:02X}".format(file_no))

    def create_linear_record_file(self, file_no, com_set, access_rights, record_size, max_num_of_records):
        parameters = [file_no]

        assert com_set in FILE_COMMUNICATION
        parameters += [ com_set ]
        parameters += [ access_rights & 0xff, access_rights >> 8 ]
        parameters += Util.bytes3_to_byte_array(record_size)
        parameters += Util.bytes3_to_byte_array(max_num_of_records)

        apdu_command = self.wrap_command(CREATE_LINEAR_RECORD_FILE, parameters)
        self.communicate(apdu_command, "Creating linear record file {:02X}".format(file_no))

    def create_cyclic_record_file(self, file_no, com_set, access_rights, record_size, max_num_of_records):
        parameters = [file_no]

        assert com_set in FILE_COMMUNICATION
        parameters += [ com_set ]
        parameters += [ access_rights & 0xff, access_rights >> 8 ]
        parameters += Util.bytes3_to_byte_array(record_size)
        parameters += Util.bytes3_to_byte_array(max_num_of_records)

        apdu_command = self.wrap_command(CREATE_CYCLIC_RECORD_FILE, parameters)
        self.communicate(apdu_command, "Creating cyclic record file {:02X}".format(file_no))

    def get_file_settings(self, file_id):

        apdu_command = self.wrap_command(GET_FILE_SETTINGS, [file_id])
        resp = self.communicate(apdu_command, "Reading file settings {:02X}".format(file_id))

        file_type = resp[0]

        file_settings = {
            "type": file_type,
            "type_str": FILE_TYPES[file_type],
            "com_set": FILE_COMMUNICATION[resp[1]],
            "access_rights": resp[2:4],
        }

        if (file_type == 0x00) or (file_type == 0x01):
            # Data file
            file_settings.update({
                "file_size": Util.byte_array3_to_dword(resp[4:7]),
            })
        elif (file_type == 0x02):
            # Value file
            file_settings.update({
                "lower_limit": Util.byte_array4_to_dword(resp[4:8]),
                "upper_limit": Util.byte_array4_to_dword(resp[8:12]),
                "value": Util.byte_array4_to_dword(resp[12:16]),
                "limited_credit_enabled": True if (resp[16] != 0) else False
            })
        elif (file_type == 0x03) or (file_type == 0x04):
            # Linear record file
            file_settings.update({
                # Length of ONE records
                "record_size": Util.byte_array3_to_dword(resp[4:7]),
                "max_num_of_records": Util.byte_array3_to_dword(resp[7:10]),
                "current_num_of_records": Util.byte_array3_to_dword(resp[10:13]),
            })
        else:
            pass

        return file_settings

    def change_key(self, cur_key_id, key_id, key, new_key):
        if len(key) != 16:
            raise Exception('Invalid key length.')
        if len(new_key) != 16:
            raise Exception('Invalid key length.')

        data = []
        if self.key_id != key_id:
            xored_key = []
            for i in range(len(new_key)):
                xored_key.append(key[i] ^ new_key[i])
            crc1 = Util.calculate_crc(xored_key, len(xored_key), 0x6363)
            crc2 = Util.calculate_crc(new_key, len(new_key), 0x6363)
            data = xored_key + [(crc1 & 0xFF), (crc1 >> 8) & 0xFF] + [(crc2 & 0xFF), (crc2 >> 8) & 0xFF] + [0x00, 0x00, 0x00, 0x00]
        else:
            crc = Util.calculate_crc(new_key, len(new_key), 0x6363)
            data = new_key + [((crc >> 8) & 0xFF), (crc & 0xFF)] + [0x00] * 6
        decrypted_data = self.session_cipher.decrypt(bytes(data))
        decrypted_data = [ord(b) for b in decrypted_data]

        apdu_command = self.wrap_command(CHANGE_KEY, [key_id] + decrypted_data)
        self.communicate(apdu_command, "Change key")

    def get_key_settings(self):
        apdu_command = self.wrap_command(GET_KEY_SETTINGS)
        resp = self.communicate(apdu_command, "Get key settings")
        return resp

    def clear_record_file(self, file_id):
        apdu_command = self.wrap_command(CLEAR_RECORD_FILE, [file_id])
        self.communicate(apdu_command, "Clear record file")
    
    def commit_transaction(self):
        self.commit()

    def abort_transaction(self):
        apdu_command = self.wrap_command(ABORT_TRANSACTION)
        self.communicate(apdu_command, "Abort file changes")
        
    def read_data(self, file_id, offset, length):
        offset_bytes = Util.bytes3_to_byte_array(offset)
        length_bytes = Util.bytes3_to_byte_array(length)
        parameters = [file_id] + offset_bytes + length_bytes

        start_time = time.time()
        apdu_command = self.wrap_command(0xbd, parameters)

        data = self.communicate(apdu_command, "Reading data file {:02X}".format(file_id))

        duration = time.time() - start_time
        self.logger.debug("Finished reading %d bytes in %f seconds", len(data), duration)

        return data
    
    def write_data(self, file_id, offset, length, data):
        offset_bytes = Util.bytes3_to_byte_array(offset)
        length_bytes = Util.bytes3_to_byte_array(length)
        parameters = [file_id] + offset_bytes + length_bytes

        data_pointer = 0
        max_apdu_write_length = 47  # Actual max value found by experimenting
        command = 0x3d  # WRITE DATA
        cycles = 0
        start_time = time.time()

        self.logger.debug("Attempting to write %d bytes to a data file %02x", len(data), file_id)

        while data_pointer < len(data):
            chunk = data[data_pointer:data_pointer + max_apdu_write_length]
            parameters = parameters + chunk
            apdu_command = self.wrap_command(command, parameters)
            self.communicate(apdu_command, "Writing line record file file {:02X}, current command {:02X} write pointer: {}".format(file_id, command, data_pointer), allow_continue_fallthrough=True)

            data_pointer += max_apdu_write_length

            # Use different parameters for the 2nd ... nth write cycle
            command = 0xaf  # CONTINUE
            max_apdu_write_length = 54
            parameters = []
            cycles += 1

        duration = time.time() - start_time
        self.logger.debug("Finished writing %d bytes in %d commands and %f seconds", len(data), cycles, duration)
    
    def credit(self, file_id, value):
        self.credit_value(file_id, value)
    
    def debit(self, file_id, value):
        self.debit_value(file_id, value)
    
    def limited_credit(self, file_id, value):
        value_bytes = dword_to_byte_array(value)

        apdu_command = self.wrap_command(LIMITED_CREDIT, [file_id] + value_bytes)
        self.communicate(apdu_command, "Limited credit file {:02X} value {:08X}".format(file_id, value))
    
    def write_record(self, file_id, offset, length, data):
        offset_bytes = Util.bytes3_to_byte_array(offset)
        length_bytes = Util.bytes3_to_byte_array(length)
        parameters = [file_id] + offset_bytes + length_bytes

        data_pointer = 0
        max_apdu_write_length = 47  # Actual max value found by experimenting
        command = WRITE_RECORD  # WRITE DATA
        cycles = 0
        start_time = time.time()

        self.logger.debug("Attempting to write %d bytes to a record file %02x", len(data), file_id)

        while data_pointer < len(data):
            chunk = data[data_pointer:data_pointer + max_apdu_write_length]
            parameters = parameters + chunk
            apdu_command = self.wrap_command(command, parameters)
            self.communicate(apdu_command, "Writing line record file file {:02X}, current command {:02X} write pointer: {}".format(file_id, command, data_pointer), allow_continue_fallthrough=True)

            data_pointer += max_apdu_write_length

            # Use different parameters for the 2nd ... nth write cycle
            command = 0xaf  # CONTINUE
            max_apdu_write_length = 54
            parameters = []
            cycles += 1

        duration = time.time() - start_time
        self.logger.debug("Finished writing %d bytes in %d commands and %f seconds", len(data), cycles, duration)
    
    def read_records(self, file_id, offset, length):
        offset_bytes = Util.bytes3_to_byte_array(offset)
        length_bytes = Util.bytes3_to_byte_array(length)
        parameters = [file_id] + offset_bytes + length_bytes
        
        apdu_command = self.wrap_command(READ_RECORDS, parameters)
        return self.communicate(apdu_command, "Reading bytes from offset {} length {} in a line record file file {:02X}".format(offset, length, file_id)) CREATE_STDDATAFILE          = 0xCD CREATE_BACKUPDATAFILE       = 0xCB CREATE_VALUE_FILE           = 0xCC CREATE_LINEAR_RECORD_FILE   = 0xC1 CREATE_CYCLIC_RECORD_FILE   = 0xC0
from pyResMan.BaseDialogs.pyResManDESFireDialogBase_CreateFile import DESFireDialogBase_CreateFile
from pyResMan.Util import IDOK, IDCANCEL
from pyResMan.Util import HexValidator
from pyResMan.DESFireEx import DESFireEx, CREATE_STDDATAFILE,\
    CREATE_BACKUPDATAFILE, CREATE_VALUE_FILE, CREATE_LINEAR_RECORD_FILE,\
    CREATE_CYCLIC_RECORD_FILE
# -*- coding: utf-8 -*-

'''
Modified on 2017-03-28

@author: javacardos@gmail.com
@organization: https://www.javacardos.com/
@copyright: JavaCardOS Technologies. All rights reserved.
'''


###########################################################################
## Class DESFireDialog_CreateFile
###########################################################################

class DESFireDialog_CreateFile ( DESFireDialogBase_CreateFile ):
    
    def __init__( self, parent, file_type ):

        DESFireDialogBase_CreateFile.__init__ ( self, parent )