from smartcard.CardConnection import CardConnection
from smartcard.CardRequest import CardRequest
from smartcard.CardType import AnyCardType
from smartcard.util import toHexString


class sCard():
    def __init__(self, app):
        self.app = app

        cardtype = AnyCardType()
        cardrequest = CardRequest(timeout=1, cardType=cardtype)
        self.cardservice = cardrequest.waitforcard()
        self.cardservice.connection.connect(CardConnection.T1_protocol)

    def getAtr(self):
        # self.app.print_message('TCA', 'Request ATR')
        atr = self.cardservice.connection.getATR()
        self.app.print_message('TCA', 'ATR: ' + toHexString(atr))

        return atr

    def scConnect(self):
        # check tacho is connected
        return self.cardservice.connection.connect()

    def scDisconnect(self):
        self.cardservice.connection.disconnect()

    def readBinaryEF(self):
        # get only ICC file. Select File + Read Binary
        # cardSerialNumber = terminalCard.readBinaryEF(0x0002, 1, 8);
        apdu = [0x00, 0xA4, 0x02, 0x0C, 0x02, 0x00, 0x02]
        # print('>> Sending to SC. SE ICC: ' + toHexString(apdu))
        # self.app.print_message('TCA', 'APDU: ' + toHexString(apdu) + ' - Request ICC file')

        response, sw1, sw2 = self.cardservice.connection.transmit(apdu)
        # print('<< Getting from SC. SE ICC response:', toHexString(response), 'Statuswords:', "%x %x" % (sw1, sw2), '-', self.checkStatusWords(sw1, sw2))
        # self.app.print_message('TCA', 'APDU ACK:' + toHexString(response) + 'Statuswords:' + "%x %x" % (sw1, sw2) + ' - ' + self.checkStatusWords(sw1, sw2))

        apdu = [0x00, 0xB0, 0x00, 0x01, 0x08]
        # print('>> Sending to SC. Read Binary Selected FILE: ' + toHexString(apdu))
        # self.app.print_message('TCA', 'APDU: ' + toHexString(apdu) + ' - Select ICC file')

        response, sw1, sw2 = self.cardservice.connection.transmit(apdu)
        # self.app.print_message('TCA', 'APDU ACK:' + toHexString(response) + ' Statuswords: ' + "%x %x" % (sw1, sw2) + ' - ' + self.checkStatusWords(sw1, sw2))

        # print('<< Getting from SC. ICC Binary data:', toHexString(response), 'Statuswords:', "%x %x" % (sw1, sw2), '-', self.checkStatusWords(sw1, sw2))

        return response

    def transmitAPDU(self, apdu):
        if len(apdu) > 2:
            # Ruptela libs. Apply only for ruptela devices!

            # Class byte of an APDU commands
            CLA = apdu[0]
            # Instruction byte of an APDU command
            INS = apdu[1]
            # Parameter bytes
            P1 = apdu[2]
            P2 = apdu[3]
            # Length of the input data for a APDU command
            Lc = apdu[4]
            # current data length
            dataLen = len(apdu[5:])

            # self.app.print_message('Server', 'CLA: ' + str(CLA))
            # self.app.print_message('Server', 'INS: ' + str(INS))
            # self.app.print_message('Server', 'P1: ' + str(P1))
            # self.app.print_message('Server', 'P2: ' + str(P2))
            # self.app.print_message('Server', 'Lc: ' + str(Lc))

            # self.app.print_message('Server', 'cmd len is ' + str(len(apdu)))

            if len(apdu) == 14 or Lc == dataLen:
                if CLA == 0x0C and INS == 0xB0:
                    # self.app.print_message('Server', 'Ruptela G1/2_1')
                    apdu.append(0x00)

            if len(apdu) == 21 and CLA == 0x00 and INS == 0x88:
                # self.app.print_message('Server', 'Ruptela G1_2')
                apdu.append(0x80)

            if Lc == dataLen:
                if CLA == 0x00 and INS == 0x86:
                    # self.app.print_message('Server', 'Ruptela G2_2')
                    apdu.append(0x00)
                elif CLA == 0x0C and INS == 0xA4:
                    # self.app.print_message('Server', 'Ruptela G2_3')
                    apdu.append(0x00)

        self.app.print_message('Server', 'APDU: ' + toHexString(apdu))

        response, sw1, sw2 = self.cardservice.connection.transmit(apdu)
        self.app.print_message('TCA', 'APDU ACK:' + toHexString(response) + ' Statuswords: ' + "%x %x" % (sw1, sw2) + ' - ' + self.checkStatusWords(sw1, sw2))

        apduAck = []
        # if list not null add to general apdu command. Command contain only 2 bytes.
        if response:
            apduAck = response
        apduAck.append(sw1)
        apduAck.append(sw2)

        if len(apduAck) == 2:
            if sw1 == 0x61:
                # TCS_79. Page 202
                apdu = [0x00, 0xC0, 0x00, 0x00, sw2]
                self.app.print_message('TCA', 'APDU: ' + toHexString(apdu) + 'Additional request data from SC.')
                # execute command with smart card

                response, sw1, sw2 = self.cardservice.connection.transmit(apdu)
                self.app.print_message('TCA', 'APDU ACK:' + toHexString(response) + ' Statuswords: ' + "%x %x" % (sw1, sw2) + ' - ' + self.checkStatusWords(sw1, sw2))

            elif CLA == 0x00 and INS == 0xB0 and P1 == 0x82 and P2 == 0xC8 and Lc == 0x00 and sw1 == 0x6C and sw2 == 0x05:
                self.app.print_message('TCA', 'APDU: ' + toHexString(apdu) + 'Stoneridge FIX. Additional request.')
                apdu = [0x00, 0xB0, 0x82, 0xC8, 0x00, sw2]

                response, sw1, sw2 = self.cardservice.connection.transmit(apdu)
                self.app.print_message('TCA', 'APDU ACK:' + toHexString(response) + ' Statuswords: ' + "%x %x" % (sw1, sw2) + ' - ' + self.checkStatusWords(sw1, sw2))

        return apduAck


    def checkMasterFileG1(self):
        apdu = [0x00, 0xA4, 0x04, 0x0C, 0x06, 0xFF, 0x54, 0x41, 0x43, 0x48, 0x4F]
        response, sw1, sw2 = self.cardservice.connection.transmit(apdu)
        # print('<< G1 MasterFile. APDU response:', toHexString(response), 'Statuswords:', "%x %x" % (sw1, sw2), '-', self.checkStatusWords(sw1, sw2))
        # self.app.print_message('TCA', 'MF G1 ACK:' + toHexString(response) + ' Statuswords: ' + "%x %x" % (sw1, sw2) + ' - ' + self.checkStatusWords(sw1, sw2))

        if sw1 == 0x90 and sw2 == 0x00:
            return True
        else:
            return False

    def checkMasterFileG2(self):
        apdu = [0x00, 0xA4, 0x04, 0x0C, 0x06, 0xFF, 0x53, 0x4D, 0x52, 0x44, 0x54]
        response, sw1, sw2 = self.cardservice.connection.transmit(apdu)
        # print('<< G2 MasterFile. APDU response:', toHexString(response), 'Statuswords:', "%x %x" % (sw1, sw2), '-', self.checkStatusWords(sw1, sw2))
        # self.app.print_message('TCA', 'MF G2 ACK:' + toHexString(response) + ' Statuswords: ' + "%x %x" % (sw1, sw2) + ' - ' + self.checkStatusWords(sw1, sw2))

        if sw1 == 0x90 and sw2 == 0x00:
            return True
        else:
            return False

    def checkIsTachographCard(self):
        if self.checkMasterFileG1():
            return True
        elif self.checkMasterFileG2():
            return True
        else:
            return False


    def checkStatusWords(self, sw1, sw2):
        # SW1, SW2 incomming as decimal. Key is string representation in HEX
        key = toHexString([sw1, sw2])

        # Description of Status Words

                        # all is ok
        status_words = {'90 00': 'Normal processing',

                        # processing with a warning
                        '62 81': 'Warning processing. Part of returned data may be corrupted',
                        '63 00': 'Authentication failed (Warning)',

                        # execution error
                        '64 00': 'Execution error — State of non-volatile memory unchanged. Integrity error',
                        '65 00': 'Execution error — State of non-volatile memory changed',
                        '65 81': 'Execution error — State of non-volatile memory changed — Memory failure',
                        '66 88': 'Security error: wrong cryptographic checksum (during Secure Messaging) '
                                 'or wrong certificate (during certificate verification) '
                                 'or wrong cryptogram (during external authentication) '
                                 'or wrong signature (during signature verification)',

                        # Control error
                        '67 00': 'Wrong length (wrong Lc or Le)',
                        '68 82': 'Secure messaging not supported',
                        '68 83': 'Last command of the chain expected',
                        '69 00': 'Forbidden command (no response available in T=0)',
                        '69 82': 'Security status not satisfied.',
                        '69 83': 'Authentication method blocked.',
                        '69 85': 'Conditions of use not satisfied.',
                        '69 86': 'Command not allowed (no current EF).',
                        '69 87': 'Expected Secure Messaging Data Objects missing',
                        '69 88': 'Incorrect Secure Messaging Data Objects',
                        '6A 80': 'Incorrect parameters in data field',
                        '6A 82': 'File not found.',
                        '6A 86': 'Wrong parameters P1-P2.',
                        '6A 88': 'Referenced data not found.',
                        '6B 00': 'Wrong parameters (offset outside the EF).',
                        '6D 00': 'Instruction code not supported or invalid.',
                        '6E 00': 'Class not supported.',
                        '6F 00': 'Other checking errors'}

        try:
            value = status_words[key]

        except KeyError:
            # Dynamic Status Word
            if '61 ' in key:
                # 61 xx
                value = 'Normal processing. XX = number of response bytes available.'
            elif '63 C' in key:
                # 63 Cx
                value = 'Wrong CHV (PIN). Remaining attempts counter provided by ‘X’.'
            elif '6C ' in key:
                # 6C xx
                value = 'Wrong length, SW2 indicates the exact length. No data field is returned.'
            else:
                value = 'Unknown error'

        return value