#include "printf.h"



static void StrPrvPrintfEx_number(unsigned long number,unsigned base,char zeroExtend,char isSigned,unsigned padToLength,char caps){
	
	char buf[64];
	unsigned idx = sizeof(buf) - 1;
	unsigned chr, i;
	char neg = 0;
	unsigned numPrinted = 0;
	
	if(padToLength > 63) padToLength = 63;
	
	buf[idx--] = 0;	//terminate
	
	if(isSigned){
		
		if(number & 0x80000000ULL){
			
			neg = 1;
			number = -number;
		}
	}
	
	do{
		if(base == 16){
			chr = number & 0x0F;
			number >>= 4;
		}
		else if(base == 2){
			chr = number & 1;
			number >>= 1;
		}
		else{
			chr = number % base;
			number = number / base;
		}
		
		buf[idx--] = (chr >= 10)?(chr + (caps ? 'A' : 'a') - 10):(chr + '0');
		
		numPrinted++;
		
	}while(number);
	
	if(neg){
		
		buf[idx--] = '-';
		numPrinted++;
	}
	
	if(padToLength > numPrinted){
		
		padToLength -= numPrinted;
	}
	else{
		
		padToLength = 0;
	}
	
	while(padToLength--){
		
		buf[idx--] = zeroExtend?'0':' ';
		numPrinted++;
	}
	
	idx++;
	
	for(i = 0; i < numPrinted; i++)
		prPutchar((buf + idx)[i]);
}

static unsigned StrVPrintf_StrLen_withMax(const char* s,unsigned max){
	
	unsigned len = 0;
	
	while((*s++) && (len < max)) len++;
	
	return len;
}

static unsigned StrVPrintf_StrLen(const char* s){
	
	unsigned len = 0;
	
	while(*s++) len++;
	
	return len;
}

static inline char prvGetChar(const char** fmtP){
	
	char ret;
	
	const char* fmt;
		
	fmt = *fmtP;
	ret = *fmt++;
	*fmtP = fmt;
	
	return ret;
}

static void cvsprintf(const char* fmtStr,va_list vl){
	
	char c, t;
	unsigned long val64;

	while((c = prvGetChar(&fmtStr)) != 0){
		
		if(c == '\n'){
			
			prPutchar(c);
		}
		else if(c == '%'){
			
			char zeroExtend = 0, useLong = 0, caps = 0;
			unsigned padToLength = 0,len,i;
			const char* str;
			
more_fmt:
			
			c = prvGetChar(&fmtStr);
			
			switch(c){
				
				case '%':
					
					prPutchar(c);
					break;
				
				case 'c':
					
					t = va_arg(vl,unsigned int);
					prPutchar(t);
					break;
				
				case 's':
					
					str = va_arg(vl,char*);
					if(!str) str = "(null)";
					if(padToLength){
						
						len = StrVPrintf_StrLen_withMax(str,padToLength);
					}
					else{
						
						padToLength = len = StrVPrintf_StrLen(str);
					}
					if(len > padToLength) len = padToLength;
					else{
						
						for(i=len;i<padToLength;i++) prPutchar(' ');
					}
					for(i = 0; i < len; i++)
						prPutchar(*str++);
					
					break;
				
				case '0':
					
					if(!zeroExtend && !padToLength){
						
						zeroExtend = 1;
						goto more_fmt;
					}
				
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					
					padToLength = (padToLength * 10) + c - '0';
					goto more_fmt;
				
				case 'u':
					
					val64 = useLong ? va_arg(vl,unsigned long) : va_arg(vl,unsigned);
					StrPrvPrintfEx_number(val64,10,zeroExtend,0,padToLength,0);
					break;
					
				case 'd':
					
					val64 = useLong ? va_arg(vl,unsigned long) : (signed long)va_arg(vl,signed);
					StrPrvPrintfEx_number(val64,10,zeroExtend,1,padToLength,0);
					break;
					
				case 'X':
					caps = 1;
					
				case 'x':
					
					val64 = useLong ? va_arg(vl,unsigned long) : va_arg(vl,unsigned);
					StrPrvPrintfEx_number(val64,16,zeroExtend,0,padToLength,caps);
					break;
					
				case 'b':
					
					val64 = useLong ? va_arg(vl,unsigned long) : va_arg(vl,unsigned);
					StrPrvPrintfEx_number(val64,2,zeroExtend,0,padToLength,0);
					break;
					
				case 'L':
				case 'l':
					useLong = 1;
					goto more_fmt;
				
				default:
					
					prPutchar(c);
					break;
				
			}
		}
		else{
			
			prPutchar(c);
		}
	}
}

void pr(const char *fmt, ...)
{
	va_list vl;
	
	va_start(vl, fmt);
	cvsprintf(fmt, vl);
	va_end(vl);
}
