#include "iqi_blob_fw_example.h"
#include "iqi_mst.h"
#include "iqi_fast_processing.h"

//BLOB implementation is available in extended version
//please contact info@iq2-development.de for more information
#if 0

//#define BLOB_WRITE_PERFORMANCE_TEST 1

#define DATA_BUFFER_SIZE 256
#define READ_BLOB_ID -4096
#define WRITE_BLOB_ID 4096

/**
 * \brief The example is base on read (non cached), write(cached) BLOB data
 *		  Blob ID defined in defines: READ_BLOB_ID/WRITE_BLOB_ID
 *		  Example expects identical length for reading and writing
 *		  To compare defference between Cached and Non cached writing, define BLOB_WRITE_PERFORMANCE_TEST should be defined
 *
 * \param[in]   uiPortNo      COM or virtual ethernet port number of iqInterface
 * \param[out]  cpErrMsg      an error message (minimum 256 symbols!)
 *
 * \return error code
 */
int16 blob_fw_example_WriteBlobDataCached(
    uint16 uiPortNo,
    char08* cpErrMsg)
{
    int16 error_resut = ERR_CUSTOM;

    do
    {
		int16 error_resut_help = mst_SwitchISDUCachedMode(uiPortNo, 0, cpErrMsg); //disable cached mode
		if (error_resut_help < ERR_NONE)
		{
			error_resut = error_resut_help;
			break;
		}
		uchar08 buffer[DATA_BUFFER_SIZE] = { 0 };

		ulong32 recvDataSize;
		printf("readind BLOB id: %d\n", READ_BLOB_ID);
		int16 result = mst_ReadBlob(uiPortNo, READ_BLOB_ID, buffer, DATA_BUFFER_SIZE, &recvDataSize, cpErrMsg);

		if (result == ERR_NONE)
		{
			printf("Received data for BLOB id: %d\n", READ_BLOB_ID);
			for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
			{
				printf("0x%X ", buffer[byteIndex]);
				if ((byteIndex != 0) && (byteIndex % 16) == 0)
				{
					printf("\n");
				}
			}
			printf("\n");
		}
		else
		{
			return result;
		}

#ifdef BLOB_WRITE_PERFORMANCE_TEST
		unsigned int tests_quantity = 50;

		clock_t start, end;
		double total_t;

		for (unsigned int mode = 0; mode <= 1; mode++)
		{
			start = clock();
			//activate 
			error_resut_help = mst_SwitchISDUCachedMode(uiPortNo, mode, cpErrMsg);
			if (error_resut_help < ERR_NONE)
			{
				error_resut = error_resut_help;
				break;
			}
			for (unsigned int counter = 0; counter < tests_quantity; counter++)
			{
#else
				unsigned int mode = 1; //cached
				unsigned int counter = 0;
#endif
				//write BLOB data 

				for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
				{
					buffer[byteIndex] = (~buffer[byteIndex]) + counter;
				}

				printf("Writing BLOB id: %d\n", WRITE_BLOB_ID);
				for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
				{
					printf("0x%X ", buffer[byteIndex]);
					if ((byteIndex != 0) && (byteIndex % 16) == 0)
					{
						printf("\n");
					}
				}
				printf("\n");
				error_resut_help = mst_WriteBlob2(uiPortNo, WRITE_BLOB_ID, buffer, recvDataSize, mode, cpErrMsg);

				if (error_resut_help < ERR_NONE)
				{
					return error_resut_help;
				}
#ifdef BLOB_WRITE_PERFORMANCE_TEST
			}
			end = clock();
			total_t = (double)(end - start) / CLOCKS_PER_SEC;
			printf("Duration for mode: %u, : %f\n", mode, total_t);
		}
#endif
        error_resut = ERR_NONE;
    } while (0);

	mst_SwitchISDUCachedMode(uiPortNo, 0, cpErrMsg); //disable cached mode

    return error_resut;
}


/**
 * \brief The example is base on read, write and validate BLOB data
 *		  Blob ID is see define READ_BLOB_ID/WRITE_BLOB_ID
 *		  Example expects identical length for reading and writing
 *
 * \param[in]   uiPortNo      COM or virtual ethernet port number of iqInterface
 * \param[out]  cpErrMsg      an error message (minimum 256 symbols!)
 *
 * \return error code
 */
int16 blob_fw_example_StartBlobTest(uint16 uiPortNo, char08* cpErrMsg)
{
	//read vendor name
	uint16 uiISDUErr = 0;
	uchar08 buffer[DATA_BUFFER_SIZE] = { 0 };

	ulong32 recvDataSize;
	printf("readind BLOB id: %d\n", READ_BLOB_ID);
	int16 result = mst_ReadBlob(uiPortNo, READ_BLOB_ID, buffer, DATA_BUFFER_SIZE, &recvDataSize, cpErrMsg);

	if (result == ERR_NONE)
	{
		printf("Received data for BLOB id: %d\n", READ_BLOB_ID);
		for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
		{
			printf("0x%X ", buffer[byteIndex]);
			if ((byteIndex != 0) && (byteIndex % 16) == 0)
			{
				printf("\n");
			}
		}
		printf("\n");
	}
	else
	{
		return result;
	}

	//write BLOB data 
	uchar08 write_buffer[DATA_BUFFER_SIZE] = { 0 };

	for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
	{
		write_buffer[byteIndex] = ~buffer[byteIndex];
	}

	printf("Write BLOB id: %d\n", -READ_BLOB_ID);
	for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
	{
		printf("0x%X ", write_buffer[byteIndex]);
		if ((byteIndex != 0) && (byteIndex % 16) == 0)
		{
			printf("\n");
		}
	}
	printf("\n");
	result = mst_WriteBlob(uiPortNo, WRITE_BLOB_ID, write_buffer, recvDataSize, cpErrMsg);

	if (result < ERR_NONE)
	{
		return result;
	}

	//test written data
	memset(buffer, 0, DATA_BUFFER_SIZE);
	printf("readind BLOB id: %d\n", READ_BLOB_ID);
	result = mst_ReadBlob(uiPortNo, READ_BLOB_ID, buffer, DATA_BUFFER_SIZE, &recvDataSize, cpErrMsg);

	if (result < ERR_NONE)
	{
		return result;
	}

	//validate data

	for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
	{
		if (write_buffer[byteIndex] != buffer[byteIndex])
		{
			snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
				"written data is not equal as read data index %d, wrote: %X, read: %x \n",
				byteIndex, write_buffer[byteIndex], buffer[byteIndex]);
			return ERR_OPERATION;
		}
	}

	printf("Written and read data are equal\n");

	return result;
}

int16 blob_fw_example_StartFwUpdateTest(uint16 uiPortNo, unsigned char is_cached, char08* cpErrMsg)
{
	int16 result = ERR_CUSTOM;
	// try to set operating mode to Operate
	uchar08 ucState;
	CHK_RETURN(mst_SetOperatingMode(uiPortNo, MST_OPERATING_MODE_OPERATE,
		MST_STATE_OPERATE, &ucState, cpErrMsg));
	mst_StateT state = (mst_StateT)ucState;
	printf("go to operate: state %d\n", state);

	// Get iqInterface master status and input process data from device
	// (see comment for mst_StatusT in iqi_mst.h)
	printf("read master status\n");
	mst_StatusT status;
	uchar08 pdIn[MAX_PD_BUF_LEN];
	int16 pdInSize;
	ASSIGN_RETURN(pdInSize, mst_GetStatus(uiPortNo, (uchar08*)&status,
		pdIn, 32, cpErrMsg));

	FILE* fp;
	errno_t err;

	const char* fw_file = "..\\..\\iolfw\\fw_application_binary.bin";

	err = fopen_s(&fp, fw_file, "rb");
	if (fp == 0)
	{
		snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN, "Cannot open file correctly. error %lu, file: %s", err, fw_file);
		return ERR_CUSTOM;
	}
	fseek(fp, 0, SEEK_END);
	long ulFileSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	uchar08* file_buffer = malloc(ulFileSize);
	if (file_buffer == NULL)
	{
		snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN, "Cannot allocate memory. size %lu", ulFileSize);
		return ERR_CUSTOM;
	}
	size_t read_size = fread(file_buffer, 1, ulFileSize, fp);
	fclose(fp);

	if (read_size != ulFileSize)
	{
		snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN, "Cannot read file correctly. Read bytes: : %llu, expected: %d", read_size, ulFileSize);
		result = ERR_CUSTOM;
	}
	else
	{
		result = mst_FirmwareUpdate2(uiPortNo,
			"BOOTLOAD_FW",
			"1234",
			file_buffer,
			ulFileSize,
			3,
			is_cached,
			cpErrMsg);
	}
	free(file_buffer);

	return result;
}

#endif