#include "SessionList.h"
#include "Pkcs11Exceptions.h"

extern SCARDCONTEXT g_hContext;


/********************************************************************************************
	Constructor.
	Inicializa el contador con el próximo ID a asignar
********************************************************************************************/
CSessionList::CSessionList(void)
{
	m_ulNextId = 0x110000;
}


/********************************************************************************************
	Destructor.
********************************************************************************************/
CSessionList::~CSessionList(void)
{
	//
	// Al ser un singleton, la llamada el destructor indica que
	// se está descargando el módulo.
	// Liberamos todos los objetos utilizados
	//
	map<CK_SLOT_ID, CSession *>::iterator it;

	for ( it=m_mapSlot2Session.begin() ; it != m_mapSlot2Session.end(); it++ )
		delete it->second;

	m_mapSlot2Session.clear();
	m_mapHandle2Slot.clear();
}


/********************************************************************************************
	Método que obtiene un nuevo ID de lector.
	Mantiene actualizado el contador de IDs.
********************************************************************************************/
CK_SESSION_HANDLE CSessionList::GetNewSessionHandle(CK_SLOT_ID slotID)
{
	m_mapHandle2Slot.insert(pair<CK_SESSION_HANDLE, CK_SLOT_ID>(m_ulNextId, slotID));

	return m_ulNextId++;
}


/********************************************************************************************
	Método que actualiza la lista de sesiones abiertas
********************************************************************************************/
void CSessionList::Add(CK_SLOT_ID slotID, CSession *pSession)
{
	DBG_TRACE(("Añadido slot 0x%08x\n", slotID));
	m_mapSlot2Session.insert(pair<CK_SLOT_ID, CSession *>(slotID, pSession));
}

//
// Método para eliminar un handle de sesión.
//
void CSessionList::DeleteHandle(CK_SESSION_HANDLE hSession)
{
	map<CK_SESSION_HANDLE, CK_SLOT_ID>::iterator itHandle2Slot = m_mapHandle2Slot.find(hSession);

	if( itHandle2Slot == m_mapHandle2Slot.end() )
		throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );

	m_mapHandle2Slot.erase(itHandle2Slot);
}

//
// Método para eliminar una sesión más todos sus handles asociados.
//
void CSessionList::DeleteSessionAndHandles(CK_SESSION_HANDLE hSession)
{
	map<CK_SESSION_HANDLE, CK_SLOT_ID>::iterator itHandle2Slot = m_mapHandle2Slot.find(hSession);

	if( itHandle2Slot == m_mapHandle2Slot.end() )
		throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );

	CK_SLOT_ID	slotId = itHandle2Slot->second;

	map<CK_SLOT_ID, CSession *>::iterator itSlot2Session = m_mapSlot2Session.find(slotId);

	if( itSlot2Session == m_mapSlot2Session.end() )
		throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );

	// Borramos el objeto Session
	delete itSlot2Session->second;

	// Eliminamos su referencia de la tabla
	m_mapSlot2Session.erase(itSlot2Session);

}


//
// Método para eliminar todas las sesiones y todos sus handles
//
void CSessionList::DeleteAllSessionsAndHandles( CK_SLOT_ID slotId )
{
	map<CK_SLOT_ID, CSession *>::iterator itSlot2Session = m_mapSlot2Session.find(slotId);

	if( itSlot2Session != m_mapSlot2Session.end() )
	{
		// Borramos el objeto Session
		delete itSlot2Session->second;
		m_mapSlot2Session.erase(itSlot2Session);
	}

}


unsigned long CSessionList::GetNumberOfSessions()
{
	unsigned long ulNumberOfSessions = 0;

	ulNumberOfSessions = (unsigned long)m_mapSlot2Session.size();

	return ulNumberOfSessions;
}

CSession * CSessionList::FindSession(CK_SESSION_HANDLE hSession)
{
	map <CK_SESSION_HANDLE, CK_SLOT_ID>::iterator itSessionList;

	itSessionList = m_mapHandle2Slot.find(hSession);

	// Si no encuentra la sesión devolvemos un objeto nulo
	if (itSessionList == m_mapHandle2Slot.end())
		throw Pkcs11Exception(CKR_SESSION_HANDLE_INVALID);

	return FindSessionInSlot(itSessionList->second);
}

CSession * CSessionList::FindSessionInSlot(CK_SLOT_ID slotID)
{
	map<CK_SLOT_ID, CSession *>::iterator itSessionList;

	itSessionList = m_mapSlot2Session.find(slotID);

	// Si no encuentra el lector lanzamos una excepción
	if (itSessionList == m_mapSlot2Session.end())
		throw Pkcs11Exception(CKR_SLOT_ID_INVALID);

	return itSessionList->second;
}

bool CSessionList::ExistSessionInSlot(CK_SLOT_ID slotID)
{
	map<unsigned long, CSession *>::iterator itSessionList;

	itSessionList = m_mapSlot2Session.find(slotID);

	// Si no encuentra la sesión devolvemos un objeto nulo
	if (itSessionList == m_mapSlot2Session.end())
		return false;

	return true;
}

void CSessionList::ResetSessionsInSlot(CK_SESSION_HANDLE hSession)
{
	// Buscamos las sesiones abiertas en ese Slot y las dejamos a SOLO LECTURA
	map<CK_SESSION_HANDLE, CK_SLOT_ID>::iterator itHandle2Slot = m_mapHandle2Slot.find(hSession);

	if( itHandle2Slot == m_mapHandle2Slot.end() )
		throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );

	// Obtenemos la lista de sesiones del slot
	CK_SLOT_ID	slotId = itHandle2Slot->second;

	map<CK_SLOT_ID, CSession *>::iterator itSession = m_mapSlot2Session.find(slotId);
	if( itSession == m_mapSlot2Session.end() )
		throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );//*/

	int idx = itSession->second->GetNumberOfHandles();
	
	for(itSession = m_mapSlot2Session.begin();  itSession != m_mapSlot2Session.end(); itSession++)
		itSession->second->Logout();

	/*map<CK_SESSION_HANDLE, CK_FLAGS>::iterator itSessions;

	// Eliminamos de la tabla todos los handles asociados a este Slot
	for(itSessions = itSession->second->begin();  itSessions != m_maphSession.end(); it++)
	{
		
	}*/

	/*// 
	for ( itSession=m_mapSlot2Session.begin() ; itSession != m_mapSlot2Session.end(); itSession++ )
		itSession->second->Logout();*/
}
