"""The CardConnection abstract class manages connections with a card and apdu transmission.

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

Copyright 2001-2010 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.CardConnectionEvent import CardConnectionEvent
from smartcard.Exceptions import SmartcardException
from smartcard.Observer import Observer
from smartcard.Observer import Observable

class CardConnection(Observable):
    """Card connection abstract class.

    Known subclasses: smartcard.pcsc.PCSCCardConnection
    """
    T0_protocol = 0x00000001
    T1_protocol = 0x00000002
    RAW_protocol = 0x00010000
    T15_protocol = 0x00000008


    def __init__( self, reader ):
        """Construct a new card connection.

        readerName: name of the reader in which the smartcard to connect to is located.
        """
        Observable.__init__(self)
        self.reader = reader
        self.errorcheckingchain=None
        self.defaultprotocol = CardConnection.T0_protocol | CardConnection.T1_protocol

    def __del__( self ):
        """Connect to card."""
        pass

    def addSWExceptionToFilter( self, exClass ):
        """Add a status word exception class to be filtered.

        exClass: the class to filter, e.g. smartcard.sw.SWException.WarningProcessingException

        Filtered exceptions will not be raised when encountered in the
        error checking chain."""
        if None!=self.errorcheckingchain:
            self.errorcheckingchain[0].addFilterException( exClass )

    def addObserver(self, observer):
        """Add a CardConnection observer."""
        Observable.addObserver( self, observer )

    def deleteObserver(self, observer):
        """Remove a CardConnection observer."""
        Observable.deleteObserver( self, observer )

    def connect( self, protocol=None, mode=None, disposition=None ):
        """Connect to card.
        protocol: a bit mask of the protocols to use, from CardConnection.T0_protocol, CardConnection.T1_protocol,
        CardConnection.RAW_protocol, CardConnection.T15_protocol

        mode: passed as-is to the PC/SC layer
        """
        Observable.setChanged( self )
        Observable.notifyObservers( self, CardConnectionEvent('connect') )

    def disconnect( self ):
        """Disconnect from card."""
        Observable.setChanged( self )
        Observable.notifyObservers( self, CardConnectionEvent('disconnect') )

    def getATR( self ):
        """Return card ATR"""
        pass

    def getProtocol( self ):
        """Return bit mask for the protocol of connection, or None if no protocol set.
        The return value is a bit mask of CardConnection.T0_protocol, CardConnection.T1_protocol,
        CardConnection.RAW_protocol, CardConnection.T15_protocol
        """
        return self.defaultprotocol

    def getReader( self ):
        """Return card connection reader"""
        return self.reader

    def setErrorCheckingChain( self, errorcheckingchain ):
        """Add an error checking chain.
        errorcheckingchain: a smartcard.sw.ErrorCheckingChain object
        The error checking strategies in errorchecking chain will be tested
        with each received response APDU, and a smartcard.sw.SWException.SWException
        will be raised upon error."""
        self.errorcheckingchain=errorcheckingchain

    def setProtocol( self, protocol ):
        """Set protocol for card connection.
        protocol: a bit mask of CardConnection.T0_protocol, CardConnection.T1_protocol,
        CardConnection.RAW_protocol, CardConnection.T15_protocol
        e.g. setProtocol( CardConnection.T1_protocol | CardConnection.T0_protocol )
        """
        self.defaultprotocol = protocol

    def transmit( self, bytes, protocol=None ):
        """Transmit an apdu. Internally calls doTransmit() class method and notify observers
        upon command/response APDU events.
        Subclasses must override the doTransmit() class method.

        bytes:      list of bytes to transmit

        protocol:   the transmission protocol, from CardConnection.T0_protocol, CardConnection.T1_protocol,
                    or CardConnection.RAW_protocol
        """
        Observable.setChanged( self )
        Observable.notifyObservers( self, CardConnectionEvent('command', [bytes, protocol] ) )
        data, sw1, sw2 = self.doTransmit( bytes, protocol )
        Observable.setChanged( self )
        Observable.notifyObservers( self, CardConnectionEvent( 'response', [data, sw1, sw2 ] ) )
        if None!=self.errorcheckingchain:
            self.errorcheckingchain[0]( data, sw1, sw2 )
        return data, sw1, sw2

    def doTransmit( self, bytes, protocol ):
        """Performs the command APDU transmission.

        Subclasses must override this method for implementing apdu transmission."""
        pass

    def control( self, controlCode, bytes=[] ):
        """Send a control command and buffer.  Internally calls doControl()
        class method and notify observers upon command/response events.
        Subclasses must override the doControl() class method.

        controlCode: command code
 
        bytes:       list of bytes to transmit
        """
        Observable.setChanged( self )
        Observable.notifyObservers( self, CardConnectionEvent('command', [controlCode, bytes] ) )
        data = self.doControl( controlCode, bytes )
        Observable.setChanged( self )
        Observable.notifyObservers( self, CardConnectionEvent( 'response', data ) )
        if None!=self.errorcheckingchain:
            self.errorcheckingchain[0]( data )
        return data

    def doControl( self, controlCode, bytes ):
        """Performs the command control.

        Subclasses must override this method for implementing control."""
        pass
