#include "P15File.h"
#include "Slot.h"
#include "Pkcs11Exceptions.h"
#include "Communicator.h"

CP15File::CP15File(CSlot & slot)
{
	m_loaded			= false;
	m_maxDataLen		= 0;
	m_asn1Type			= ASN1TYPE_ISO;
	m_pathEncodingType	= PATHENC_BIN;

	try
	{
		m_asn1Type = slot.getTokenType().GetASN1Type();
		m_pathEncodingType = slot.getTokenType().GetPathEncodingType();
	}
	catch(Pkcs11Exception)
	{
		// Si no hay slot seguimos con m_asn1Type ASN1TYPE_ISO
	}
}

CP15File::CP15File(void)
{
	m_loaded			= false;
	m_maxDataLen		= 0;
	m_asn1Type			= ASN1TYPE_ISO;
	m_pathEncodingType	= PATHENC_BIN;
}

CP15File::~CP15File(void)
{
	FreeObjects();
}


ushort CP15File::GetFileId() const
{
	return m_ushFileId;
}

int CP15File::GetASN1Type(void)
{
	return m_asn1Type;
}

int CP15File::GetPathEncodingType(void)
{
	return m_pathEncodingType;
}

void CP15File::SetASN1Type(int iASN1Type)
{
	m_asn1Type = iASN1Type;
}

void CP15File::SetPathEncodingType(int iPathEncodingType)
{
	m_pathEncodingType = iPathEncodingType;
}

void CP15File::FreeObjects(void)
{
	for(ulong idx=0; idx< m_P11Objects.size(); idx++)
	{
		delete (m_P11Objects[idx]);
	}

	m_P11Objects.clear();
}

void CP15File::AddObjects2List(CObjList & objList)
{
	for(ulong idx=0; idx< m_P11Objects.size(); idx++)
	{
		objList.Insert(m_P11Objects[idx]);
	}
}

void CP15File::DeleteObjectFromList(CPKCS11Object *pObject)
{
	vector<CPKCS11Object *>::iterator iter;

	ulong idx = 0;

	bool bEncontrado = false;

	while( (!bEncontrado) && (idx < m_P11Objects.size()) )
	{
		if(m_P11Objects[idx] != pObject)
		{
			++idx;
			continue;
		}

		iter = m_P11Objects.begin();

		// Eliminamos la entrada del vector
		for (iter = m_P11Objects.begin(); iter != m_P11Objects.end()-(m_P11Objects.size()-idx); iter++);

		m_P11Objects.erase(iter);

		return;
	}

	throw Pkcs11Exception( CKR_OBJECT_HANDLE_INVALID );
}


void CP15File::LoadP15File(CCommunicator & comm)
{
	byteBuffer		bbData;
	ushort			offset = 0;

	//
	// TODO::: Falta limpiar estado antiguo
	//

	// Accedemos al DF, cuya ruta es MF->5015->FileId
	comm.SelectFileByName( MF_NAME );

	comm.SelectFileById( 0x5015 );

	try
	{
		// Utilizamos el SFI
		offset = ( (GetFileId() & 0x001F) | 0x0080 ) << 8;

		while(1)
		{
			comm.readEF_sequence( 0, bbData, offset );

			// Ya hemos leído una entrada del DF, así que lo decodificamos.
			// Además, excepto para el caso del TokenType, se parseará a una estructura P11.
			if( !DecodeObject( bbData ) )
				throw Pkcs11Exception( CKR_DEVICE_ERROR );

			if( offset & 0x8000 )			// Quitamos el SFI
				offset &= 0x00ff;

			offset += (ushort)bbData.size();

			// Guardamos el tamaño total leído para controlar después que no quede basura en el DF
			m_maxDataLen = offset;

		}
	}
	catch( Pkcs11Exception & e )
	{
		// Excluímos CKR_DATA_INVALID porque la última lectura siempre devolverá ese error
		if( e.getErrId() != CKR_DATA_INVALID )
		{
			comm.GetSlot().SetP15Error();
			throw e;
		}
	}

	m_loaded = true;
}


// Método para realizar la codificación ASN1
int CP15File::EncodeASN1( CPKCS11Object *pObj11, byteBuffer & efEncoded )
{
	return 0;
}

void CP15File::WriteP15File(CCommunicator * comm)
{
	byteBuffer		bbData;
	ushort			offset		= 0;
	unsigned int	idx			= 0;
	int				itamDF		= 0;

	//
	// TODO::: Falta limpiar estado antiguo
	//

	// Accedemos al DF, cuya ruta es MF->5015->FileId
	comm->SelectFileByName( MF_NAME );

	comm->SelectFileById( 0x5015 );

	try
	{
		// Utilizamos el SFI
		offset = ( (GetFileId() & 0x001F) | 0x0080 ) << 8;

		while( idx<m_P11Objects.size() )
		{
			if(bbData.size())
				bbData.zeroClear();

			// Codificamos la entrada ASN1 del objeto PKCS#11
			if( EncodeASN1(m_P11Objects[idx], bbData) != RC_OK)
				throw Pkcs11Exception( CKR_DEVICE_ERROR );

			// Escribimos esa nueva entrada en el FE correspondiente
			comm->writeEF_sequence( 0, bbData, offset );

			if( offset & 0x8000 )			// Quitamos el SFI
				offset &= 0x00ff;

			offset += (ushort)bbData.size();

			itamDF = offset;

			idx++;
		}

		// Limpiamos el resto del fichero hasta el tamaño máximo anterior.
		if(itamDF<m_maxDataLen)
		{
			bbData.clear();
			bbData.resize(m_maxDataLen-itamDF);
			comm->WriteBinary( offset, bbData );
		}
		else
			// El tamaño máximo se ha incrementado tras esta escritura, así que lo almacenaremos
			m_maxDataLen = itamDF;

	}
	catch( Pkcs11Exception & e )
	{
		if( e.getErrId() != CKR_DATA_INVALID )
			throw e;
	}

	m_loaded = true;

}
