/*
 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// pcsc++ - PCSC client interface layer in C++
//
// NOTE: TO BE MOVED TO security_utilities LAYER.
//
#ifndef _H_PCSC_PP
#define _H_PCSC_PP

#include <security_utilities/utilities.h>
#include <security_utilities/errors.h>
#include <security_utilities/transactions.h>
#include <security_utilities/debugging.h>
#include <PCSC/winscard.h>
#include <vector>
#include <string>

#include <cstdio>


namespace Security {
namespace PCSC {


//
// PCSC-domain error exceptions
//
class Error : public CommonError {
public:
	Error(unsigned long err);

    const unsigned long error;
	OSStatus osStatus() const;
	int unixError() const;
	const char *what () const throw ();
	
	static void check(unsigned long err) { if (err != SCARD_S_SUCCESS) throwMe(err); }
	static void throwMe(unsigned long err);

private:
	IFDEBUG(void debugDiagnose(const void *id) const);
};


//
// A PODWrapper for the PCSC READERSTATE structure
//
class ReaderState : public PodWrapper<ReaderState, SCARD_READERSTATE> {
public:
	void set(const char *name, unsigned long known = SCARD_STATE_UNAWARE);
	
	const char *name() const	{ return szReader; }
	void name(const char *s)	{ szReader = s; }

	unsigned long lastKnown() const { return dwCurrentState; }
	void lastKnown(unsigned long s);

	unsigned long state() const { return dwEventState; }
	bool state(unsigned long it) const { return state() & it; }
	bool changed() const		{ return state(SCARD_STATE_CHANGED); }
	
	template <class T>
	T * &userData() { return reinterpret_cast<T * &>(pvUserData); }
	
	// DataOid access to the ATR data
	const void *data() const { return rgbAtr; }
	size_t length() const { return cbAtr; }
	void setATR(const void *atr, size_t size);
	
	IFDUMP(void dump());
};


//
// A Session represents the entire process state for the PCSC protocol
//
class Session {
	friend class Card;
public:
	Session();
	virtual ~Session();

	void open();
	bool isOpen() const { return mIsOpen; }
	
	void listReaders(vector<string> &readers, const char *groups = NULL);
	
	void statusChange(ReaderState *readers, unsigned int nReaders, long timeout = 0);
	void statusChange(ReaderState &reader, long timeout = 0)
	{ return statusChange(&reader, 1, timeout); }
	void statusChange(vector<ReaderState> &readers, long timeout = 0)
	{ return statusChange(&readers[0], readers.size(), timeout); }
	

private:
	bool check(long rc);
	
private:
	bool mIsOpen;
	SCARDCONTEXT mContext;
	std::vector<char> mReaderBuffer;
};


//
// A Card represents a PCSC-managed card slot
//
class Card {
public:
	static const unsigned long defaultProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
	
	Card();
	virtual ~Card();

	void connect(Session &session, const char *reader,
		unsigned long share = SCARD_SHARE_SHARED,
		unsigned long protocols = defaultProtocols);
	void reconnect(unsigned long share = SCARD_SHARE_SHARED,
		unsigned long protocols = defaultProtocols,
		unsigned long initialization = SCARD_LEAVE_CARD);
	void disconnect(unsigned long disposition = SCARD_LEAVE_CARD);
	virtual void didDisconnect();
	virtual void didEnd();

	void checkReset(unsigned int rv);
	bool isConnected() const { return mConnectedState == kConnected; }
	bool isInTransaction() const { return mTransactionNestLevel > 0; }

	void transmit(const unsigned char *pbSendBuffer, size_t cbSendLength,
		unsigned char *pbRecvBuffer, size_t &pcbRecvLength);

	// primitive transaction interface
	void begin();
	void end(unsigned long disposition = SCARD_LEAVE_CARD);
	void cancel();

protected:
	void setIOType(unsigned long activeProtocol);

	IFDUMP(void dump(const char *direction, const unsigned char *buffer, size_t length);)
	
private:
	enum
	{
		kInitial,
		kConnected,
		kDisconnected
	} mConnectedState;
		
	int32_t mHandle;
	int mTransactionNestLevel;
	SCARD_IO_REQUEST *mIOType;
};


//
// A PCSC-layer transaction (exclusive sequence of calls)
//
class Transaction : public ManagedTransaction<Card> {
public:
	Transaction(Card &card, Outcome outcome = conditional)
		: ManagedTransaction<Card>(card, outcome), mDisposition(SCARD_LEAVE_CARD) { }
	
	void disposition(unsigned long disp);	// change disposition on successful outcome

protected:
	void commitAction();

private:
	unsigned long mDisposition;				// disposition on success
};


}   // namespce PCSC
}   // namespace Security


#endif //_H_PCSC_PP
