// SDKUtilities.cpp 
//

#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <limits.h>
#include <crtdbg.h>

#include "SDKUtilities.h"
#include "NDTSError.h"
#include "SDKUtilitiesError.h"
#include "SubversionRevision.h"


void CALL_CONV SDKUtilities_DLLInitialise()
{
	NDTS_InitErrorService() ;
	NDTS_RegisterGetCodeStringFunction(NDTS_ERROR_UTILITY_SDK_INDEX, &SDKUtilities_GetCodeString) ;
  
}

void CALL_CONV SDKUtilities_DLLUninitialise()
{
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH :
			SDKUtilities_DLLInitialise() ;
			break ;
		case DLL_PROCESS_DETACH :
			SDKUtilities_DLLUninitialise() ;
			break ;
		case DLL_THREAD_ATTACH :
			break ;
		case DLL_THREAD_DETACH :
			break ;
    }
	hModule; lpReserved; // Keep compiler happy

    return TRUE ;
}

// **************************************************************************************
 

void CALL_CONV SDKUtilities_ByteCopy(const void *pIn, void *pOut, unsigned long numberOfBytes)
{
	// For efficiency, copies as many four byte words on four byte boundary as possible. 
	// Do the rest as pre and post bytes.
	unsigned long *pInUL, *pOutUL ;
	unsigned char *pInUC, *pOutUC ;
	unsigned long i, numberOfULs, numberOfPostBytes, numberOfPreBytes ;

	numberOfPreBytes = ((unsigned long) pIn & 3) != 0 ? 4 - ((unsigned long) pIn & 3) : 0 ;
	numberOfULs = (numberOfBytes - numberOfPreBytes) >> 2 ;
	numberOfPostBytes = (numberOfBytes - numberOfPreBytes) & 3 ;

	pOutUC = (unsigned char *) pOut ;
	pInUC = (unsigned char *) pIn ;

	for (i = 0 ; i < numberOfPreBytes ; i++)
		*pOutUC++ = *pInUC++ ;

	pOutUL = (unsigned long *) pOutUC ;
	pInUL = (unsigned long *) pInUC ;

	for (i = 0 ; i < numberOfULs ; i++)
		*pOutUL++ = *pInUL++ ;

	pOutUC = (unsigned char *) pOutUL ;
	pInUC = (unsigned char *) pInUL ;

	for (i = 0 ; i < numberOfPostBytes ; i++)
		*pOutUC++ = *pInUC++ ;
}

NDTS_Boolean SDKUtilities_SignAndMagToTwosComp(unsigned char *pBuffer, int sixteenBitWords)
{
	unsigned long *pCurrent32BitWord = 0; // Point to the first then the current 32 bit word to swap
	unsigned long *pEndOf32BitWords = 0;  // Pointer to the first 32 bit word following the end of data to  be swapped
	NDTS_Boolean is16BitsLeftOver = NDTS_BFalse;
	const int maskLowerSignBit = (1 << 11);
	const int maskHigherSignBit = (1 << (11 + 16));

	_ASSERTE(sixteenBitWords <= 65536);

	// Check alignment and swap first 16 bits if neccessary
	switch ((unsigned int)pBuffer % 4)
	{
	case 0:
		// Buffer starts on 32 bit boundary
		// Numbers in example are 16 bit "addresses"
		//2 3 4 5 6 : 5 16 bit words: 5 >> 2 = 2 {2 32bit words to swap and 1 16bit at the end}
		//ZZZ ZZZ X  - ZZZ 32 bit swap X 16 bit swap
		//
		//2 3 4 5 = 4 16 bit words : 4 >> 2 = 2 {2 32bit word to swap and no 16bit}
		//ZZZ ZZZ
		pCurrent32BitWord = (unsigned long*)(pBuffer);
		pEndOf32BitWords  = (unsigned long*)pBuffer  + (sixteenBitWords >> 1);
		if (sixteenBitWords % 2 == 1)
		{
			_ASSERTE(0);  // RQ SDK currently rounds to even number of samples due to firmware limitation so this isn't tested
			is16BitsLeftOver = NDTS_BTrue;
		}
		break;
	case 1:
	case 3:
		SDKUtilities_HandleError(SDKUtilities_SignAndMagToTwosComp_MustBe16BitAligned) ;
		_ASSERTE(0);
		return NDTS_BFalse;
		break;
	case 2:
		// Swap non aligned first 16 bits
		_ASSERTE(0); // This code hasn't been tested because A Scan memory always seems to be 32 bit aligned
		if (*(short*)pBuffer & maskLowerSignBit)
			*(short*)pBuffer = (~(*(short*)pBuffer)) |
			(short)maskLowerSignBit;

		//1 2 3 4 = 4 16bit words : (4-1) >> 1  = 2 {1 32bit word to swap and 1 16bit at the end}
		//X ZZZ X
		//
		//1 2 3 4 5 = 5 16bit words : (5-1) >> 1 = 2 {2 32bit words to swap and no 16bit at end}
		//X ZZZ ZZZ
		pCurrent32BitWord = (unsigned long*)(pBuffer + 2);
		pEndOf32BitWords  = (unsigned long*)(pBuffer + 2) + ((sixteenBitWords - 1) >> 1);
		if (sixteenBitWords % 2 == 0)
			is16BitsLeftOver = NDTS_BTrue;
		break;
	default:
		_ASSERTE(0);
		break;
	}

	// Do 32 bit words, two swaps at a time, minimising memory access
	while (pCurrent32BitWord < pEndOf32BitWords)
	{
		unsigned long value = *pCurrent32BitWord;
		unsigned long originalValue = value;
		if (value & maskHigherSignBit)
			value = (value & 0xffff) | 
					((~value) & 0xffff0000) |
					maskHigherSignBit;
		if (value & maskLowerSignBit)
			value = (value & 0xffff0000) | 
					((~value) & 0xffff) | 
					maskLowerSignBit;
		// This should be faster than writing if there is no change
		// because it is a reg to reg compare rather than a mem write and
		// possibly extra cache invalidate, especially where there is lots of +ve data
		if (value != originalValue)
			*pCurrent32BitWord = value;

		pCurrent32BitWord++;
	}

	// Do the final 16 bits if required
	if (is16BitsLeftOver)
	{
		if (*(short*)pCurrent32BitWord & maskLowerSignBit)
			*(short*)pCurrent32BitWord = (~(*(short*)pCurrent32BitWord)) |
										 (short)maskLowerSignBit;
	}
	return NDTS_BTrue;
}



unsigned long CALL_CONV SDKUtilities_CalculateCRC32(unsigned long seed, const unsigned long *pCRCTable, const char *pData, unsigned long numberOfBytes)
{ 
	unsigned long i, j, crcAccum ;
	const char *p = pData ;

	crcAccum = seed ;

	for (j = 0 ; j < numberOfBytes ; j++)
	{ 
		i = ((unsigned long) (crcAccum >> 24) ^ *p++) & 0xff ;
		crcAccum = (crcAccum << 8) ^ pCRCTable[i] ; 
	}
	
	return crcAccum ; 
}

void CALL_CONV  SDKUtilities_GenerateCRCTable(unsigned long *pCRCTable)
{ 
	unsigned long i, j, crcAccum ;
	const int polynomial = 0x04c11db7 ;

	for (j = 0 ; j < 256 ; j++)
	{ 
		crcAccum = j << 24 ;

		for (i = 0 ; i < 8 ; i++)
			crcAccum = (crcAccum & 0x80000000L) != 0 ? (crcAccum << 1) ^ polynomial : (crcAccum << 1) ;

		pCRCTable[j] = crcAccum ; 
	}
}

const char * CALL_CONV SDKUtilities_GetSubversionRevisionString()
{
	return(SDKUtilities_SUBVERSION_INFO_STRING) ;
}