//
//	Copyright © 2005 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#include "StdAfx.h"

// GUID stuff
#ifdef _WINDOWS
namespace windows {
	// NOTE!
	// NOTE!
	// NOTE! You have to import library 'Rpcrt4.lib' for this to be used !!!
	#include <Rpc.h>
}
#else

#endif

using namespace VWFC;

VWFC::Tools::UUID::UUID(UInt32 a, UInt32 b, UInt32 c, UInt32 d)
{
	fData[0]			= (UInt8) (a & 0xFF);
	fData[1]			= (UInt8) ((a >> 8) & 0xFF);
	fData[2]			= (UInt8) ((a >> 16) & 0xFF);
	fData[3]			= (UInt8) ((a >> 24) & 0xFF);
	fData[4]			= (UInt8) (b & 0xFF);
	fData[5]			= (UInt8) ((b >> 8) & 0xFF);
	fData[6]			= (UInt8) ((b >> 16) & 0xFF);
	fData[7]			= (UInt8) ((b >> 24) & 0xFF);
	fData[8]			= (UInt8) (c & 0xFF);
	fData[9]			= (UInt8) ((c >> 8) & 0xFF);
	fData[10]			= (UInt8) ((c >> 16) & 0xFF);
	fData[11]			= (UInt8) ((c >> 24) & 0xFF);
	fData[12]			= (UInt8) (d & 0xFF);
	fData[13]			= (UInt8) ((d >> 8) & 0xFF);
	fData[14]			= (UInt8) ((d >> 16) & 0xFF);
	fData[15]			= (UInt8) ((d >> 24) & 0xFF);
}

VWFC::Tools::UUID::UUID(UInt8* pArray, size_t size)
{
	for(size_t i=0; i<16; i++) {
		if ( i < size )	fData[i] = pArray[i];
		else			fData[i] = 0;
	}
}

VWFC::Tools::UUID::UUID(const VWFC::Tools::UUID& src)
{
	for(int i=0; i<16; i++)
		fData[i] = src.fData[i];
}

VWFC::Tools::UUID::~UUID()
{
}

VWFC::Tools::UUID& VWFC::Tools::UUID::operator=(const VWFC::Tools::UUID& src)
{
	for(int i=0; i<16; i++)
		fData[i] = src.fData[i];
	return *this;
}

bool VWFC::Tools::UUID::operator==(const VWFC::Tools::UUID& id)
{
	for(int i=0; i<16; i++) {
		if ( fData[i] == id.fData[i] )
			return false;
	}
	return true;
}

bool VWFC::Tools::UUID::operator!=(const VWFC::Tools::UUID& id)
{
	for(int i=0; i<16; i++) {
		if ( fData[i] != id.fData[i] )
			return false;
	}
	return true;
}

static char GetPieceAsChar(UInt8 piece)
{
	VWFC_ASSERT( piece <= 0x0F );
	char	ch;
	if ( piece <= 9 )
		ch	= '0' + piece;
	else
		ch	= 'A' + (piece - 0x0A);

	return ch;
}

static TXString	GetPieceAsText(UInt8 piece)
{
	char	buffer[3];
	buffer[0]	= ::GetPieceAsChar( piece >> 4 );
	buffer[1]	= ::GetPieceAsChar( piece & 0x0F );
	buffer[2]	= 0x00;

	TXString	str	= buffer;
	return str;
}

// {09E95D97-364C-43d5-8ADF-FF4CE0EC41A7}
VWFC::Tools::UUID::operator TXString() const
{
	TXString	str		= "{";
	str					+= ::GetPieceAsText( fData[0] );
	str					+= ::GetPieceAsText( fData[1] );
	str					+= ::GetPieceAsText( fData[2] );
	str					+= ::GetPieceAsText( fData[3] );
	str					+= "-";
	str					+= ::GetPieceAsText( fData[4] );
	str					+= ::GetPieceAsText( fData[5] );
	str					+= "-";
	str					+= ::GetPieceAsText( fData[6] );
	str					+= ::GetPieceAsText( fData[7] );
	str					+= "-";
	str					+= ::GetPieceAsText( fData[8] );
	str					+= ::GetPieceAsText( fData[9] );
	str					+= "-";
	str					+= ::GetPieceAsText( fData[10] );
	str					+= ::GetPieceAsText( fData[11] );
	str					+= ::GetPieceAsText( fData[12] );
	str					+= ::GetPieceAsText( fData[13] );
	str					+= ::GetPieceAsText( fData[14] );
	str					+= ::GetPieceAsText( fData[15] );
	str					+= TXString("}");


	return str;
}

static UInt8 GetDigitForChar(char ch)
{
	if ( ch >= '0' && ch <= '9' )
		return ch - '0';
	if ( ch >= 'A' && ch <= 'F' )
		return 0x0A + (ch - 'A');
	if ( ch >= 'a' && ch <= 'f' )
		return 0x0A + (ch - 'a');

	THROW_VWFC_EXCEPTION( VWException, 0, "Bad UUID string : bad char" );
	return 0;
}

static UInt8 GetDigitForPiece(const char* pString)
{
	UInt8		a		= ::GetDigitForChar( * (pString + 0) );
	UInt8		b		= ::GetDigitForChar( * (pString + 1) );
	
	VWFC_ASSERT( a <= 0x0F );
	VWFC_ASSERT( b <= 0x0F );

	UInt8		res		= a << 4 | (b & 0x0F);
	return res;
}

VWFC::Tools::UUID::UUID(const TXString& str)
{
	// {09E95D97-364C-43d5-8ADF-FF4CE0EC41A7}
	bool	b	= str.GetLength() == 38;
	if ( b ) {
		bool	b1	= str.GetAt( 0 ) == '{';
		bool	b2	= str.GetAt( 9 ) == '-';
		bool	b3	= str.GetAt( 14 ) == '-';
		bool	b4	= str.GetAt( 19 ) == '-';
		bool	b5	= str.GetAt( 24 ) == '-';
		bool	b6	= str.GetAt( 37 ) == '}';

		b			= b1 && b2 && b3 && b4 && b5 && b6;
	}

	VWFC_ASSERT( b );
	if ( ! b )
		THROW_VWFC_EXCEPTION( VWException, 0, "Bad UUID string : not in proper format" );

	const char* pString		= (const char*) str;

	fData[0]		= ::GetDigitForPiece( pString + 1 );
	fData[1]		= ::GetDigitForPiece( pString + 3 );
	fData[2]		= ::GetDigitForPiece( pString + 5 );
	fData[3]		= ::GetDigitForPiece( pString + 7 );
	fData[4]		= ::GetDigitForPiece( pString + 10 );
	fData[5]		= ::GetDigitForPiece( pString + 12 );
	fData[6]		= ::GetDigitForPiece( pString + 15 );
	fData[7]		= ::GetDigitForPiece( pString + 17 );
	fData[8]		= ::GetDigitForPiece( pString + 20 );
	fData[9]		= ::GetDigitForPiece( pString + 22 );
	fData[10]		= ::GetDigitForPiece( pString + 25 );
	fData[11]		= ::GetDigitForPiece( pString + 27 );
	fData[12]		= ::GetDigitForPiece( pString + 29 );
	fData[13]		= ::GetDigitForPiece( pString + 31 );
	fData[14]		= ::GetDigitForPiece( pString + 33 );
	fData[15]		= ::GetDigitForPiece( pString + 35 );
}

VWFC::Tools::UUID::UUID()
{
#ifdef _WINDOWS
	GUID	guid;
	using namespace windows;
	UuidCreateSequential( & guid );
	fData[0]			= (UInt8) ((guid.Data1 >> 24) & 0xFF);
	fData[1]			= (UInt8) ((guid.Data1 >> 16) & 0xFF);
	fData[2]			= (UInt8) ((guid.Data1 >> 8) & 0xFF);
	fData[3]			= (UInt8) (guid.Data1 & 0xFF);
	fData[4]			= guid.Data2 >> 8;
	fData[5]			= guid.Data2 & 0xFF;
	fData[6]			= guid.Data3 >> 8;
	fData[7]			= guid.Data3 & 0xFF;
	fData[8]			= guid.Data4[0];
	fData[9]			= guid.Data4[1];
	fData[10]			= guid.Data4[2];
	fData[11]			= guid.Data4[3];
	fData[12]			= guid.Data4[4];
	fData[13]			= guid.Data4[5];
	fData[14]			= guid.Data4[6];
	fData[15]			= guid.Data4[7];
#else
	CFUUIDRef		myUUID			= ::CFUUIDCreate( kCFAllocatorDefault );
	CFStringRef     myUUIDString	= ::CFUUIDCreateString( kCFAllocatorDefault, myUUID );
	
	char            strBuffer[100];
	// This is the safest way to obtain a C string from a CFString.
	CFStringGetCString(myUUIDString, strBuffer, 100, kCFStringEncodingASCII);

	const char* pString		= (const char*) strBuffer;

	fData[0]		= ::GetDigitForPiece( pString + 0 );
	fData[1]		= ::GetDigitForPiece( pString + 2 );
	fData[2]		= ::GetDigitForPiece( pString + 4 );
	fData[3]		= ::GetDigitForPiece( pString + 6 );
	fData[4]		= ::GetDigitForPiece( pString + 9 );
	fData[5]		= ::GetDigitForPiece( pString + 11 );
	fData[6]		= ::GetDigitForPiece( pString + 14 );
	fData[7]		= ::GetDigitForPiece( pString + 16 );
	fData[8]		= ::GetDigitForPiece( pString + 19 );
	fData[9]		= ::GetDigitForPiece( pString + 21 );
	fData[10]		= ::GetDigitForPiece( pString + 24 );
	fData[11]		= ::GetDigitForPiece( pString + 26 );
	fData[12]		= ::GetDigitForPiece( pString + 28 );
	fData[13]		= ::GetDigitForPiece( pString + 30 );
	fData[14]		= ::GetDigitForPiece( pString + 32 );
	fData[15]		= ::GetDigitForPiece( pString + 34 );
#endif
}

void VWFC::Tools::UUID::GetUUID(UInt32& out1, UInt32& out2, UInt32& out3, UInt32& out4)
{
   out1 = (UInt32) ( fData[0] + fData[1] * 256 + fData[2] * 65536 + fData[3] * 16777216 );
   out2 = (UInt32) ( fData[4] + fData[5] * 256 + fData[6] * 65536 + fData[7] * 16777216 );
   out3 = (UInt32) ( fData[8] + fData[9] * 256 + fData[10] * 65536 + fData[11] * 16777216 );
   out4 = (UInt32) ( fData[12] + fData[13] * 256 + fData[14] * 65536 + fData[15] * 16777216 );
}

void VWFC::Tools::UUID::GetUUID(UInt8* pArray, size_t size)
{
	for(size_t i=0; i<size; i++) {
		if ( i >= 16 )
			pArray[ i ]	= 0;
		else
			pArray[ i ]	= fData[ i ];
	}
}

TXString VWFC::Tools::UUID::Encode(const TXString& arrChars)
{
	const size_t	validCharsCount	= arrChars.GetLength();
	const size_t	extractCount	= 6;

	UInt32				nums[4];
	this->GetUUID( nums[0], nums[1], nums[2], nums[3] );
	char				szBuffer[ 4 * extractCount + 1 ];
	for(size_t iNum=0; iNum<4; iNum++) {
		UInt32		thisNum	= nums[ iNum ];
		for(size_t count=0; count<extractCount; count++) {
			size_t		index		= thisNum % validCharsCount;
			thisNum					= thisNum / validCharsCount;

			szBuffer[extractCount*iNum + count]	= arrChars.GetAt( index );
		}
	}
	szBuffer[ 4 * extractCount ]	= 0x00;

	return szBuffer;
}
