/*
*	Example implementation of printf() type functions.
*
*	We assume that you have the following functions defined:
*
*	void UartPutc( char data );
*
*		Writes a character to the UART.
*
*	int	write( int file, const void *data, size_t dlen );
*
*		Writes 'dlen' bytes from 'data' to a file, whatever that is on
*		your system. The file is identified by the 'file' integer. This
*		is pretty much the same as the write() function on a POSIX system.
*
*	We then create the following functions:
*
*		printf() 		- with full float and wide character support
*		fprintf() 		- with float but not wide character support
*		vfprintf() 		- with float but not wide character support
*		snprintf()		- integer only
*
*	as an example. That should be enough for you to write pretty much
*	any function in the printf() family.
*
*	Please realise that while the example functions seem long, that's
*	due to the comments. In fact, the longest of them contains only
*	four C statements, and simple ones of that.
*/

#include <stdio.h>			// It's your header for the functions in this file
#include <files.h>			// Your header, declaring write()
#include <uart.h>			// Your header, declaring UartPutc()

#include <stdio_format.h>	// Library header, declaring print_XXX()
#include <wchar.h>			// Library header, defining wide character support
#include <string.h>			// Library header, defining memcpy()

#include <stdarg.h>			// GCC header, declaring the vararg macros

/*
*	Declaration of our local output functions
*/

static int uart_out( void **unused, const char *data, size_t len );
static int string_write( void **par, const char *str, size_t len );

/****************************/
/*			printf()		*/
/****************************/

/*
*	Print to the UART, support everything
*/

int printf( const char *fmt, ... )
{
va_list	arg;		// Variable argument list object
int		ret;		// Return value

	// Get the variable argument list first

	va_start( arg, fmt );

	// Call the library's formatting function, passing it everything and
	// returning with whatever that returned to us.

	ret = print_flt(	// We need to call _flt() for float support
		fmt,			// First argument is the format string
		arg,			// Next the variable argument list
		-1,				// No limit on output amount
		uart_out,		// The wrapper function around UartPutc()
		NULL,			// No parameter for the wrapper
		wcitomb );		// Function to transform a wide char to UTF-8

	// We have to finish with the vararg.

	va_end( arg );
	return ret;
}

/*
*	This is the UART output wrapper
*/

static int uart_out( void **unused, const char *data, size_t len )
{
	// We do not use the first parameter

	(void) unused;

	// We have to output 'len' bytes from 'data' on the UART

	while ( len-- ) UartPutc( *data++ );

	// We have to return a positive number to indicate success

	return 1;
}

/****************************/
/*			fprintf()		*/
/****************************/

/*
*	Print to file, support everything but wide char
*/

int fprintf( int file, const char *format, ... )
{
va_list	args;
int		ret;

	// Get the argument list

	va_start( args, fmt );

	// This is an extremely simple case, as everything fits
	// the format function's requirements. The only thing is
	// that we have to cast write() to the relevant function
	// type and the file number to void **. The last parameter
	// is NULL, disabling wide character processing.

	ret = print_flt( format,				// Format string
					   args,			 	// Argument list
					   -1, 					// No limit on the amount of output
					   (BLC_FMT_PUT) write,	// The output function
					   (void **) file,		// Parameter to the output func
					   NULL 				// Wide chars are not supported
					);
	// Done

	va_end( arg );
	return ret;
}

/****************************/
/*			vfprintf()		*/
/****************************/

/*
*	Like above but with variable argument list passed as argument
*/

int vfprintf( int file, const char *format, va_list args )
{
	// This is even simpler than fprintf() because we already
	// have the va_list prepared.

	return( print_flt( format, args, -1, (BLC_FMT_PUT) write,
					   (void **) file, NULL ) );
}

/****************************/
/*			snprintf()		*/
/****************************/

/*
*	Print to a string, with size limit. Integers only.
*/

int snprintf( char *res, size_t len, const char *fmt, ... )
{
va_list	arg;
int		ret;

	// Get the variable argument list

	va_start( arg, fmt );

	// Call print_int() to do the work. The string_write() function
	// will be called with the address of the pointer to the result
	// string as its first argument.

	ret = print_int( fmt, arg, len, string_write, (void **) &res, NULL );

	// We have to put a closing '\0' at the end of the string, but only
	// if we have not yet exhausted the buffer and there was no error.
	// Note that string_write() advanced the 'res' pointer to point to
	// the byte after the last byte written by format_int().

	if ( ret >= 0 && ret < len ) *res = 0;

	// Our return value is whatever print_int() returned.

	va_end( arg );
	return( ret );
}

/*
*	Helper function to write into a string.
*
*	The first parameter is a pointer to a pointer to the next character
*	in the output buffer. This function does not need to check if it
*	exhausted the available buffer space or not, the format function
*	itself takes care of that.
*/

static int string_write( void **par, const char *str, size_t len )
{
char	*p;

	// Get the pointer to the next character of the string

	p = *par;

	// Copy the data into the output buffer. We use the fast version
	// of memcpy(), memcpy_forw() that is also defined in this library.

	memcpy_forw( p, str, len );

	// Store the new end of the string so that at the next
	// invocation we know wehere to write.

	*par = p + len;

	// Report success. Any positive number would do, but we
	// return the length, because that's what a normal write
	// function would do.

	return( len );
}

