/*
 * @Author: jiejie
 * @Github: https://github.com/jiejieTop
 * @Date: 2019-12-25 23:54:19
 * @LastEditTime: 2020-06-17 15:23:09
 * @Description: the code belongs to jiejie, please keep the author insalof_formation and source code according to the license.
 */
#include "format.h"
#include "salof_defconfig.h"

#ifdef SALOF_USING_LOG

static int _get_atoi(const char **str)
{
    int n;
    for (n = 0; is_digit(**str); (*str)++)
        n = n * 10 + **str - '0';
    return n;
}

static void _buff_put_char(char *buf, unsigned int *pos, unsigned int max, char c)
{
    if (*pos < max)
        buf[(*pos)] = c;
    (*pos)++;
}


/**
 * Formats an integer number
 *  buf - buffer to print into
 *  len - current position in buffer
 *  maxlen - last valid position in buf
 *  num - number to print
 *  base - it's base
 *  width - how many spaces this should have; padding
 *  flags - above F flags
 */
static void _salof_format_int(char *buf, unsigned int *len, unsigned int maxlen,
        signed long long num, int base, int width, int flags)
{
    char nbuf[64], sign = 0;
    char altb[8]; // small buf for sign and #
    int n = num;
    int npad;           // number of pads
    char pchar = ' ';   // padding character
    char *digits = "0123456789ABCDEF";
    char *ldigits = "0123456789abcdef";
    int i, j;

    if (base < 2 || base > 16)
        return;
    if (flags & F_SMALL) digits = ldigits;
    if (flags & F_LEFT) flags &= ~F_ZEROPAD;

    if ((flags & F_SIGNED) && num < 0) {
        n = -num;
        sign = '-';
    } else if (flags & F_PLUS) {
        sign = '+';
    } else if (flags & F_SPACE)
        sign = ' ';

    i = 0;
    do {
        nbuf[i++] = digits[n % base];
        n = n / base;
    } while (n > 0);
   
    j = 0;
    if (sign) altb[j++] = sign;
    if (flags & F_ALTERNATE) {
        if (base == 8 || base == 16) {
            altb[j++] = '0';
            if (base == 16)
                altb[j++] = (flags & F_SMALL) ? 'x' : 'X';
        }
    }
    altb[j] = 0;

    npad = width > i + j ? width - i - j : 0;

    if (width > i + j)
        npad = width - i - j;

    if (npad > 0 && ((flags & F_LEFT) == 0)) {
        if (flags & F_ZEROPAD) {
            for (j = 0; altb[j]; j++)
                _buff_put_char(buf, len, maxlen, altb[j]);
            altb[0] = 0;
        }
        while (npad-- > 0)
            _buff_put_char(buf, len, maxlen, (flags & F_ZEROPAD) ? '0' : ' ');
    }
    for (j = 0; altb[j]; j++)
        _buff_put_char(buf, len, maxlen, altb[j]);

    while (i-- > 0)
        _buff_put_char(buf, len, maxlen, nbuf[i]);

    if (npad > 0 && (flags & F_LEFT))
        while(npad-- > 0)
            _buff_put_char(buf, len, maxlen, pchar);
}

static void _salof_format_char(char *buf, unsigned int *pos, unsigned int max, char c,
        int width, int flags)
{
    int npad = 0;
    if (width > 0) npad = width - 1;
    if (npad < 0) npad = 0;

    if (npad && ((flags & F_LEFT) == 0))
        while (npad-- > 0)
            _buff_put_char(buf, pos, max, ' ');

    _buff_put_char(buf, pos, max, c);

    if (npad && (flags & F_LEFT))
        while (npad-- > 0)
            _buff_put_char(buf, pos, max, ' ');
}

/**
 * strlen()
 */
static unsigned int _str_len(char *s)
{
    unsigned int i;
    for (i = 0; *s; i++, s++)
        ;
    return i;
}

static void _salof_format_str(char *buf, unsigned int *pos, unsigned int max, char *s,
        int width, int flags)
{
    int npad = 0;
    if (width > 0) npad = width - _str_len(s);
    if (npad < 0) npad = 0;

    if (npad && ((flags & F_LEFT) == 0))
        while (npad-- > 0)
            _buff_put_char(buf, pos, max, ' ');

    while (*s)
        _buff_put_char(buf, pos, max, *s++);

    if (npad && (flags & F_LEFT))
        while (npad-- > 0)
            _buff_put_char(buf, pos, max, ' ');
}




/***********************************************************************************************************************/

/**
 * Shrinked down, vsnprintf implementation.
 *  This will not handle floating numbers (yet).
 */
int salof_format_nstr(char *buf, unsigned int size, const char *fmt, va_list ap)
{
    unsigned int n = 0;
    char c, *s;
    char state = 0;
    signed long long num;
    int base;
    int flags, width, precision, lflags;

    if (!buf) size = 0;

    for (;;) {
        c = *fmt++;
        if (state == S_DEFAULT) {
            if (c == '%') {
                state = S_FLAGS;
                flags = 0;
            } else {
                _buff_put_char(buf, &n, size, c);
            }
        } else if (state == S_FLAGS) {
            switch (c) {
                case '#': flags |= F_ALTERNATE; break;
                case '0': flags |= F_ZEROPAD; break;
                case '-': flags |= F_LEFT; break;
                case ' ': flags |= F_SPACE; break;
                case '+': flags |= F_PLUS; break;
                case '\'':
                case 'I' : break; // not yet used
                default: fmt--; width = 0; state = S_WIDTH;
            }
        } else if (state == S_WIDTH) {
            if (c == '*') {
                width = va_arg(ap, int);
                if (width < 0) {
                    width = -width;
                    flags |= F_LEFT;
                }
            } else if (is_digit(c) && c > '0') {
                fmt--;
                width = _get_atoi(&fmt);
            } else {
                fmt--;
                precision = -1;
                state = S_PRECIS;
            }
        } else if (state == S_PRECIS) {
            // Ignored for now, but skip it
            if (c == '.') {
                if (is_digit(*fmt))
                    precision = _get_atoi(&fmt);
                else if (*fmt == '*')
                    precision = va_arg(ap, int);
                precision = precision < 0 ? 0 : precision;
            } else
                fmt--;
            lflags = 0;
            state = S_LENGTH;
        } else if (state == S_LENGTH) {
            switch(c) {
                case 'h': lflags = lflags == L_CHAR ? L_SHORT : L_CHAR; break;
                case 'l': lflags = lflags == L_LONG ? L_LLONG : L_LONG; break;
                case 'L': lflags = L_DOUBLE; break;
                default: fmt--; state = S_CONV;
            }
        } else if (state == S_CONV) {
            if (c == 'd' || c == 'i' || c == 'o' || c == 'b' || c == 'u' 
                    || c == 'x' || c == 'X') {
                if (lflags == L_LONG)
                    num = va_arg(ap, int);
                else if (lflags & (L_LLONG | L_DOUBLE))
                    num = va_arg(ap, signed long long);
                else if (c == 'd' || c == 'i')
                    num = va_arg(ap, int);
                else
                    num = (unsigned int) va_arg(ap, int);

                base = 10;
                if (c == 'd' || c == 'i') {
                    flags |= F_SIGNED;
                } else if (c == 'x' || c == 'X') {
                    flags |= c == 'x' ? F_SMALL : 0;
                    base = 16;
                } else if (c == 'o') {
                    base = 8;
                } else if (c == 'b') {
                    base = 2;
                }
                _salof_format_int(buf, &n, size, num, base, width, flags);
            } else if (c == 'p') {
                num = (size_t) va_arg(ap, void *);
                base = 16;
                flags |= F_SMALL | F_ALTERNATE;
                _salof_format_int(buf, &n, size, num, base, width, flags);
            } else if (c == 's') {
                s = va_arg(ap, char *);
                if (!s)
                    s = "(null)";
                _salof_format_str(buf, &n, size, s, width, flags);
            } else if (c == 'c') {
                c = va_arg(ap, int);
                _salof_format_char(buf, &n, size, c, width, flags);
            } else if (c == '%') {
                _buff_put_char(buf, &n, size, c);
            } else {
                _buff_put_char(buf, &n, size, '%');
                _buff_put_char(buf, &n, size, c);
            }
            state = S_DEFAULT;
        }
        if (c == 0)
            break;
    }
    n--;
    if (n < size)
        buf[n] = 0;
    else if (size > 0)
        buf[size - 1] = 0;

    return n;
}

#endif


