#include "dk_channel.h"
#include "dk_utils.h"
#include "dk_log.h"

#define APDU_OFFSET_LC    4

#ifdef DK_DEBUG
DK_Result (*test_open_channel)(channel_t *channel, secEse_7816_rpdu_t *rpdu) = NULL;
DK_Result __open_channel(channel_t *channel, secEse_7816_rpdu_t *rpdu)
#else
DK_Result open_channel(channel_t *channel, secEse_7816_rpdu_t *rpdu)
#endif
{
	DK_Result rc;

	byte aid[] = {(byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x01,
				  (byte) 0x51, (byte) 0x53, (byte) 0x45, (byte) 0x4C,
				  (byte) 0x46, (byte) 0x30, (byte) 0x31, (byte) 0x01};
	
	rc = spiOpen();
	if (rc) {
		DK_LOG_ERR("could not open spi");
		goto catch_error;
	}

	rc = initProcess();
	if (rc) {
		DK_LOG_ERR("could not init process");
		goto catch_error;
	}

    rc = secEseOpen(&channel->id);
	DK_LOG_WARN("lib opened channel %02x, FORCING it to 0", channel->id);
	// channel->id = 0;

    if (rc) {
        DK_LOG_ERR("secEseOpen channel open fail (0x%x). try again", rc);
		goto catch_error;
    }

	dk_memcpy(channel->aid, aid, sizeof(aid));
	channel->aid_len = sizeof(aid);

	rc = secEseSelect(0/*channel->id*/, channel->aid, 0, channel->aid_len, rpdu);
	if (rc) {
		DK_LOG_ERR("error selecting applet");
	}
	
	channel->is_open = TRUE;
	rc = DK_SUCCESS;

catch_error:
	return rc;
}

// TODO fix return codes
#ifdef DK_DEBUG
DK_Result (*test_transmit)(byte* buf, size_t buflen, secEse_7816_rpdu_t* rpdu, channel_t* channel) = NULL;
DK_Result __transmit(byte* buf, size_t buflen, secEse_7816_rpdu_t* rpdu, channel_t* channel)
#else
DK_Result transmit(byte* buf, size_t buflen, secEse_7816_rpdu_t* rpdu, channel_t* channel)
#endif
{	
	DK_Result rc; 

	byte cdata[MAX_CAPDU_DATA_SIZE] = {0};

	secEse_7816_cpdu_t cpdu = {0};
	cpdu.pdata = cdata;

	printf(TAG"buf[%d]: ", buflen);
	print_array(buf, buflen);
	if (MAX_CAPDU_DATA_SIZE < buf[APDU_OFFSET_LC]) {
		return DK_ERROR_GENERIC;
	}
	
	rc = barray_to_cpdu(buf, buflen, &cpdu);
	if (rc) {
		DK_LOG_ERR("error transforming byte array to cpdu");
		goto catch_error;
	}

	cpdu_dump(&cpdu);

	rc = secEseTransmit(0, &cpdu, rpdu);
	if (rc) {
		DK_LOG_ERR("error on secEseTransmit");
		goto catch_error;
	}

	rc = DK_SUCCESS;

catch_error:
	return DK_SUCCESS;
}

#ifdef DK_DEBUG
DK_Result (*test_close_channel)(channel_t *channel) = NULL;
DK_Result __close_channel(channel_t *channel)
#else
DK_Result close_channel(channel_t *channel)
#endif
{
	DK_Result rc;

	if (channel->is_open == FALSE) {
		DK_LOG_WARN("channel already closed");
		rc = DK_SUCCESS;
		goto catch_error;
	}

	rc = secEseClose(channel->id);
	if (rc) {
		DK_LOG_ERR("error closing channel");
		goto catch_error;
	}

	rc = spiClose();
	if (rc) {
		DK_LOG_ERR("error closing spi");
		goto catch_error;
	}

	channel->is_open = FALSE;

	rc = DK_SUCCESS;

catch_error:
	return rc;
}

#ifdef DK_DEBUG
DK_Result open_channel(channel_t *channel, secEse_7816_rpdu_t *rpdu)
{
	if (test_open_channel) return test_open_channel(channel, rpdu);
	return __open_channel(channel, rpdu);
}

DK_Result transmit(byte* buf, size_t buflen, secEse_7816_rpdu_t* rpdu, channel_t* channel)
{
	if (test_transmit) return test_transmit(buf, buflen, rpdu, channel);
	return __transmit(buf, buflen, rpdu, channel);
}

DK_Result close_channel(channel_t *channel)
{
	if (test_close_channel) return test_close_channel(channel);
	return __close_channel(channel);
}

DK_Result mock_open_channel(channel_t *channel, secEse_7816_rpdu_t *rpdu)
{
	DK_Result rc;

	rpdu->pdata = dk_malloc(MAX_RAPDU_DATA_SIZE);
	channel->is_open = TRUE;

	rc = DK_SUCCESS;

catch_error:
	return rc;
}

DK_Result mock_close_channel(channel_t *channel)
{
	channel->is_open = FALSE;
	return DK_SUCCESS;
}

DK_Result mock_transmit_send_apdu(byte* buf, size_t buflen, secEse_7816_rpdu_t* rpdu, channel_t* channel)
{
	DK_Result rc;

	byte expected1[] = {0x84, 0x00, 0x00, 0x00, 
						0x28, 0x6c, 0x97, 0x44, 
						0x09, 0x59, 0xd2, 0x00, 
						0x18, 0xa4, 0xc9, 0xa5, 
						0x55, 0x97, 0x9a, 0x93, 
						0x49, 0xb5, 0xb6, 0x6e, 
						0x35, 0x0a, 0x34, 0x73, 
						0xba, 0x05, 0x93, 0xd1, 
						0x75, 0xae, 0xb4, 0xdf, 
						0xfb, 0x14, 0xd1, 0x3a, 
						0x19, 0x63, 0x82, 0xda, 
						0x0b};

	byte expected2[] = {0x84, 0xba, 0xca, 0xfe, 
						0x18, 0xf4, 0x48, 0x8a, 
						0x60, 0x2e, 0xa3, 0x14, 
						0x37, 0x51, 0xc3, 0x36, 
						0xad, 0xd9, 0x08, 0xb7, 
						0x99, 0x11, 0xb8, 0x31, 
						0x87, 0xd5, 0xe6, 0x20, 
						0x61};

	byte expected3[] = {0x84, 0xbb, 0xcc, 0xdd, 
						0x18, 0xca, 0x33, 0xba, 
						0xe2, 0x6e, 0xe0, 0x50, 
						0x44, 0x83, 0x58, 0x24, 
						0xb5, 0xb7, 0xc7, 0xf9, 
						0x65, 0xf0, 0x9c, 0x42, 
						0xad, 0x91, 0x60, 0xd0, 
						0x57};

	byte response1[] = {0xba, 0xce, 0x5a, 0x0e, 
						0x51, 0x0a, 0x69, 0xe7, 
						0x22, 0xfd, 0xd2, 0x2a, 
						0x41, 0xb8, 0xbd, 0x3b, 
						0x3c, 0x01, 0xc8, 0x44, 
						0x63, 0x44, 0x5d, 0x14, 
						0x5e, 0x3f, 0xe1, 0x1a, 
						0x26, 0xda, 0xa8, 0x56, 
						0xef, 0x84, 0xad, 0xdc,
						0xf4, 0x3b, 0x17, 0x6c, 
						0x90, 0x00}; 

	byte response2[] = {0x61, 0x1c, 0x9e, 0x42, 
						0xa3, 0x3c, 0x42, 0x60, 
						0x11, 0xc9, 0x02, 0xaa, 
						0x45, 0xc6, 0x01, 0xee, 
						0x5a, 0xed, 0x57, 0x6c, 
						0xa9, 0x46, 0x86, 0x64, 
						0x90, 0x00};

	byte response3[] = {0x20, 0x85, 0x08, 0xa9, 
						0x5f, 0x3a, 0x52, 0x64, 
						0xff, 0xc0, 0x79, 0x40, 
						0x79, 0x6e, 0xab, 0x39, 
						0x79, 0xd2, 0x95, 0xcb, 
						0x7d, 0x27, 0xdf, 0x59, 
						0x90, 0x00};

	if (dk_memcmp(buf, expected1, sizeof(expected1)) == 0) {
		barray_to_rpdu(response1, sizeof(response1), rpdu);
	}
	else if (dk_memcmp(buf, expected2, sizeof(expected2)) == 0) {
		barray_to_rpdu(response2, sizeof(response2), rpdu);
	}
	else if (dk_memcmp(buf, expected3, sizeof(expected3)) == 0) {
		barray_to_rpdu(response3, sizeof(response3), rpdu);
	}
	else {
		rc = DK_ERROR_BAD_STATE;
		goto catch_error;
	}

	rc = DK_SUCCESS;

catch_error:
	return rc;
}

DK_Result mock_transmit_open_secure_channel(
	byte* buf, 
	size_t buflen, 
	secEse_7816_rpdu_t* rpdu, 
	channel_t* channel)
{
	DK_Result rc;

	byte expected1[] = {0xca, 0xca, 0x00, 0xe0,
						0x00};

	byte expected2[] = {0x80, 0x50, 0x31, 0x00,
						0x08, 0xf2, 0x6d, 0x9a,
						0x1f, 0x21, 0x67, 0x46,
						0xd3, 0x00};

	byte expected3[] = {0x84, 0x82, 0x01, 0x00,
						0x10, 0x90, 0x14, 0xcf,
						0xc8, 0x0b, 0xa5, 0x35,
						0xea, 0xc4, 0x95, 0x09,
						0xb3, 0x73, 0xfd, 0x99,
						0x91};

	byte response1[] = {
		(byte) 0xE0, (byte) 0x12, (byte) 0xC0, (byte) 0x04, 
		(byte) 0x01, (byte) 0x01, (byte) 0x88, (byte) 0x10, 
		(byte) 0xC0, (byte) 0x04, (byte) 0x02, (byte) 0x01, 
		(byte) 0x88, (byte) 0x10, (byte) 0xC0, (byte) 0x04, 
		(byte) 0x03, (byte) 0x01, (byte) 0x88, (byte) 0x10, 
		(byte) 0x90, (byte) 0x00};

	byte response2[] = {
        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
        (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x03, 
        (byte) 0x10, (byte) 0x05, (byte) 0x26, (byte) 0xA0, 
        (byte) 0xC4, (byte) 0x0C, (byte) 0x28, (byte) 0x57, 
        (byte) 0x16, (byte) 0xFC, (byte) 0xDF, (byte) 0x9F, 
        (byte) 0xC2, (byte) 0x97, (byte) 0x53, (byte) 0x59, 
        (byte) 0x1C, (byte) 0x00, (byte) 0x00, (byte) 0x06, 
        (byte) 0x90, (byte) 0x00};

	byte response3[] = {(byte) 0x90, (byte) 0x00};
    
	if (dk_memcmp(buf, expected1, sizeof(expected1)) == 0) {
		barray_to_rpdu(response1, sizeof(response1), rpdu);
	}
	else if (dk_memcmp(buf, expected2, sizeof(expected2)) == 0) {
		barray_to_rpdu(response2, sizeof(response2), rpdu);
	}
	else if (dk_memcmp(buf, expected3, sizeof(expected3)) == 0) {
		barray_to_rpdu(response3, sizeof(response3), rpdu);
	}
	else {
		rc = DK_ERROR_BAD_STATE;
		goto catch_error;
	}

	rc = DK_SUCCESS;

catch_error:
	return rc;
}

DK_Result mock_transmit_full(
	byte* buf, 
	size_t buflen, 
	secEse_7816_rpdu_t* rpdu, 
	channel_t* channel)
{
	DK_Result rc;

	// printf(TAG"buf[%d]: ", buflen);
	// print_array(buf, buflen);

	byte getDataRequest[] = {
		(byte)0x80, (byte)0xca, (byte)0x00, (byte)0xe0,
		(byte)0x00};

	byte initializeUpdateRequest[] = { // third byte is the kvn
		(byte)0x80, (byte)0x50, (byte)0x31, (byte)0x00,
		(byte)0x08, (byte)0xf2, (byte)0x6d, (byte)0x9a, 
		(byte)0x1f, (byte)0x21, (byte)0x67, (byte)0x46, 
		(byte)0xd3, (byte)0x00};

	byte externalAuthRequest[] = {
		(byte)0x84, (byte)0x82, (byte)0x33, (byte)0x00, 
		(byte)0x10, (byte)0x90, (byte)0x14, (byte)0xcf, 
		(byte)0xc8, (byte)0x0b, (byte)0xa5, (byte)0x35, 
		(byte)0xea, (byte)0xc5, (byte)0xe5, (byte)0x70, 
		(byte)0xb1, (byte)0xbb, (byte)0xbd, (byte)0xfc, 
		(byte)0xee};

	byte getDataResponse[] = {
		(byte) 0xE0, (byte) 0x12, (byte) 0xC0, (byte) 0x04, 
		(byte) 0x01, (byte) 0x01, (byte) 0x88, (byte) 0x10, 
		(byte) 0xC0, (byte) 0x04, (byte) 0x02, (byte) 0x01, 
		(byte) 0x88, (byte) 0x10, (byte) 0xC0, (byte) 0x04, 
		(byte) 0x03, (byte) 0x01, (byte) 0x88, (byte) 0x10, 
		(byte) 0x90, (byte) 0x00};

	byte initializeUpdateResponse[] = {
		(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
		(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
		(byte)0x00, (byte)0x00, (byte)0x01, (byte)0x03, 
		(byte)0x60, (byte)0x05, (byte)0x26, (byte)0xa0, 
		(byte)0xc4, (byte)0x0c, (byte)0x28, (byte)0x57, 
		(byte)0x16, (byte)0xfc, (byte)0xdf, (byte)0x9f, 
		(byte)0xc2, (byte)0x97, (byte)0x53, (byte)0x59, 
		(byte)0x1c, (byte)0x00, (byte)0x00, (byte)0x1b, 
		(byte)0x90, (byte)0x00};
	
	byte externalAuthResponse[] = {
		(byte) 0x90, (byte) 0x00};

	

	if (dk_memcmp(buf, getDataRequest, sizeof(getDataRequest)) == 0) {
		barray_to_rpdu(getDataResponse, sizeof(getDataResponse), rpdu);
	}
	else if (dk_memcmp(buf, initializeUpdateRequest, sizeof(initializeUpdateRequest)) == 0) {
		barray_to_rpdu(initializeUpdateResponse, sizeof(initializeUpdateResponse), rpdu);
	}
	else if (dk_memcmp(buf, externalAuthRequest, sizeof(externalAuthRequest)) == 0) {
		barray_to_rpdu(externalAuthResponse, sizeof(externalAuthResponse), rpdu);
	}
	else {
		rc = DK_ERROR_BAD_STATE;
		goto catch_error;
	}

	rc = DK_SUCCESS;

catch_error:
	return rc;
}

DK_Result dump_channel(channel_t *channel)
{
	DK_LOG_INFO("******************dumping channel******************");

	printf(TAG"  id: %x\n", channel->id);

	printf(TAG"  aid[%d]: ", channel->aid_len);
	print_array(channel->aid, channel->aid_len);

	printf(TAG"  is_open: %x\n", channel->is_open);

	DK_LOG_INFO("***************************************************");
}

#endif