	/**
 * @file output.c
 * @brief Output functions
 * 
 */

#include "io.h"

/**
 * @brief Put one char to console
 * @param chr - char to put
 * @return number of written characters
 * 
 */
unsigned int putc (const char chr){
	def_putc(chr);
	return 1;
}

/**
 * @brief Put null terminated string to console
 * @param str - pointer to null terminatted string
 * @return number of written characters
 * 
 */
unsigned int puts (const char * str){
	int i;
	for (i=0; *str; i++) {
		def_putc(*str++);
	}
	return i;
}

/**
 * @brief Put formated string to the console
 * @param format - format null terminated format string 
 * @param ... - arguments
 * 	 %c for characters, %s for strings, %d, %u, %i for 
 *   integers, %x for integers in hexadecimal format, %p pointers without 
 *   modifier for aligning and valid digits.
 * @return number of written characters
 * 
 */
unsigned int printk (const char* format, ...) {
	/* unwind the arguments (if any) */
	return printk_unwind(format, (void*)(&format+1));	
}

/**
 * @brief Put formated string to the console
 * @param format - formated null terminated format string
 * @param arguments - void pointer to arguments stored in the memory "in the line"
 * @return number of written characters
 * 
 * !note! this function is used e.g. in debug.c, see function panic
 * 
 */
unsigned int printk_unwind (const char * format, void* arguments){
	static char number[MAX_NUM_LENGTH];
	static char int_to_hexa[] = "0123456789ABCDEF";
	int num_length = 0;
	int format_character_start = 0;
	char *char_arg;
	int int_arg;
	int output_chars_count = 0;

	while (!(*format == '\0')) {		
		if (format_character_start) {
			switch (*format) {
				case 'c':
					int_arg = (*(int*)arguments);
					output_chars_count+=putc(int_arg);
					output_chars_count++;
					arguments += sizeof(int);
					break;
				case 's':
				 	char_arg = *((char**)arguments);
				 	while (*char_arg != '\0') {
				 		output_chars_count+=putc(*char_arg);
				 		*char_arg++;
				 	}
				 	arguments += sizeof(char*);
				 	break;
				case 'd':
					int_arg = (*(int*)arguments);
					num_length = 0;
					if (int_arg < 0) {
						output_chars_count+=putc('-');
						int_arg = -int_arg;
					}
					if (int_arg == 0) {
						output_chars_count+=putc('0');
					}
					if (int_arg >0) {
						while (int_arg > 0) {
							number[num_length++] = (int_arg % 10) + '0'; //modulo 10
							int_arg /= 10;	//div 10
						}
						while (num_length-- > 0) 
							output_chars_count+=putc(number[num_length]);
					}					 	
				 	arguments += sizeof(int);
				 	break;
				case 'x':
					num_length = 0;
					int_arg = (*(int*)arguments);
					if (int_arg < 0) {
						output_chars_count+=putc('-');
						int_arg = -int_arg;
					}
					puts("0x");
					output_chars_count+=2;
					if (int_arg == 0) {
						output_chars_count+=putc('0');
					}
					if (int_arg > 0) {
						while (int_arg > 0) {
							number[num_length++] = int_to_hexa[int_arg % 16];
							int_arg /= 16;
						}
						while (num_length-- > 0) 
							output_chars_count+=putc(number[num_length]);
					}
					arguments += sizeof(int);
					break;
				case 'p':
					num_length = 0;
					int_arg = (*(unsigned int*)arguments);
					if (int_arg == 0) {
						output_chars_count+=puts("(nil)");
					} else {
						
						if (int_arg < 0) {
							output_chars_count+=putc('-');
							int_arg = -int_arg;
						}
						
						puts("0x");
						output_chars_count+=2;
						if (int_arg > 0) {
							while (int_arg > 0) {
								number[num_length++] = int_to_hexa[int_arg % 16];
								int_arg /= 16;
							}
							while (num_length < POINTER_PRINT_LENGTH) {
								number[num_length++] = '0';
							}
							while (num_length-- > 0) 
								output_chars_count+=putc(number[num_length]);
						}
					}
					arguments += sizeof(unsigned int);
					break;
				case '%':
					output_chars_count += putc('%');
					break;
				default:
					output_chars_count += putc(*format);
			} //end switch
			format_character_start = 0;
			format++;
		} else {
			format_character_start = (*format == '%') ? 1: 0;
			if (!format_character_start)
				output_chars_count += putc(*format);
			format++;
		}
	}
	return output_chars_count;
}

