#pragma once

#include <string>
//#include "SlotList.h"
#include "Slot.h"
#include "Pkcs11Exceptions.h"
#include "Mech.h"

class CMech;

#define MF_NAME			"Master.File"
#define ICC_CRYPTO_NAME "ICC.Crypto"

#define SC_FL_PUBLIC_KEY	0x00000001
#define SC_FL_PRIVATE_KEY	0x00000002
#define SC_FL_SIGNATURE_KEY	0x00000004
#define SC_FL_EXCHANGE_KEY	0x00000008

using namespace std;


/********************************************************************************************
	Estructura con los bytes de estado
********************************************************************************************/
typedef struct STATUS_BYTES 
{
    BYTE sw1; // Byte de estado 1
    BYTE sw2; // Byte de estado 2
} STATUS_BYTES;


/********************************************************************************************
	Clase que representa un elemento de comunicación.
********************************************************************************************/
class CCommunicator
{
	public:

		// Constructor
		CCommunicator( CSlot & slot );

		// Destructor
		virtual ~CCommunicator(void);

		// Método que envía un APDU Select File indicando el fichero por ID, y nos devolverá el tamaño 
		unsigned short SelectFileGetSize( ushort fileId );

		// Método para devolver el tamaño del fichero indicado en el path
		int GetEFSize( byteBuffer & path );

		// Método que nos indica el path y el tamaño del fichero RSA que contiene determinada clave
		virtual unsigned short SelectRSAFileGetSize( unsigned long ulUsoClave, byte keyID, byteBuffer & path );

		// Método que envía APDUs Select File para seleccionar un fichero elemental a partir del Path
		void SelectFileByPath( byteBuffer & path );

		// Método que envía un APDU Select File indicando el fichero por ID
		void SelectFileById( ushort fileId );
	
		// Método que envía un APDU Select File indicando el fichero por nombre
		void SelectFileByName( string sName );

		// Métodos para leer el contenido de ficheros
		void ReadBinary( ushort ushOffset, ulong ulLength, byteBuffer & data ); 
		void ReadBinary( ushort ushOffset, ulong * pulLength, byte * pData );

		//
		// Método para verificar el PIN
		//
		virtual void VerifyPIN( CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen );

		//
		// Método para modificar el PIN
		//
		virtual void ChangePIN( CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen,
								CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen,
								byte bCodeId = 1);

		//
		// Método interno para desbloquear con el PUK
		//
		virtual void UnblockPIN( CK_UTF8CHAR_PTR pUnblock, CK_ULONG ulUnbLen,
								 CK_UTF8CHAR_PTR pNewPIN, CK_ULONG ulPinLen);

		//
		// Método para obtener el contador de intentos de PIN restantes
		//
		virtual ushort GetPINCounter();

		//
		// Método para leer una estructura sequence de un fichero
		//
		void readEF_sequence( unsigned short id, byteBuffer & ef, unsigned short startOffset = 0 );

		void readEF_CompressedData( byteBuffer & path, byteBuffer & data );

		// Métodos para escribir el contenido de ficheros
		void WriteBinary( ushort ushOffset, byteBuffer & data ); 
		void WriteBinary( ushort ushOffset, byte * pData, ulong ulDataLen );

		void writeEF_sequence( unsigned short id, byteBuffer & ef, unsigned short startOffset = 0);

		// Método para la creación de nuevos ficheros elementales
		void CreateFileEF( bool privado, byteBuffer & path, unsigned int size );

		// Métodos para comprimir datos
		void CompressData( byteBuffer & data );
		void CompressData( byteBuffer & dataIn, byteBuffer & dataOut );

		//
		// Método para leer compomentes públicos de claves RSA
		//
		void ReadRSAPublicComponents( byte keyReference, byteBuffer & publicExponent, byteBuffer & modulus );

		// Método para realizar una firma RSA
		virtual void SignMech ( CMech * pMechanism, byteBuffer & data, byteBuffer & signature );

		// Métodos para realizar una firma RSA por partes
		virtual void SignMechUpdate ( CMech * pMechanism, byteBuffer & data);
		virtual void SignMechFinal ( CMech * pMechanism, byteBuffer & signature);

		// Método para realizar una verificación RSA
		void VerifySignatureMech ( CMech * pMechanism, byteBuffer & data, byteBuffer & signature );

		// Métodos para realizar una verificación RSA por partes
		void VerifySignatureMechUpdate ( CMech * pMechanism, byteBuffer & data );
		void VerifySignatureMechFinal ( CMech * pMechanism, byteBuffer & signature );

		// Método para realizar un descifrado RSA (tajetas CERES)
		void DecryptMech ( CMech * pMechanism, byteBuffer & encryptedData, byteBuffer & decryptedData );
		void Decrypt ( byte keyReference, byteBuffer & encryptedData, byteBuffer & decryptedData );

		CK_RV GetChallenge( CK_BYTE_PTR	pRandomData,
							CK_ULONG	ulRandomLen );

		virtual CK_RV StoreRSAComponents( CK_ULONG	usoClave,
											 CK_BYTE	etiqueta,
											 byteBuffer	&componente,
											 CK_BYTE	keyID);

		virtual CK_RV GenerateKey ( CK_ULONG	usoClave, 
									CK_ULONG	keyLen, 
									byteBuffer	&publicExponent, 
									CK_BYTE		keyID );

		virtual CK_RV DeleteKey (	CK_ULONG	usoClave,
									CK_BYTE		keyID );

		CSlot & GetSlot(){return m_slot;};

		virtual byte GetMinKeyId (void)
		{
			return 0x01;
		}

		virtual byte GetMaxKeyId (void)
		{
			return 0x20;
		}

		virtual int GetDniType()
		{
			return 0x00;
		};

protected:
		//
		// Funciones para el envío directo de APDUs
		// La primera versión de sendAPDU es virtual pura, y debe ser implementada en 
		// las clases hijas.
		// Las otra forma de sendAPDU acaban llamando a la primera.
		//
		virtual void sendAPDU( byte * pCmd, ulong cmdLen, byteBuffer & answer, unsigned short expected_SW = 0x9000 ) = 0;

		void sendAPDU( byteBuffer & cmd, byteBuffer & answer, unsigned short expected_SW = 0x9000 );

		virtual void sendAPDUExt( byteBuffer & cmdHeader, byteBuffer & cmdData, byteBuffer & answer,unsigned short expected_SW = 0x9000 );

		void sendPlainAPDUExt( byteBuffer & cmdExtendido, byteBuffer & answer,unsigned short expected_SW = 0x9000 );
		
		void sendPlainAPDU( byteBuffer & cmd, byteBuffer & answer, unsigned short expected_SW = 0x9000 );
		void sendPlainAPDU( byte * pCmd, ulong cmdLen, byteBuffer & answer, unsigned short expected_SW = 0x9000 );
		void getResponse( byteBuffer & response,unsigned short expected_SW = 0x9000 );


		unsigned short	m_SW;

		//
		// Referencia al lector asociado
		//
		CSlot	& m_slot;

		//
		// Respuesta del último comando SELECT FILE
		//
		byteBuffer	m_lastFCI;
};

class CComm_Plain : public CCommunicator
{
public:
	CComm_Plain( CSlot & slot ) : CCommunicator(slot) {};

	void SignMech ( CMech * pMechanism, byteBuffer & data, byteBuffer & signature );

	void SignMechFinal ( CMech * pMechanism, byteBuffer & signature );

protected:
	virtual void sendAPDU( byte * pCmd, ulong cmdLen, byteBuffer & answer, unsigned short expected_SW = 0x9000 )
	{
		sendPlainAPDU( pCmd, cmdLen, answer, expected_SW );
	}
};


class CComm_Ceres1_0 : public CComm_Plain
{
public:
	CComm_Ceres1_0( CSlot & slot ) : CComm_Plain(slot) {};

	CK_RV DeleteKey (	CK_ULONG	usoClave,
						CK_BYTE		keyID );

	CK_RV StoreRSAComponents( CK_ULONG	usoClave,
							  CK_BYTE	etiqueta,
							  byteBuffer	&componente,
							  CK_BYTE	keyID);
	
	CK_RV GenerateKey (	CK_ULONG	usoClave,
						CK_ULONG	keyLen,
						byteBuffer	&publicExponent,
						CK_BYTE		keyID );

	byte GetMinKeyId (void)
	{
		return 0x20;
	}

	byte GetMaxKeyId (void)
	{
		return 0x30;
	}

	unsigned short SelectRSAFileGetSize( unsigned long ulUsoClave, byte keyID, byteBuffer & path );

protected:

};