#pragma once

#ifndef print_h_
#define print_h_

#ifdef HAVE_CXX11_HEADERS

template <typename T>
struct binwrap
{
    T data;
};
template <typename T>
static FORCE_INLINE constexpr binwrap<T> bin(T x)
{
    return binwrap<T>{x};
}
template <typename T>
struct octwrap
{
    T data;
};
template <typename T>
static FORCE_INLINE constexpr octwrap<T> oct(T x)
{
    return octwrap<T>{x};
}
template <typename T>
struct hexwrap
{
    T data;
};
template <typename T>
static FORCE_INLINE constexpr hexwrap<T> hex(T x)
{
    return hexwrap<T>{x};
}
template <typename T>
struct HEXwrap
{
    T data;
};
template <typename T>
static FORCE_INLINE constexpr HEXwrap<T> HEX(T x)
{
    return HEXwrap<T>{x};
}

FORCE_INLINE constexpr size_t buildprintpiece(void)
{
    return 0;
}

template<typename T>
enable_if_t<is_integral<T>::value, size_t> buildprintpiece(T x)
{
    // log_10(2^x) = x * log(2)/log(10) ~= x * 0.30103
    size_t constexpr numdigits = (sizeof(T) * CHAR_BIT + 1) / 3;
    char str[numdigits + 2]; // +2 for '-', '\0'

    char * strptr = str;

    size_t totalChars;
    if (x < 0)
    {
        size_t numChars = logbasenegative<10>(x);
        totalChars = numChars+1;
        *strptr++ = '-';
        strptr[numChars] = '\0';
        do
        {
            DivResult<T> const qr = divrhs<10>(x);
            x = qr.q;
            strptr[numChars-1] = '0' - (char)qr.r;
        }
        while (--numChars);
    }
    else
    {
        size_t numChars = logbase<10>(x);
        totalChars = numChars;
        strptr[numChars] = '\0';
        do
        {
            DivResult<T> const qr = divrhs<10>(x);
            x = qr.q;
            strptr[numChars-1] = '0' + (char)qr.r;
        }
        while (--numChars);
    }

    initputs(str);
    return totalChars;
}

template<typename T>
enable_if_t<is_integral<T>::value, size_t> buildprintpiece(binwrap<T> x)
{
    make_unsigned_t<T> const data = x.data;

    int constexpr numChars = sizeof(x)*CHAR_BIT;
    char str[numChars+1];
    str[numChars] = '\0';

    for (int p = 0; p < numChars; ++p)
        str[numChars - 1 - p] = '0' + (char)((data >> p) & 1);

    initputs(str);
    return numChars;
}

template<typename T>
enable_if_t<is_integral<T>::value, size_t> buildprintpiece(octwrap<T> x)
{
    make_unsigned_t<T> const data = x.data;

    int constexpr numChars = (sizeof(x)*CHAR_BIT + 2) / 3;
    char str[numChars+1];
    str[numChars] = '\0';

    for (int p = 0; p < numChars; ++p)
        str[numChars - 1 - p] = '0' + (char)((data >> (p*3)) & 7);

    initputs(str);
    return numChars;
}

template<typename T>
enable_if_t<is_integral<T>::value, size_t> buildprintpiece(hexwrap<T> x)
{
    static char const hexletters[] = "0123456789abcdef";

    make_unsigned_t<T> const data = x.data;

    int constexpr numChars = (sizeof(x)*CHAR_BIT + 3) >> 2;
    char str[numChars+1];
    str[numChars] = '\0';

    for (int p = 0; p < numChars; ++p)
        str[numChars - 1 - p] = hexletters[(int)((data >> (p<<2)) & 0xF)];

    initputs(str);
    return numChars;
}

template<typename T>
enable_if_t<is_integral<T>::value, size_t> buildprintpiece(HEXwrap<T> x)
{
    static char const HEXletters[] = "0123456789ABCDEF";

    make_unsigned_t<T> const data = x.data;

    int constexpr numChars = (sizeof(x)*CHAR_BIT + 3) >> 2;
    char str[numChars+1];
    str[numChars] = '\0';

    for (int p = 0; p < numChars; ++p)
        str[numChars - 1 - p] = HEXletters[(int)((data >> (p<<2)) & 0xF)];

    initputs(str);
    return numChars;
}

template <typename T>
FORCE_INLINE size_t buildprintpiece(const T * x)
{
    return buildprintpiece(hex((uintptr_t)x));
}

FORCE_INLINE size_t buildprintpiece(char const *str)
{
    initputs(str);
    return strlen(str);
}

template<typename T>
static FORCE_INLINE size_t buildprint(T first)
{
    return buildprintpiece(first);
}

template<typename T, typename... Args>
size_t buildprint(T first, Args... args)
{
    size_t const len = buildprintpiece(first);
    return len + buildprint(args...);
}

// this file is incomplete. a fuller implementation exists but has not been completed and debugged.

#endif

#endif // print_h_