/*
 * Copyright (c) 2015-2021, Renesas Electronics Corporation
 * All rights reserved.
 */

/******************************************************************************
 * @file          printf.c
 * - Version      : 0.01
 * @brief         Log output library.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 29.05.2020 0.01     First Release
 *****************************************************************************/

#include <stdarg.h>
#include <stdint.h>
#include "debug.h"
#include "scif.h"

/***********************************************************
 * tf_printf
 ***********************************************************/

static uint32_t uint32_print(uint32_t num)
{
    uint8_t  num_buf[10];
    uint32_t count = 0U;
    uint32_t i = 0U;
    uint32_t rem;
    uint32_t unum = num;

    while(1)
    {
        rem = unum % 10U;
        num_buf[i] = '0' + rem;

        i++;
        unum = unum / 10U;
        if (unum < 1U )
        {
            break;
        }
    }

    while (i != 0U)
    {
        i--;
        (void)console_putc(num_buf[i]);
        count++;
    };

    return count;
}
/* End of function uint32_print */

static uint32_t int32_print(int32_t num)
{
    uint32_t unum;
    uint32_t count = 0U;

    if (num < 0)
    {
        console_putc('-');
        count = 1U;
        unum = (uint32_t)-num;
    }
    else
    {
        unum = (uint32_t)num;
    }

    count += uint32_print(unum);

    return count;
}
/* End of function int32_print */

static uint32_t uint32_hex_print(uint32_t num)
{
    uint32_t i;
    uint32_t count = 0U;
    uint8_t  c;

    for (i = 0U; i < 8U; i++)
    {
        c = (uint8_t)((num >> ((7U - i) * 4)) & 0x0FU); /* 0-F */
        if ( c >= 0x0AU)
        {
            c += 'a' - 0x0AU;   /* A-F */
        }
        else
        {
            c += '0';       /* 0-9 */
        }
        (void)console_putc(c);
        count++;
    }

    return count;
}
/* End of function uint32_hex_print */

static uint32_t str_print(const char *str)
{
    uint32_t count = 0;

    while (*str)
    {
        (void)console_putc(*str);
        str++;
        count++;
    }

    return count;
}
/* End of function str_print */

void tf_printf(const char *fmt, ...)
{
    va_list  args;
    int32_t  num;
    uint32_t unum;
    char     *str;
    uint32_t count = 0U;

    va_start(args, fmt);
    while (*fmt)
    {
        if (*fmt == '%')
        {
            fmt++;
            switch (*fmt)
            {
                case 'i':
                case 'd':
                {
                    num = va_arg(args, int32_t);
                    count += int32_print(num);
                    break;
                }
                case 's':
                {
                    str = va_arg(args, char *);
                    count += str_print(str);
                    break;
                }
                case 'x':
                {
                    unum = va_arg(args, uint32_t);
                    count += uint32_hex_print(unum);
                    break;
                }
                case 'u':
                {
                    unum = va_arg(args, uint32_t);
                    count += uint32_print(unum);
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
        else
        {
            (void)console_putc(*fmt);
            count++;
        }
        fmt++;
    }
    va_end(args);
}
/* End of function tf_printf */

/***********************************************************
 * sprintf
 ***********************************************************/

static uint32_t uint32_sprint(uint32_t num, char *buf)
{
    uint8_t  num_buf[10];
    uint32_t count = 0U;
    uint32_t i = 0U;
    uint32_t rem;
    uint32_t unum = num;

    while(1)
    {
        rem = unum % 10U;
        num_buf[i] = '0' + rem;

        i++;
        unum = unum / 10U;
        if (unum < 1U )
        {
            break;
        }
    }

    while (i != 0U)
    {
        i--;
        *(buf + count) = num_buf[i];
        count++;
    };

    return count;
}
/* End of function uint32_sprint */

static uint32_t int32_sprint(int32_t num, char *buf)
{
    uint32_t unum;
    uint32_t count = 0U;

    if (num < 0)
    {
        *(buf + count) = '-';
        count = 1U;
        unum = (uint32_t)-num;
    }
    else
    {
        unum = (uint32_t)num;
    }

    count += uint32_sprint(unum, (buf + count));

    return count;
}
/* End of function int32_sprint */

static uint32_t uint32_hex_sprint(uint32_t num, char *buf)
{
    uint32_t i;
    uint32_t count = 0U;
    uint8_t  c;

    for (i = 0U; i < 8U; i++)
    {
        c = (uint8_t)((num >> ((7U - i) * 4)) & 0x0FU); /* 0-F */
        if ( c >= 0x0AU)
        {
            c += 'a' - 0x0AU;   /* A-F */
        }
        else 
        {
            c += '0';       /* 0-9 */
        }
        *(buf + count) = c;
        count++;
    }

    return count;
}
/* End of function uint32_hex_sprint */

static uint32_t str_sprint(const char *str, char *buf)
{
    uint32_t count = 0;

    while (*str)
    {
        *(buf + count) = *str;
        str++;
        count++;
    }

    return count;
}
/* End of function str_sprint */

int sprintf(char *buf, const char *fmt, ...)
{
    va_list  args;
    int32_t  num;
    uint32_t unum;
    char     *str;
    int32_t count = 0;

    va_start(args, fmt);

    if (fmt == NULL)
    {
        fmt = "(fmt null)\n";
    }

    while (*fmt)
    {
        if (*fmt == '%')
        {
            fmt++;
            switch (*fmt)
            {
                case 'i':
                case 'd':
                {
                    num = va_arg(args, int32_t);
                    count += int32_sprint(num, (buf + count));
                    break;
                }
                case 's':
                {
                    str = va_arg(args, char *);
                    count += str_sprint(str, (buf + count));
                    break;
                }
                case 'x':
                {
                    unum = va_arg(args, uint32_t);
                    count += uint32_hex_sprint(unum, (buf + count));
                    break;
                }
                case 'u':
                {
                    unum = va_arg(args, uint32_t);
                    count += uint32_sprint(unum, (buf + count));
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
        else
        {
            *(buf + count) = *fmt;
            count++;
        }
        fmt++;
    }
    *(buf + count) = '\0';
    va_end(args);

    return(count);
}
/* End of function sprintf */

