/** \file printf.c
 * Simplified printf() and sprintf() implementation - prints formatted string to
 * USART (or whereever). Most common % specifiers are supported. It costs you about
 * 3k FLASH memory - without floating point support it uses only 1k ROM!
 * \author Freddie Chopin, Martin Thomas, Marten Petschke and many others
 * \date 16.2.2012
 * reduced scanf() added by Andrzej Dworzynski on the base of reduced sscanf() written by
 * some nice gay from this post: http://www.fpgarelated.com/usenet/fpga/show/61760-1.php
 * thanks to everybody:)
 * \date 12.2.2013
 * http://www.skybeeper.com/index.php/en/english-en/25-tighten-scanf
 */

/******************************************************************************
* chip: STM32F10x
* compiler: arm-none-eabi-gcc 4.6.0
*
* global functions:
* 	int printf_(const char *format, ...)
* 	int sprintf_(char *buffer, const char *format, ...)
*	int rscanf(const char* format, ...)
*
* STM32 specific functions:
*   Usart1Init(void)
*	void Usart1Put(char);                // blocking put char, used by printf()
*	char Usart1Get(void); 				//used by rscanf()
* set up scanf buffer by changing scanf_buff_size variable

* local functions:
* 	int putc_strg(int character, printf_file_t *stream)
* 	int vfprintf_(printf_file_t *stream, const char *format, va_list arg)
* 	void long_itoa (long val, int radix, int len, vfprintf_stream *stream)
*	static int rsscanf(const char* str, const char* format, va_list ap)
******************************************************************************/

/*
+=============================================================================+
| includes
+=============================================================================+
*/

#include <stdarg.h>     // (...) parameter handling
#include <stdlib.h>     //NULL pointer definition

#include "stm32f10x.h"	// only this headerfile is used
#include "printf.h"
#include "usart1.h"

#define assert_param(expr) ((void)0) /*dummy to make the stm32 header work*/

/*
+=============================================================================+
| global declarations
+=============================================================================+
*/
//in file printf.h
/*
+=============================================================================+
| local declarations
+=============================================================================+
*/


static void putc_strg(char);          // the put() function for sprintf()
char *SPRINTF_buffer;          
int scanf_buff_size = 12;	//chang this if needed
static int vfprintf_(void (*) (char), const char *format, va_list arg); //generic print
static void long_itoa (long, int, int, void (*) (char)); //heavily used by printf
static int rsscanf(const char* str, const char* format, va_list ap);//used by rscanf
/*
+=============================================================================+
| sample main()  file
+=============================================================================+
*/
/*
int main(void)
{
	char my_buff[12];
    int i;
	
		Usart1Init();
		printf_(" USART1 Test\r\n");

	    printf_("Enter your family name: ");
	    rscanf ("%s",my_buff);
	    printf_("%s\r\n",my_buff);
	    printf_("Enter your age: ");
	    rscanf ("%d",&i);
	    printf_("%d\r\n",i);
	    printf_("Mr. %s, %d years old.\r\n",my_buff,i);
	    printf_ ("Enter a hexadecimal number:\r\n");
	    rscanf("%x",&i);
	    printf_("You have entered %x (%d).\r\n",i,i);
while(1);
return 0;
}


*/


/*
+=============================================================================+
| global functions
+=============================================================================+
*/
int printf_(const char *format, ...)
{
	va_list arg;

	va_start(arg, format);
	vfprintf_((&Usart1Put), format, arg);
	va_end(arg);

	return 0;
}

int sprintf_(char *buffer, const char *format, ...)
{
	va_list arg;

	SPRINTF_buffer=buffer;	 //Pointer auf einen String in Speicherzelle abspeichern

	va_start(arg, format);
	vfprintf_((&putc_strg), format, arg);
	va_end(arg);

	*SPRINTF_buffer ='\0';             // append end of string

	return 0;
}


//Reads data from usart1  and stores them according to parameter format
//into the locations given by the additional arguments, as if
// scanf was used

// Reduced version of scanf (%d, %x, %c, %n are supported)
// %d dec integer (E.g.: 12)
// %x hex integer (E.g.: 0xa0)
// %b bin integer (E.g.: b1010100010)
// %n hex, de or bin integer (e.g: 12, 0xa0, b1010100010)
// %c any character
//buffer support 12 bytes

int rscanf(const char* format, ...){
	va_list args;
	va_start( args, format );
	int count = 0;
	char ch = 0;
	char buffer[scanf_buff_size];

	SPRINTF_buffer = buffer;


	while(count =< scanf_buff_size )//get string
	{
	count++;
	ch = Usart1Get();
	if(ch != '\n' && ch != '\r') *SPRINTF_buffer++ = ch;
	else
	break;
	}
	*SPRINTF_buffer = '\0';//end of string

	SPRINTF_buffer = buffer;
	count =  rsscanf(SPRINTF_buffer, format, args);
	va_end(args);
	return count;

}

/**
 * @def debug(format...)
 * @brief prints the timestamp, file name, line number, printf-formated @a format string and the
 * optional parameters to stdout
 *
 * The output looks like this:<br>
 * <pre>
 * 12345     filename.c[123]: format string
 * ^    ^    ^          ^
 * |    |    |          line number
 * |    |    +--------- file name
 * |    +-------------- tab character
 * +------------------- timestamp (ms since reset)
 * </pre>
 *
 * */
//#define USE_DEBUG
#ifdef USE_DEBUG
	#define debug(format,...) {\
		printf_("%ul\t%s[%i]: ", millisec, __FILE__, __LINE__); /* print file name and line number */\
		printf_(format, ## __VA_ARGS__);               /* print format string and args */\
		printf_("\n"); \
	}
#else
	#define debug(format,...) ((void)0)
#endif /* USE_DEBUG */



/*
+=============================================================================+
| local functions
+=============================================================================+
*/
// putc_strg() is the putc()function for sprintf_()
static void putc_strg(char character)
{
	*SPRINTF_buffer = (char)character;	// just add the character to buffer
	 SPRINTF_buffer++;

}

/*--------------------------------------------------------------------------------+
 * vfprintf_()
 * Prints a string to stream. Supports %s, %c, %d, %ld %ul %02d %i %x  %lud  and %%
 *     - partly supported: long long, float (%l %f, %F, %2.2f)
 *     - not supported: double float and exponent (%e %g %p %o \t)
 *--------------------------------------------------------------------------------+
*/
static int vfprintf_(void (*putc)(char), const char* str,  va_list arp)
{
	int d, r, w, s, l;  //d=char, r = radix, w = width, s=zeros, l=long
	char *c;            // for the while loop only
	//const char* p;
#ifdef INCLUDE_FLOAT
	float f;
	long int m, w2;
#endif



	while ((d = *str++) != 0) {
		if (d != '%') {//if it is not format qualifier
			(*putc)(d);
			continue;//get out of while loop
		}
		d = *str++;//if it is '%'get next char
		w = r = s = l = 0;
		if (d == '%') {//if it is % print silmpy %
			(*putc)(d);
			d = *str++;
		}
		if (d == '0') {
			d = *str++; s = 1;  //padd with zeros
		}
		while ((d >= '0')&&(d <= '9')) {
			w += w * 10 + (d - '0');
			d = *str++;
		}
		if (s) w = -w;      //padd with zeros if negative

	#ifdef INCLUDE_FLOAT
		w2 = 0;
		if (d == '.')
			d = *str++;
		while ((d >= '0')&&(d <= '9')) {
			w2 += w2 * 10 + (d - '0');
			d = *str++;
		}
	#endif

		if (d == 's') {// if string
			c = va_arg(arp, char*); //get buffer addres
			//p = c;//debug
			while (*c)
				(*putc)(*(c++));//write the buffer out
			continue;
		}


		//debug

			//while(*p) Usart1Put(*p++);

		if (d == 'c') {
			(*putc)((char)va_arg(arp, int));
			continue;
		}
		if (d == 'u') {     // %ul
			r = 10;
			d = *str++;
		}
		if (d == 'l') {     // long =32bit
			l = 1;
			if (r==0) r = -10;
			d = *str++;
		}
//		if (!d) break;
		if (d == 'u') r = 10;//     %lu,    %llu
		else if (d == 'd' || d == 'i') {if (r==0) r = -10;}  //can be 16 or 32bit int
		else if (d == 'X' || d == 'x') r = 16;               // 'x' added by mthomas
		else if (d == 'b') r = 2;
		else str--;                                         // normal character

	#ifdef INCLUDE_FLOAT
		if (d == 'f' || d == 'F') {
			f=va_arg(arp, double);
			if (f>0) {
				r=10;
				m=(int)f;
			}
			else {
				r=-10;
				f=-f;
				m=(int)(f);
			}
			long_itoa(m, r, w, (putc));
			f=f-m; m=f*(10^w2); w2=-w2;
			long_itoa(m, r, w2, (putc));
			l=3; //do not continue with long
		}
	#endif

		if (!r) continue;  //
		if (l==0) {
			if (r > 0){      //unsigned
				long_itoa((unsigned long)va_arg(arp, int), r, w, (putc)); //needed for 16bit int, no harm to 32bit int
			}
			else            //signed
				long_itoa((long)va_arg(arp, int), r, w, (putc));
		} else if (l==1){  // long =32bit
				long_itoa((long)va_arg(arp, long), r, w, (putc));        //no matter if signed or unsigned
		}
	}

	return 0;
}


static void long_itoa (long val, int radix, int len, void (*putc) (char))
{
	char c, sgn = 0, pad = ' ';
	char s[20];
	int  i = 0;


	if (radix < 0) {
		radix = -radix;
		if (val < 0) {
			val = -val;
			sgn = '-';
		}
	}
	if (len < 0) {
		len = -len;
		pad = '0';
	}
	if (len > 20) return;
	do {
		c = (char)((unsigned long)val % radix); //cast!
		if (c >= 10) c += ('A'-10); //ABCDEF
		else c += '0';            //0123456789
		s[i++] = c;
		val = (unsigned long)val /radix; //cast!
	} while (val);
	if (sgn) s[i++] = sgn;
	while (i < len)
		s[i++] = pad;
	do
		(*putc)(s[--i]);
	while (i);
}





//
// Reduced version of sscanf (%d, %x, %c, %n are supported)
// %d dec integer (E.g.: 12)
// %x hex integer (E.g.: 0xa0)
// %b bin integer (E.g.: b1010100010)
// %n hex, de or bin integer (e.g: 12, 0xa0, b1010100010)
// %c any character
//

static int rsscanf(const char* str, const char* format, va_list ap)
{
	//va_list ap;
	int value, tmp;
	int count;
	int pos;
	char neg, fmt_code;
	const char* pf;
	char* sval;
	//va_start(ap, format);

	for (pf = format, count = 0; *format != 0 && *str != 0; format++, str++)
	{
		while (*format == ' ' && *format != 0) format++;//
		if (*format == 0) break;

		while (*str == ' ' && *str != 0) str++;//increment pointer of input string
		if (*str == 0) break;

		if (*format == '%')//recognize how to format
		{
			format++;
			if (*format == 'n')
			{
                if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))//if in str sth like 0xff
                {
                    fmt_code = 'x';
                    str += 2;
                }
                else
                if (str[0] == 'b')
                {
                    fmt_code = 'b';
                    str++;
                }
                else
                    fmt_code = 'd';
			}
			else
				fmt_code = *format; //it is format letter

			switch (fmt_code)
			{
			case 'x':
			case 'X':
				for (value = 0, pos = 0; *str != 0; str++, pos++)
				{
					if ('0' <= *str && *str <= '9')
						tmp = *str - '0';
					else
					if ('a' <= *str && *str <= 'f')
						tmp = *str - 'a' + 10;
					else
					if ('A' <= *str && *str <= 'F')
						tmp = *str - 'A' + 10;
					else
						break;

					value *= 16;
					value += tmp;
				}
				if (pos == 0)
					return count;
				*(va_arg(ap, int*)) = value;
				count++;
				break;

            case 'b':
				for (value = 0, pos = 0; *str != 0; str++, pos++)
				{
					if (*str != '0' && *str != '1')
                        break;
					value *= 2;
					value += *str - '0';
				}
				if (pos == 0)
					return count;
				*(va_arg(ap, int*)) = value;
				count++;
				break;

			case 'd':
				if (*str == '-')
				{
					neg = 1;
					str++;
				}
				else
					neg = 0;
				for (value = 0, pos = 0; *str != 0; str++, pos++)
				{
					if ('0' <= *str && *str <= '9')
						value = value*10 + (int)(*str - '0');
					else
						break;
				}
				if (pos == 0)
					return count;
				*(va_arg(ap, int*)) = neg ? -value : value;
				count++;
				break;

			case 'c':
				*(va_arg(ap, char*)) = *str;
				count++;
				break;
			case 's':
				sval = va_arg(ap, char*);


				while(*str){
				 *sval++ = *str++;
				count++;
				}
				*sval = NULL;

				break;

			default:
				return count;
			}
		}
		else
		{
			if (*format != *str)//
				break;
		}
	}



	return count;
}
/******************************************************************************
* END OF FILE
******************************************************************************/