/*! @file

    This file offers a C++ access to the PCSC library.

    @id $Id$
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

#ifndef PCSC_HXX
#define PCSC_HXX

#include <cryptaux.hxx>

#include <string>

#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
  #undef UNICODE
  #undef DATADIR
  #include <winscard.h>
  #undef ERROR
  #ifndef MAX_ATR_SIZE
    #define MAX_ATR_SIZE 33
  #endif
  namespace pcsc {
    
    #ifdef UNICODE
      inline std::wstring strconv(std::string s) {
        return std::wstring(s.begin(), s.end());
      }
      inline std::string strconv(std::wstring s) {
        return std::string(s.begin(), s.end());
      }
      typedef wchar_t char_t;
      typedef std::wstring string;
    #else
      inline const std::string& strconv(const std::string& s) {
        return s;
      }
      typedef char char_t;
      typedef std::string string;
    #endif
  }
#else
  #include <PCSC/pcsclite.h>
  #include <PCSC/wintypes.h>
  #include <PCSC/winscard.h>
  namespace pcsc {
    inline const std::string& strconv(const std::string& s) {
      return s;
    }
    typedef char char_t;
    typedef std::string string;
  }
#endif

#include <vector>
#include <map>
#include <mrw/checkcxx11.hxx>
#include <memory>

#include <sstream>
#include <iomanip>

/*! @defgroup gpcsc C++ Wrapper around pcsc-lite API

    This library is a C++ wrapper to the awful pcsc-lite interface.
    The reason for this wrapper is to get a nice object oriented
    interface written in C++ manner and using standard types and so to
    avoid the ugly M$-C-quirks. This interface is memory clean.
    @todo: Not implemented, not supported:
        - SCardGetStatusChange(hContext, 0, rgReaderStates, 1);
        - several other
    I do not need these, and I don't know the usecase where they are
    required.  If you need something that is not yet supported, please
    let me know, what you need and why. Then I'll add it so that it
    best fits the common needs. */
//@{
/*! @defgroup pcsclib PCSC C++ Library */
/*! @defgroup pcscexceptions PCSC Exceptions */

//! @ref gpcsc @copydoc gpcsc
namespace pcsc {

  //============================================================================
  //! @addtogroup pcscexceptions
  //@{

  //----------------------------------------------------------------------------
  class exception: public std::exception {
    public:
      exception(const std::string& reason) throw(): _what("pcsc: "+reason) {
        CRYPTOLOG("ERROR: "<<what());
      }
      ~exception() throw() {}
      const char* what() const throw() {
        return _what.c_str();
      }
    private:
      std::string _what;
  };
  //----------------------------------------------------------------------------
  class not_implemented: public exception {
    public:
      not_implemented(const std::string& reason) throw():
          exception("feature is not implemented: "+reason) {
      }
  };
  //----------------------------------------------------------------------------
  class access_error: public exception {
    public:
      access_error(const std::string& reason) throw():
          exception("smardcard access error: "+reason) {
      }
  };
  //----------------------------------------------------------------------------
  class wrong_pin: public access_error {
    public:
      wrong_pin(const std::string& reason) throw():
          access_error("wrong pin: "+reason) {
      }
  };
  //----------------------------------------------------------------------------
  class runtime_error: public exception {
    public:
      runtime_error(const std::string& reason, const std::string& data) throw():
          exception("runtime error, "+reason+": "+crypto::hex(data)) {}
  };
  //----------------------------------------------------------------------------
  class neesting_error: public exception {
    public:
      neesting_error() throw():
          exception("neesting error: more endTransaction than beginTransaction")
      {}
  };
  //@}

  //! @addtogroup pcsclib
  //@{

  //============================================================================
  class Connection {
      
      //...............................................................typedefs
    public:
      

      //------------------------------------------------------------------Reader
      friend class Reader;
      class Reader {

          //............................................................typedefs
        public:

          friend class Transaction;
          //! Scoped transaction.
          /*! This is a scoped transaction. That means, the
              transaction is started in the constructor and ended in
              the destructor, unless it is already ended before.  In
              case of an exception or leaving the block, the
              transaction is always cleaned up.

              There's a neesting counter to make sure, transaction is
              started only once and ended only once, even though the
              locking can be neested.

              @code
              pcsc::Connection c;
              {
              Transaction t(c.reader("name"));
              [...] // do some stuff, possible exceptions thrown
              if (problem) return; // automatically ended
              [...]
              t.end(); // ended if we reach this line
              [...]
              } // also ended, unless commit reaced
              @endcode */
          class Transaction {
            public:
              //! Begins a transaction.
              /*! @note Please note that the Reader is required in the
                  destructor und must therefore live longer than the
                  Transaction instance. */
              Transaction(std::shared_ptr<Reader> r):
                  _reader(r), _running(true) {
                CRYPTOLOG("log");
                _reader->beginTransaction();
              }
              //! Cancels the transaction if not yet finished.
              ~Transaction() try {
                CRYPTOLOG("log");
                end();
              } catch (...) {
                if (!std::uncaught_exception()) throw;
              }
              //! Ends the running transaction.
              void end() {
                if (_running) _reader->endTransaction();
                _running = false;
              }
            private:
              std::shared_ptr<Reader> _reader;
              bool _running;
          };

          //! State and attribute list of a reader.
          class Status {
            public:
              Status(DWORD s, const std::string& a):
                  state(s), atr(a) {}
              const DWORD state;
              const std::string atr;
          };
      
          //.............................................................methods
        public:

          //! Disconnects connection.
          ~Reader() {
            CRYPTOLOG("Disconnect Reader");
            _state = SCardDisconnect(_id, SCARD_RESET_CARD);
            /// ignore failure in disconnect,  possibly smartcard has
            /// been removed
          }

          //! Get reader status.
          Status status() {
            DWORD dummy(0);
            DWORD s;
            DWORD len(MAX_ATR_SIZE);
            unsigned char a[MAX_ATR_SIZE];
            check(SCardStatus(_id, 0, &dummy, &s, &_protocol, a, &len),
                  "query smartcard status");
            return Status(s, std::string((char*)a, len));
          }

          //! Transmit data to reader.
          /*! @note Take care: Stings may contain embedded @c 0. */
          std::string transmit(char cla, char ins, char p1, char p2,
                               const std::string& lc = std::string(),
                               unsigned char le = 253) {
            std::string claInsP1P2;
            claInsP1P2.push_back(cla);
            claInsP1P2.push_back(ins);
            claInsP1P2.push_back(p1);
            claInsP1P2.push_back(p2);
            return transmit(claInsP1P2, lc, le);
          }

          //! Transmit data to reader.
          /*! @note Take care: Stings may contain embedded @c 0. */
          std::string transmit(char cla, char ins, char p1, char p2,
                               unsigned char le) {
            std::string claInsP1P2;
            claInsP1P2.push_back(cla);
            claInsP1P2.push_back(ins);
            claInsP1P2.push_back(p1);
            claInsP1P2.push_back(p2);
            return transmit(claInsP1P2, std::string(), le);
          }
          
          //! Transmit data to reader.
          /*! @note Take care: Stings may contain embedded @c 0. */
          std::string transmit(char cla, char ins, char p1, char p2,
                               const char* lc, int len,
                               unsigned char le = 253) {
            std::string claInsP1P2;
            claInsP1P2.push_back(cla);
            claInsP1P2.push_back(ins);
            claInsP1P2.push_back(p1);
            claInsP1P2.push_back(p2);
            return transmit(claInsP1P2, std::string(lc, len), le);
          }
          
          //! Transmit data to reader.
          /*! @note Take care: Stings may contain embedded @c 0.
              @note Prefer the transmit methods that passes @c cla, @c
                    ins, @c p1 and @c p2 separate.*/
          std::string transmit(const std::string& claInsP1P2,
                               const std::string& lc,
                               unsigned char le = 253) {
            if (claInsP1P2.size()!=4)
              throw runtime_error("transmit: claInsP1P2 must be 4 byte",
                                  claInsP1P2);
            if (lc.size()>255) throw runtime_error("transmit: lc too long", lc);
            std::string msg(claInsP1P2);
            if (lc.size()) (msg+=(char)lc.size())+=lc;
            msg+=le;
            return transmit(msg);
          }
          
          //! Transmit data to reader.
          /*! @note Take care: Stings may contain embedded @c 0.
              @note Prefer the transmit methods that passes @c cla, @c
                    ins, @c p1 and @c p2 separate.*/
          std::string transmit(std::string in) {
            const DWORD bufflen(1024);
            DWORD len(bufflen); // arbitrary
            unsigned char buff[bufflen];
            SCARD_IO_REQUEST rPci;
            rPci.dwProtocol  = pci()->dwProtocol;
            rPci.cbPciLength = sizeof(rPci);
            // log only in verbose debuggung; could log pins
            CRYPTOLOG_VERBOSE("SCardTransmit: "<<crypto::hex(in));
            try {
              check(SCardTransmit(_id, &rPci,
                                  (LPCBYTE)in.c_str(),
                                  in.size(),
                                  0, buff, &len),
                    "smartcard transmit message "+crypto::hex(in));
            } catch (std::exception& x) {
              CRYPTOLOG("failed with "<<x.what());
              // try to fix Apple's Mac OS X 10.10 implementation bug
#             ifdef __APPLE__
                /*! @bug Work around Mac OSX 10.10 bug. On Mac OSX
                         10.10 there is a bug in PCSC: After a
                         reconnect, first transaction (SCardTransmit)
                         fails with SCARD_W_RESET_CARD
                         (0x80100068). */
                for (int cnt(0); cnt<10 && _state==SCARD_W_RESET_CARD; ++cnt) {
                  // just try to resend
                  CRYPTOLOG("Mac OS X 10.10 implementation bug: "
                            "On Mac OSX 10.10 there is a bug in "
                            "PCSC: After a reconnect, first "
                            "transaction (SCardTransmit) fails "
                            "with SCARD_W_RESET_CARD (0x80100068). "
                            "Retry Nr. "<<cnt);
                  try {
                    reconnect();
                    if (check(SCardTransmit(_id, &rPci,
                                            (LPCBYTE)in.c_str(),
                                            in.size(),
                                            0, buff, &len),
                              "smartcard transmit resend message "
                              +crypto::hex(in))) {
                      CRYPTOLOG("successful after retry nr. "<<cnt);
                      return std::string((char*)buff, len);
                    }
                  } catch (std::exception& x) {
                    CRYPTOLOG("failed again with "<<x.what());
                  }
                }
#             endif
              CRYPTOLOG("definitely failed with "<<x.what());
              throw; // just rethrow otherwise
            }
            //CRYPTOLOG(" -> "<<crypto::hex(std::string((char*)buff, len)));
            return std::string((char*)buff, len);
          }

          //! Transmit control command and data to the reader.
          /*!
              * @note Take care: Strings may contain embedded @c 0.
              */
          std::string control(DWORD controlCode,
                              std::string in) {
            DWORD len(256); // arbitrary
            UCHAR dataBuffer[256];
            CRYPTOLOG("SCardControl: "<<"Command: "<<controlCode);
            CRYPTOLOG(" -> "<<crypto::hex(in));
            check(SCardControl(_id, controlCode,
                               (LPCBYTE)in.c_str(), in.size(),
                               dataBuffer, sizeof(dataBuffer), &len),
                  "smartcard control message sent");
            CRYPTOLOG(" -> "<<crypto::hex(std::string((char*)dataBuffer, len)));
            return std::string((char*)dataBuffer, len);
          }
          
          //! @c false if last operation was not successful
          operator bool() const {
            //  Values are 32 bit values layed out as follows:
            //   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
            //   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
            //   ---+-+-+-----------------------+-------------------------------
            //   Sev|C|R|     Facility          |               Code            
            //   ---+-+-+-----------------------+-------------------------------
            //  where Sev - is the severity code
            //          00 - Success
            //          01 - Informational
            //          10 - Warning
            //          11 - Error
            // So everything with Sev=00 is successful
            // theoretically even with Sev=01, but that's still rejected
            //return (_state>>30&3)==0;
            return _state==SCARD_S_SUCCESS;
          }
          
          //...........................................................variables
        public:
      
          const std::string name;

          //.............................................................methods
        private:

          //! Use scoped transactions with @ref Transaction
          /*! Calls @c SCardBeginTransaction unless it's already inside
              a transaction. */
          void beginTransaction() {
            if (++_neesting==1) {
              CRYPTOLOG("open transaction");
              check(SCardBeginTransaction(_id), "smartcard begin transaction");
            }
          }

          //! Use scoped transactions with @ref Transaction
          /*! Calls @c SCardEndTransaction if it's inside a transaction.
              
              @throws neesting_error if there are more calls to @c
              endTransaction than to @c beginTransaction. */
          void endTransaction() {
            if (--_neesting==0) {
              CRYPTOLOG("close transaction");
              check(SCardEndTransaction(_id, SCARD_LEAVE_CARD),
                    "smartcard end transaction");
            } else if (_neesting<0) {
              _neesting = 0;
              throw neesting_error();
            }
          }

          /*! @throw not_implemented if _protocol is unknown. */
          const SCARD_IO_REQUEST* pci() {
            switch(_protocol) {
              case SCARD_PROTOCOL_T0: return SCARD_PCI_T0;
              case SCARD_PROTOCOL_T1: return SCARD_PCI_T1;
            }
            if (_connection->exc()) throw not_implemented("unknown protocol");
            return 0;
          }
          
          //! Sets state and throws an exception if neccessary.
          /*! @throw access_error if it is instanciated for exceptions and
              an error occured in the last command. */
          bool check(DWORD state, const std::string context="") {
            _state = state;
            return _connection->check(state, context);
          }

          //! Reconnect SmartCard After Reset
          /*! @param mode one of
                       - @c SCARD_LEAVE_CARD
                         Do not do anything special on reconnect.
                       - @c SCARD_RESET_CARD
                         Reset the card (Warm Reset).
                       - @c SCARD_UNPOWER_CARD
                         Power down the card and reset it (Cold Reset). */
          void reconnect(DWORD mode=SCARD_LEAVE_CARD) {
            check(SCardReconnect(_id, _mode, _protocol, mode, &_protocol));
          }

          //! Only Connection is allowed to instanciate.
          friend class Connection;
          
          //! Establishes a connection to the given named cardreader
          Reader(const std::string& nm, std::shared_ptr<Connection> c,
                 DWORD mode=SCARD_SHARE_SHARED,
                 DWORD protocol=SCARD_PROTOCOL_T1):
            name(nm), _connection(c), _mode(mode) {
            CRYPTOLOG("Connect Reader");
            check(SCardConnect(_connection->id(), strconv(name).c_str(),
                               _mode, protocol,
                               &_id, &_protocol),
                  "connect smartcard \""+name+"\"");
          }

          //! forbidden
          Reader();

          //...........................................................variables
        private:

          std::shared_ptr<Connection> _connection;
          SCARDHANDLE _id;
          DWORD _state;
          DWORD _mode;
          DWORD _protocol;
          static int _neesting;
      
      };
      //------------------------------------------------------------------Reader
      
      enum Scope {
        USER     = SCARD_SCOPE_USER,
        TERMINAL = SCARD_SCOPE_TERMINAL,
        SYSTEM   = SCARD_SCOPE_SYSTEM
      };

      typedef std::vector<std::string> Strings;
      
      //................................................................methods
    public:

      //! Closes the connection (releases the smartcard context)
      ~Connection() {
        CRYPTOLOG("Close Connection id="<<_id);
        _state = SCardReleaseContext(_id);
        /// ignore failure in disconnect,  possibly smartcard has
        /// been removed
      }

      //! Scans for available readers from a specified list of groups.
      /*! Defaults to all groups. */
      static Strings scan(const Strings& groups = Strings(),
                          Scope s=USER, bool exceptions=true) {
        Connection c(s, exceptions);
        Strings res;
        std::string grp(c.join(groups));
        DWORD num(0);
        if (!c.check(SCardListReaders(c._id,
                                      groups.size()?strconv(grp).data():0, 0,
                                      &num),
                     "smartcard get size of readers of groups "+grp))
          return res;
        CRYPTOLOG("size of readers: "<<num);
        if (!num) return res;
        std::auto_ptr<char_t> nm(new char_t[num]);
        if (!c.check(SCardListReaders(c._id,
                                      groups.size()?strconv(grp).data():0,
                                      nm.get(), &num),
                     "smartcard list reader names of groups "+grp))
          return res;
        CRYPTOLOG("got all readers, size is "<<num);
        if (!num) return res;
        CRYPTOLOG("list of readers: "
                  <<crypto::readable(std::string(nm.get(), num-1)));
        return res = c.split(strconv(string(nm.get(), num-1)));
      }

      //! Find all readers with a given ATR.
      /*! @param atr full or partial ATR to match to the reader's ATR
          @param s scope of scanning
          @param exceptions whether exeptions should be thrown on error
          @returns list of readers that contain @c atr in their ATR */
      static Strings getReadersWithAtr(const std::string& atr,
                                       Scope s=USER, bool exceptions=true) {
        CRYPTOLOG("getting all readers with atr: "<<atr);
        Connection c(s, exceptions);
        Strings res;
        pcsc::Connection::Strings readers(c.scan());
        for (pcsc::Connection::Strings::const_iterator it(readers.begin());
             it!=readers.end(); ++it)
          try {
            if (crypto::hex(c.reader(*it)->status().atr).find(atr)
                !=string::npos)
              CRYPTOLOG("found reader: "<<(*it));
              res.push_back(*it);
          } catch (std::exception& x) { // ignore unusable readers
            CRYPTOLOG("ignored unusable reader: "<<x.what());
          }
        return res;
      }

      //! Get a reader, open a connection if not already open.
      /*! First use scan() to get a list of readers, then open a
          connection to the reader, then access it. */
      static std::shared_ptr<Reader> reader(const std::string& name,
                                          Scope s=USER, bool exceptions=true) {
        CRYPTOLOG("get reader: "<<name);
        return std::shared_ptr<Reader>
          (new Reader(name,
                      std::shared_ptr<Connection>
                      (new Connection(s, exceptions))));
      }

      //................................................................methods
    private:
      
      //! Opens a connection (establishes a smartcard context)
      /*! The errorhandling is defined with the @c exceptions flag:

          Using exceptions:
          @code
          try {
            pcsc::Connection c; // standard with exceptions
            pcsc::Connection::Strings r(c.scan());
          } catch (std::exception& x) {
            std::cout<<"Error with message: "<<x.what()<<std::endl;
          }
          @endcode

          Without exceptions:
          @code
          pcsc::Connection c(SYSTEM, false);
          if (!c) std::cout<<"Error with message: "<<c.error()<<std::endl;
          pcsc::Connection::Strings r(c.scan());
          if (!c) std::cout<<"Error with message: "<<c.error()<<std::endl;
          @endcode

          Recommendation: Use exceptions!

          @param s defines the scope of the connection
          @param exceptions
            - @c true:  class throws exceptions in case of an error
            - @c false: no exceptions, check your instance after each
                        operation */
      Connection(Scope s=USER, bool exceptions=true):
          _exc(exceptions), _id(0), _s(s),
          _state(SCardEstablishContext(s, 0, 0, &_id)) {
        CRYPTOLOG("Open Connection id="<<_id);
        check("establish smartcard context");
      }

      //! Splits a buffer with 0 separators into a vector of strings.
      static Strings split(const std::string& in) {
        Strings res;
        for (std::string::size_type pos(0); pos<in.size() && in[pos]!=0;
             pos+=res.rbegin()->size()+1)
          res.push_back(in.substr(pos).c_str());
        return res;
      }
      
      //!  Joins a vector of strings into a  buffer with 0 separators.
      static std::string join(const Strings& in) {
        std::string res;
        if (in.size()) {
          for (Strings::const_iterator it(in.begin());
               it!=in.end(); ++it)
            res+=*it+'\0';
          res+='\0';
        }
        return res;
      }

      operator bool() const {
#ifdef WIN32
        return (_state>>30&3)==0;
#else
        return _state==SCARD_S_SUCCESS;
#endif
      }

      //! Throws an exception if neccessary.
      /*! @throw access_error if it is instanciated for exceptions and
              an error occured in the last command. */
      bool check(DWORD s, const std::string& context="") {
        _state = s;
        return check(context);
      }

      //! Throws an exception if neccessary.
      /*! @throw access_error if it is instanciated for exceptions and
              an error occured in the last command. */
      bool check(const std::string& context="") {
        if (_exc&&!*this) {
          if (context.size()) {
#ifdef SCARD_W_WRONG_CHV
            if (_state==SCARD_W_WRONG_CHV) {
              throw wrong_pin(context+": "+error());
            } else {
#endif
              throw access_error(context+": "+error());
#ifdef SCARD_W_WRONG_CHV
            }
#endif
          } else {
#ifdef SCARD_W_WRONG_CHV
            if (_state==SCARD_W_WRONG_CHV) {
              throw wrong_pin(error());
            } else {
#endif
              throw access_error(error());
#ifdef SCARD_W_WRONG_CHV
            }
#endif
          }
        }
        return *this;
      }
      
      //! Get the describing text of the last error
      std::string error() const {
        std::stringstream ss;
        switch (_state) {
#ifdef SCARD_E_CANCELLED
          case SCARD_E_CANCELLED:
            ss<<"The action was canceled by an SCardCancel request.";
            break;
#endif
#ifdef SCARD_E_CANT_DISPOSE
          case SCARD_E_CANT_DISPOSE:
            ss<<"The system could not dispose of the media in the requested"
              <<" manner.";
            break;
#endif
#ifdef SCARD_E_CARD_UNSUPPORTED
          case SCARD_E_CARD_UNSUPPORTED:
            ss<<"The smart card does not meet minimal requirements for"
              <<" support.";
            break;
#endif
#ifdef SCARD_E_DUPLICATE_READER
          case SCARD_E_DUPLICATE_READER:
            ss<<"The reader driver did not produce a unique reader name.";
            break;
#endif
#ifdef SCARD_E_INSUFFICIENT_BUFFER
          case SCARD_E_INSUFFICIENT_BUFFER:
            ss<<"The data buffer for returned data is too small for the"
              <<" returned data.";
            break;
#endif
#ifdef SCARD_E_INVALID_ATR
          case SCARD_E_INVALID_ATR:
            ss<<"An ATR string obtained from the registry is not a valid"
              <<" ATR string.";
            break;
#endif
#ifdef SCARD_E_INVALID_HANDLE
          case SCARD_E_INVALID_HANDLE:
            ss<<"The supplied handle was not valid.";
            break;
#endif
#ifdef SCARD_E_INVALID_PARAMETER
          case SCARD_E_INVALID_PARAMETER:
            ss<<"One or more of the supplied parameters could not be properly"
              <<" interpreted.";
            break;
#endif
#ifdef SCARD_E_INVALID_TARGET
          case SCARD_E_INVALID_TARGET:
            ss<<"Registry startup information is missing or not valid.";
            break;
#endif
#ifdef SCARD_E_INVALID_VALUE
          case SCARD_E_INVALID_VALUE:
            ss<<"One or more of the supplied parameters values could not"
              <<" be properly interpreted.";
            break;
#endif
#ifdef SCARD_E_NOT_READY
          case SCARD_E_NOT_READY:
            ss<<"The reader or smart card is not ready to accept commands.";
            break;
#endif
#ifdef SCARD_E_NOT_TRANSACTED
          case SCARD_E_NOT_TRANSACTED:
            ss<<"An attempt was made to end a nonexistent transaction.";
            break;
#endif
#ifdef SCARD_E_NO_MEMORY
          case SCARD_E_NO_MEMORY:
            ss<<"Not enough memory available to complete this command.";
            break;
#endif
#ifdef SCARD_E_NO_SERVICE
          case SCARD_E_NO_SERVICE:
            ss<<"The smart card resource manager is not running.";
            break;
#endif
#ifdef SCARD_E_NO_SMARTCARD
          case SCARD_E_NO_SMARTCARD:
            ss<<"The operation requires a smart card, but no smart card"
              <<" is currently in the device.";
            break;
#endif
#ifdef SCARD_E_PCI_TOO_SMALL
          case SCARD_E_PCI_TOO_SMALL:
            ss<<"The PCI receive buffer was too small.";
            break;
#endif
#ifdef SCARD_E_PROTO_MISMATCH
          case SCARD_E_PROTO_MISMATCH:
            ss<<"The requested protocols are incompatible with the protocol"
              <<" currently in use with the smart card.";
            break;
#endif
#ifdef SCARD_E_READER_UNAVAILABLE
          case SCARD_E_READER_UNAVAILABLE:
            ss<<"The specified reader is not currently available for use.";
            break;
#endif
#ifdef SCARD_E_READER_UNSUPPORTED
          case SCARD_E_READER_UNSUPPORTED:
            ss<<"The reader driver does not meet minimal requirements for"
              <<" support.";
            break;
#endif
#ifdef SCARD_E_SERVICE_STOPPED
          case SCARD_E_SERVICE_STOPPED:
            ss<<"The smart card resource manager has shut down.";
            break;
#endif
#ifdef SCARD_E_SHARING_VIOLATION
          case SCARD_E_SHARING_VIOLATION:
            ss<<"The smart card cannot be accessed because of other"
              <<" outstanding connections.";
            break;
#endif
#ifdef SCARD_E_SYSTEM_CANCELLED
          case SCARD_E_SYSTEM_CANCELLED:
            ss<<"The action was cancelled by the system, presumably to log"
              <<" off or shut down.";
            break;
#endif
#ifdef SCARD_E_TIMEOUT
          case SCARD_E_TIMEOUT:
            ss<<"The user-specified time-out value has expired.";
            break;
#endif
#ifdef SCARD_E_UNKNOWN_CARD
          case SCARD_E_UNKNOWN_CARD:
            ss<<"The specified smart card name is not recognized.";
            break;
#endif
#ifdef SCARD_E_UNKNOWN_READER
          case SCARD_E_UNKNOWN_READER:
            ss<<"The specified reader name is not recognized.";
            break;
#endif
#ifdef SCARD_F_COMM_ERROR
          case SCARD_F_COMM_ERROR:
            ss<<"An internal communications error has been detected.";
            break;
#endif
#ifdef SCARD_F_INTERNAL_ERROR
          case SCARD_F_INTERNAL_ERROR:
            ss<<"An internal consistency check failed.";
            break;
#endif
#ifdef SCARD_F_UNKNOWN_ERROR
          case SCARD_F_UNKNOWN_ERROR:
            ss<<"An internal error has been detected, but the source is"
              <<" unknown.";
            break;
#endif
#ifdef SCARD_F_WAITED_TOO_LONG
          case SCARD_F_WAITED_TOO_LONG:
            ss<<"An internal consistency timer has expired.";
            break;
#endif
#ifdef SCARD_S_SUCCESS
          case SCARD_S_SUCCESS:
            ss<<"No error was encountered.";
            break;
#endif
#ifdef SCARD_W_REMOVED_CARD
          case SCARD_W_REMOVED_CARD:
            ss<<"The smart card has been removed, so that further"
              <<" communication is not possible.";
            break;
#endif
#ifdef SCARD_W_RESET_CARD
          case SCARD_W_RESET_CARD:
            ss<<"The smart card was reset.";
            break;
#endif
#ifdef SCARD_W_UNPOWERED_CARD
          case SCARD_W_UNPOWERED_CARD:
            ss<<"Power has been removed from the smart card, so that"
              <<" further communication is not possible.";
            break;
#endif
#ifdef SCARD_W_UNRESPONSIVE_CARD
          case SCARD_W_UNRESPONSIVE_CARD:
            ss<<"The smart card is not responding to a reset.";
            break;
#endif
#ifdef SCARD_W_UNSUPPORTED_CARD
          case SCARD_W_UNSUPPORTED_CARD:
            ss<<"The reader cannot communicate with the smart card,"
              <<" due to ATR configuration conflicts.";
            break;
#endif
#ifdef SCARD_E_NO_READERS_AVAILABLE
          case SCARD_E_NO_READERS_AVAILABLE:
            ss<<"No smart card reader is available.";
            break;
#endif
#ifdef ERROR_BROKEN_PIPE
          case ERROR_BROKEN_PIPE:
            ss<<"The client attempted a smart card operation in a"
              <<" remote session, such as a client session running"
              <<" on a terminal server, and the operating system in"
              <<" use does not support smart card redirection.";
            break;
#endif
#ifdef SCARD_E_BAD_SEEK
          case SCARD_E_BAD_SEEK:
            ss<<"There was an error trying to set the smart card file"
              <<" object pointer.";
            break;
#endif
#ifdef SCARD_E_CERTIFICATE_UNAVAILABLE
          case SCARD_E_CERTIFICATE_UNAVAILABLE:
            ss<<"The requested certificate could not be obtained.";
            break;
#endif
#ifdef SCARD_E_COMM_DATA_LOST
          case SCARD_E_COMM_DATA_LOST:
            ss<<"A communications error with the smart card has been detected.";
            break;
#endif
#ifdef SCARD_E_DIR_NOT_FOUND
          case SCARD_E_DIR_NOT_FOUND:
            ss<<"The specified directory does not exist in the smart card.";
            break;
#endif
#ifdef SCARD_E_FILE_NOT_FOUND
          case SCARD_E_FILE_NOT_FOUND:
            ss<<"The specified file does not exist in the smart card.";
            break;
#endif
#ifdef SCARD_E_ICC_CREATEORDER
          case SCARD_E_ICC_CREATEORDER:
            ss<<"The requested order of object creation is not supported.";
            break;
#endif
#ifdef SCARD_E_ICC_INSTALLATION
          case SCARD_E_ICC_INSTALLATION:
            ss<<"No primary provider can be found for the smart card.";
            break;
#endif
#ifdef SCARD_E_INVALID_CHV
          case SCARD_E_INVALID_CHV:
            ss<<"The supplied PIN is incorrect.";
            break;
#endif
#ifdef SCARD_E_NO_ACCESS
          case SCARD_E_NO_ACCESS:
            ss<<"Access is denied to this file.";
            break;
#endif
#ifdef SCARD_E_NO_DIR
          case SCARD_E_NO_DIR:
            ss<<"The supplied path does not represent a smart card directory.";
            break;
#endif
#ifdef SCARD_E_NO_FILE
          case SCARD_E_NO_FILE:
            ss<<"The supplied path does not represent a smart card file.";
            break;
#endif
#ifdef SCARD_E_NO_KEY_CONTAINER
          case SCARD_E_NO_KEY_CONTAINER:
            ss<<"The requested key container does not exist on the smart card.";
            break;
#endif
#ifdef SCARD_E_NO_SUCH_CERTIFICATE
          case SCARD_E_NO_SUCH_CERTIFICATE:
            ss<<"The requested certificate does not exist.";
            break;
#endif
#ifdef SCARD_E_SERVER_TOO_BUSY
          case SCARD_E_SERVER_TOO_BUSY:
            ss<<"The Smart card resource manager is too busy to complete this"
              <<" operation.";
            break;
#endif
#ifdef SCARD_E_UNSUPPORTED_FEATURE
          case SCARD_E_UNSUPPORTED_FEATURE:
            ss<<"This smart card does not support the requested feature.";
            break;
#else
#ifdef SCARD_E_UNEXPECTED
          case SCARD_E_UNEXPECTED:
            ss<<"An unexpected card error has occurred.";
            break;
#endif
#endif
#ifdef SCARD_E_UNKNOWN_RES_MNG
          case SCARD_E_UNKNOWN_RES_MNG:
            ss<<"An unrecognized error code was returned from a layered"
              <<" component.";
            break;
#endif
#ifdef SCARD_E_WRITE_TOO_MANY
          case SCARD_E_WRITE_TOO_MANY:
            ss<<"The smartcard does not have enough memory to store the"
              <<" information.";
            break;
#endif
#ifdef SCARD_P_SHUTDOWN
          case SCARD_P_SHUTDOWN:
            ss<<"The operation has been aborted to allow the server application"
              <<" to exit.";
            break;
#endif
#ifdef SCARD_W_CANCELLED_BY_USER
          case SCARD_W_CANCELLED_BY_USER:
            ss<<"The action was cancelled by the user.";
            break;
#endif
#ifdef SCARD_W_CARD_NOT_AUTHENTICATED
          case SCARD_W_CARD_NOT_AUTHENTICATED:
            ss<<"No PIN was presented to the smart card.";
            break;
#endif
#ifdef SCARD_W_CHV_BLOCKED
          case SCARD_W_CHV_BLOCKED:
            ss<<"The card cannot be accessed because the maximum number"
              <<" of PIN entry attempts has been reached.";
            break;
#endif
#ifdef SCARD_W_EOF
          case SCARD_W_EOF:
            ss<<"The end of the smart card file has been reached.";
            break;
#endif
#ifdef SCARD_W_SECURITY_VIOLATION
          case SCARD_W_SECURITY_VIOLATION:
            ss<<"Access was denied because of a security violation.";
            break;
#endif
#ifdef SCARD_W_WRONG_CHV
          case SCARD_W_WRONG_CHV:
            ss<<"The card cannot be accessed because the wrong PIN was"
              <<" presented.";
            break;
#endif
          default:
            ss<<"unknown PCSC state=0x"
              <<std::hex<<std::setfill('0')<<std::setw(8)<<_state;
            switch (_state>>30) {
              case 0: ss<<" means SUCCESS"; break;
              case 1: ss<<" means INFORMATIONAL"; break;
              case 2: ss<<" means WARNING"; break;
              case 3: ss<<" means ERROR"; break;
              default: ss<<" illegal value";
            }
            ss<<" C="<<(_state>>29&1);
            ss<<" R="<<(_state>>28&1);
            ss<<" Facility=0x"<<std::hex<<std::setfill('0')<<std::setw(3)
              <<(_state>>16&0xfff);
            ss<<" Code=0x"<<std::hex<<std::setfill('0')<<std::setw(4)
              <<(_state&0xffff);
        }
        return ss.str();
      }
      //! set state
      void state(DWORD s) {
        _state = s;
      }
      //! @returns state
      DWORD state() {
        return _state;
      }
      //! @returns connection id
      SCARDCONTEXT id() {
        return _id;
      }
      bool exc() {
        return _exc;
      }

    private:

      bool _exc;
      SCARDCONTEXT _id;
      Scope _s;
      DWORD _state;

  };

  //@}
  
}
//@}

#endif
