#include "TokenTypeList.h"
#include "Pkcs11Exceptions.h"

#include "ASN1/TokenTypesConfig.h"

#if defined WIN32
    // near the top of your CPP file
    // El símbolo __ImageBase está definido en windows al comienzo de la DLL.
    // Lo utilizamos con ::GetModuleFileName para obtener el nombre completo de la DLL
    EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#endif

#if defined _CFG_FNMT_DNI_30_PRUEBAS_
	#include "cfg_FNMT_Pruebas_3_0.h"
#elif defined _CFG_DNI_
	#include "cfg_DNIe.h"
	//#include "cfg_DNIePruebas.h"
#elif defined _CFG_DNI_PRUEBAS_
	#include "cfg_DNIePruebas.h"
#elif defined _CFG_FNMT_CERES_
	#include "cfg_FNMT_Ceres.h"
// Esta versión 4.30 tiene valores incorrectos en su RCS (23-05-2013). Se deja
// el código de cara al futuro, cuando se genere una RCA de real.
#elif defined _CFG_FNMT_CERES_430_ 
	#include "cfg_FNMT_Ceres-430.h"
#elif defined _CFG_TIF_REAL_
	#include "cfg_TIF_real.h"
#elif defined _CFG_FNMT_CERES_430_REAL_
	#include "cfg_FNMT_Ceres-430_REAL.h"
#else
	//#include "cfg_FNMT_Pruebas.h"
	#include "cfg_FNMT_Real.h"
#endif

// Constructor
CTokenTypeList::CTokenTypeList()
{
	// Inicializamos el tipo de objetos que deberá manejar el driver
	m_PublicOnly	= false;
	m_PrivateOnly	= false;

	// Cargamos las configuraciones de Token Types hardcoded.
	LoadTokenTypeconfig(tokenTypesCfg,sizeof(tokenTypesCfg));

    #if defined WIN32
        // Obtenemos la ruta COMPLETA de la DLL y buscamos en ella el fichero de configuración
        LPTSTR  strCfgFileName = new TCHAR[_MAX_PATH];
        ::GetModuleFileName((HINSTANCE)&__ImageBase, strCfgFileName, _MAX_PATH);

        DBG_TRACE(( "[PRIV/PUB] Comprobamos driver %s\n", strCfgFileName ));

        memcpy(&strCfgFileName[strlen(strCfgFileName)-3], "cfg", 3);

        if(strlen(strCfgFileName) > 8 ) // "PUB.dll"
        {
            if(!_memicmp(&strCfgFileName[strlen(strCfgFileName)-7],"pub",3))
                m_PublicOnly = true;

            if(!_memicmp(&strCfgFileName[strlen(strCfgFileName)-8],"priv",4))
                m_PrivateOnly = true;
        }

        // Abrimos el fichero en la ruta adecuada
        FILE	*fd;
        fd = fopen(strCfgFileName, "rb");

        //
        // Ya no necesitamos el nombre del fichero cfg.
        //
        delete strCfgFileName;

        if( !fd )
            return;

        // Obtenemos el tamaño del fichero y reservamos memoria
        fseek(fd, 0, SEEK_END);
        byteBuffer bbTTConfigData(ftell(fd));
        fseek(fd, 0, SEEK_SET);

        // Leemos el contenido
        if(fread(&bbTTConfigData[0], 1, bbTTConfigData.size(), fd) != bbTTConfigData.size())
        {
            fclose(fd);
            return;
        }

        fclose(fd);

        // Cargamos las nuevas configuraciones de los TokenTypes
        LoadTokenTypeconfig(&bbTTConfigData[0],bbTTConfigData.size());
    #endif          // WIN32
}


void CTokenTypeList::LoadTokenTypeconfig( byte * pTokenTypeConfig, ulong ulTTConfigSize )
{
	TokenTypesConfig	* pTTCfg = NULL; 

	asn_dec_rval_t ret_val;

	// Realizamos la decodificación BER de los datos miembros
	ret_val = ber_decode( NULL,
						 &asn_DEF_TokenTypesConfig,
						 (void**)&pTTCfg,
						 (void*)pTokenTypeConfig,
						 ulTTConfigSize );

	if( ret_val.code == RC_FAIL )
		throw Pkcs11Exception( CKR_GENERAL_ERROR );

	for( int idx=0; idx<pTTCfg->list.count; ++idx )
	{
		CardTokenType	* pASN1TT = pTTCfg->list.array[idx];
		CTokenType * pTT = new CTokenType( string((char *)pASN1TT->name.buf,pASN1TT->name.size) );

		// TODO: podríamos eliminar las configuraciones con el mismo nombre:
		// buscar en m_vecTokenTypes un tokentype con el mismo nombre y eliminarlo si lo encontramos.

		for( int atrIdx=0; atrIdx<pASN1TT->atrs.list.count; ++ atrIdx )
		{
			CardATR * pCartAtr = pASN1TT->atrs.list.array[atrIdx];
			byteBuffer  atr( pCartAtr->atr.buf, pCartAtr->atr.size );
			byteBuffer  mask( pCartAtr->mask.buf, pCartAtr->mask.size );
			pTT->AddAtr( atr, mask );
		}

		for( int mechIdx=0; mechIdx<pASN1TT->mechanism.list.count; ++ mechIdx )
		{
			pTT->AddMechanism( CUtil::GetInt32(pASN1TT->mechanism.list.array[mechIdx]) );
		}

		//if( pASN1TT->params )
		int count = pASN1TT->params==NULL?0:pASN1TT->params->list.count;
		if( count > 0 )
		{
			//pTT->setCommType( pASN1TT->communicationsType.buf[0], pASN1TT->params->buf, pASN1TT->params->size );
			pTT->setCommType( pASN1TT->communicationsType.buf[0], pASN1TT->params->list.array[0]->buf, pASN1TT->params->list.array[0]->size );
			
			if( count > 1 )
				pTT->setPinParams(pASN1TT->params->list.array[1]->buf, pASN1TT->params->list.array[1]->size );
		}
		else
		{
			pTT->setCommType( pASN1TT->communicationsType.buf[0], NULL, 0 );
			pTT->setPinParams(NULL, 0 );
		}

		m_vecTokenTypes.insert(m_vecTokenTypes.begin(), pTT);

		pTT->SetASN1Flags( pASN1TT->asn1Flags.buf[0] );

		//m_vecTokenTypes.push_back(pTT);
	}

	if( pTTCfg )
		asn_DEF_TokenTypesConfig.free_struct(&asn_DEF_TokenTypesConfig, pTTCfg, 0);
}

// Destructor
CTokenTypeList::~CTokenTypeList()
{
	for (ulong idx = 0; idx < size(); idx++)
	{
		delete at( idx );
	}

}

ulong CTokenTypeList::size(void)
{
	return (ulong)m_vecTokenTypes.size();
}

CTokenType * CTokenTypeList::at( ulong idx )
{
	return m_vecTokenTypes.at( idx );
}
