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

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.

	This file is part of Sortix.

	Sortix is free software: you can redistribute it and/or modify it under the
	terms of the GNU General Public License as published by the Free Software
	Foundation, either version 3 of the License, or (at your option) any later
	version.

	Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
	WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
	FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
	details.

	You should have received a copy of the GNU General Public License along
	with Sortix. If not, see <http://www.gnu.org/licenses/>.

	iprintable.cpp
	A common interface shared by all devices that can be printed text to.

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

#include "platform.h"
#include "iprintable.h"

namespace Sortix
{
	inline int UInt8ToString(uint8_t Num, char* Dest)
	{
		uint8_t Copy = Num;
		int Result = 0;

		while ( Copy > 9 ) { Copy /= 10; Result++; }

		int Offset = Result;
		while ( Offset >= 0 )
		{
			Dest[Offset] = '0' + Num % 10; Num /= 10; Offset--;
		}

		return Result + 1;
	}

	inline int UInt16ToString(uint16_t Num, char* Dest)
	{
		uint16_t Copy = Num;
		int Result = 0;

		while ( Copy > 9 ) { Copy /= 10; Result++; }

		int Offset = Result;
		while ( Offset >= 0 )
		{
			Dest[Offset] = '0' + Num % 10; Num /= 10; Offset--;
		}

		return Result + 1;
	}

	inline int UInt32ToString(uint32_t Num, char* Dest)
	{
		uint32_t Copy = Num;
		int Result = 0;

		while ( Copy > 9 ) { Copy /= 10; Result++; }

		int Offset = Result;
		while ( Offset >= 0 )
		{
			Dest[Offset] = '0' + Num % 10; Num /= 10; Offset--;
		}

		return Result + 1;
	}

	int UInt64ToString(uint64_t Num, char* Dest)
	{
		uint64_t Copy = Num;
		int Result = 0;

		while ( Copy > 9 ) { Copy /= 10; Result++; }

		int Offset = Result;
		while ( Offset >= 0 )
		{
			Dest[Offset] = '0' + Num % 10; Num /= 10; Offset--;
		}

		return Result + 1;
	}

	// Missing functions here.

	inline int UInt32ToString16(uint32_t Num, char* Dest)
	{
		uint32_t Copy = Num;
		int Result = 0;

		while ( Copy > 15 ) { Copy /= 16; Result++; }

		int Offset = Result;
		while ( Offset >= 0 )
		{
			if ( Num % 16 < 10 )
			{
				Dest[Offset] = '0' + Num % 16;
			}
			else
			{
				Dest[Offset] = 'A' + (Num % 16) - 10;
			}
			Num /= 16; Offset--;
		}

		return Result + 1;
	}

	inline int UInt64ToString16(uint64_t Num, char* Dest)
	{
		uint64_t Copy = Num;
		int Result = 0;

		while ( Copy > 15 ) { Copy /= 16; Result++; }

		int Offset = Result;
		while ( Offset >= 0 )
		{
			if ( Num % 16 < 10 )
			{
				Dest[Offset] = '0' + Num % 16;
			}
			else
			{
				Dest[Offset] = 'A' + (Num % 16) - 10;
			}
			Num /= 16; Offset--;
		}

		return Result + 1;
	}

	// Missing functions here.

	#define READY_SIZE 128
	#define READY_FLUSH() Ready[AmountReady] = 0; Print(Ready); AmountReady = 0;
	#define F(N) (*(Format+N))

	#define U32 1
	#define U64 2
	#define X32 3
	#define X64 4
	#define STRING 5
	#define CHAR 6

	void IPrintable::PrintFV(const char* Format, const void* Parameters)
	{
		char Ready[READY_SIZE + 1];
		int AmountReady = 0;
		const char* Parameter = (const char*) Parameters;

		while ( *Format != '\0' )
		{
			if ( *Format != '%' )
			{
				Ready[AmountReady] = *Format; AmountReady++; Format++;
			}
			else
			{
				int Type = 0;
				int Len = 1;

				if ( F(1) == 'c' ) { Type = CHAR; Len += 1; } else
				if ( F(1) == 's' ) { Type = STRING; Len += 1; } else
				if ( F(1) == 'u' ) { Type = U32; Len += 1; } else
				if ( F(1) == '3' && F(2) == '2' && F(3) == 'u' ) { Type = U32; Len += 3; } else
				if ( F(1) == '6' && F(2) == '4' && F(3) == 'u' ) { Type = U64; Len += 3; } else
				if ( F(1) == 'x' ) { Type = X32; Len += 1; } else
				if ( F(1) == '3' && F(2) == '2' && F(3) == 'x' ) { Type = X32; Len += 3; } else
				if ( F(1) == '6' && F(2) == '4' && F(3) == 'x' ) { Type = X64; Len += 3; } else
	#ifdef PLATFORM_X86
				if ( F(1) == 'z' ) { Type = U32; Len += 1; } else
				if ( F(1) == 'p' ) { Type = X32; Len += 1; } else
	#elif defined(PLATFORM_X64)
				if ( F(1) == 'z' ) { Type = U64; Len += 1; } else
				if ( F(1) == 'p' ) { Type = X64; Len += 1; } else
	#endif
	//#ifdef PLATFORM_X86_FAMILY
				if ( F(1) == 'j' && F(2) == 'u' ) { Type = U64; Len += 2; } else
				if ( F(1) == 'j' && F(2) == 'x' ) { Type = X64; Len += 2; } else { }
	//#endif

				if ( Type == STRING )
				{
					// TODO: This isn't efficient.
					READY_FLUSH();
					Print(*((const char**)Parameter)); Parameter += sizeof(char*);
				}
				else if ( Type == CHAR )
				{
					if ( READY_SIZE <= AmountReady ) { READY_FLUSH(); }
					Ready[AmountReady] = *((uint32_t*)Parameter) & 0xFF; Parameter += sizeof(uint32_t);
				}
				else if ( Type == U32 )
				{
					if ( READY_SIZE - AmountReady < 10 ) { READY_FLUSH(); }
					uint32_t Num = *((uint32_t*)Parameter); Parameter += sizeof(uint32_t);
					AmountReady += UInt32ToString(Num, Ready + AmountReady);
				}
				else if ( Type == U64 )
				{
					if ( READY_SIZE - AmountReady < 20 ) { READY_FLUSH(); }
					uint64_t Num = *((uint64_t*)Parameter); Parameter += sizeof(uint64_t);
					AmountReady += UInt64ToString(Num, Ready + AmountReady);
				}
				else if ( Type == X32 )
				{
					if ( READY_SIZE - AmountReady < 8 ) { READY_FLUSH(); }
			 		uint32_t Num = *((uint32_t*)Parameter); Parameter += sizeof(uint32_t);
					AmountReady += UInt32ToString16(Num, Ready + AmountReady);
				}
				else if ( Type == X64 )
				{
					if ( READY_SIZE - AmountReady < 16 ) { READY_FLUSH(); }
			 		uint64_t Num = *((uint64_t*)Parameter); Parameter += sizeof(uint64_t);
					AmountReady += UInt64ToString16(Num, Ready + AmountReady);
				}
				else // Unsupported/unknown format. Just echo!
				{
					Ready[AmountReady] = *Format; AmountReady++;
				}

				Format += Len;
			}

			if ( READY_SIZE == AmountReady ) { READY_FLUSH(); }
		}

		// Flush our cache.
		if ( AmountReady ) { Ready[AmountReady] = 0; Print(Ready); }
	}
}
