"""Abstract CarType.

__author__ = "https://www.gemalto.com/"

Copyright 2001-2012 gemalto
Author: Jean-Daniel Aussel, mailto:jean-daniel.aussel@gemalto.com

This file is part of pyscard.

pyscard is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

pyscard is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with pyscard; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
"""

from smartcard.Exceptions import InvalidATRMaskLengthException
from smartcard.System import readers
from smartcard.util import toHexString


class CardType:
    """Abstract base class for CardTypes.

    Known subclasses: L{smartcard.CardType.AnyCardType}
    L{smartcard.CardType.ATRCardType}."""

    def __init__(self):
        """CardType constructor."""
        pass

    def matches(self, atr, reader=None):
        """Returns true if atr and card connected match the L{CardType}.

        @param atr:    the atr to check for matching
        @param reader: the reader (optional); default is None

        The reader can be used in some subclasses to do advanced
        matching that require connecting to the card."""
        pass


class AnyCardType(CardType):
    """The AnyCardType matches any card."""

    def __init__(self):
        super().__init__()

    def matches(self, atr, reader=None):
        """Always returns true, i.e. AnyCardType matches any card.

        @param atr:    the atr to check for matching
        @param reader: the reader (optional); default is None"""
        return True


class ATRCardType(CardType):
    """The ATRCardType defines a card from an ATR and a mask."""

    def __init__(self, atr, mask=None):
        """ATRCardType constructor.
        @param atr:    the ATR of the CardType
        @param mask:   an optional mask to be applied to the ATR for
            L{CardType} matching default is None
        """
        super().__init__()
        self.atr = list(atr)
        self.mask = mask
        if mask is None:
            self.maskedatr = self.atr
        else:
            if len(self.atr) != len(self.mask):
                raise InvalidATRMaskLengthException(toHexString(mask))
            self.maskedatr = list(map(lambda x, y: x & y, self.atr, self.mask))

    def matches(self, atr, reader=None):
        """Returns true if the atr matches the masked CardType atr.

        @param atr:    the atr to check for matching
        @param reader: the reader (optional); default is None

        When atr is compared to the CardType ATR, matches returns true if
        and only if CardType.atr & CardType.mask = atr & CardType.mask,
        where & is the bitwise logical AND."""

        if len(atr) != len(self.atr):
            return not True

        if self.mask is not None:
            maskedatr = list(map(lambda x, y: x & y, list(atr), self.mask))
        else:
            maskedatr = atr
        return self.maskedatr == maskedatr


if __name__ == "__main__":
    """Small sample illustrating the use of CardType.py."""
    r = readers()
    print(r)
    connection = r[0].createConnection()
    connection.connect()
    atrct = ATRCardType([0x3B, 0x16, 0x94, 0x20, 0x02, 0x01, 0x00, 0x00, 0x0D])
    print(atrct.matches(connection.getATR()))
