/** @file
 * IPRT / No-CRT - Minimal C++ ostream header.
 */

/*
 * Copyright (C) 2022-2023 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
 * in the VirtualBox distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 *
 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
 */

#ifndef VBOX_INCLUDED_SRC_nocrt_ostream
#define VBOX_INCLUDED_SRC_nocrt_ostream
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

/* Currently all in the ios header. */
#include <iprt/nocrt/ios>

namespace std
{
    /**
     * Basic output stream.
     */
    template<typename a_CharType, typename a_CharTraits /*= std::char_traits<a_CharType>*/ >
    class basic_ostream : public basic_ios<a_CharType, a_CharTraits>
    {
    protected:
        /** Sentry class that performs pre and post output work. */
        class sentry
        {
        private:
            basic_ostream &m_rParent;

        public:
            explicit sentry(basic_ostream &a_rParent)
                : m_rParent(a_rParent)
            {
                if (a_rParent.good())
                {
                    basic_ostream *pTiedStream = a_rParent.tie();
                    if (!pTiedStream)
                    { /* likely? */ }
                    else
                    {
                        pTiedStream->flush();
                        if (!pTiedStream->good())
                            a_rParent.setstate(failbit);
                    }
                }
            }

            explicit operator bool() const
            {
                return m_rParent.good();
            }

            ~sentry()
            {
                if (   (m_rParent.flags() & std::ios_base::unitbuf)
                    && m_rParent.good())
                    m_rParent.rdbuf()->pubsync();
            }
        };

    public:
        explicit basic_ostream(std::basic_streambuf<a_CharType,a_CharTraits> *a_pBuf)
            : basic_ios(a_pBuf)
        { }

        /** For cerr initialization.
         * @internal */
        explicit basic_ostream(std::basic_streambuf<a_CharType,a_CharTraits> *a_pBuf,
                               std::basic_ostream<a_CharType, a_CharTraits> *a_pTiedStream,
                               bool a_fUnbuffered)
            : basic_ios(a_pBuf)
        {
            m_pTiedStream = a_pTiedStream;
            if (!a_fUnbuffered)
                setf(std::ios_base::unitbuf);
        }

    private:
        basic_ostream(basic_ostream const &a_rSrc);             /* not copyable */
        basic_ostream &operator=(basic_ostream const &a_rSrc);  /* not copyable */

    public:
        virtual ~basic_ostream()
        {
        }

    public:
        basic_ostream &put(char_type a_ch)
        {
            sentry PrePost(*this);
            if (PrePost)
            {
                if (m_pBuf->sputc(a_ch) == traits_type::eof())
                    m_fState |= badbit;
            }
            return *this;
        }

        basic_ostream &write(const char_type *a_pchSrc, std::streamsize a_cchToWrite)
        {
            sentry PrePost(*this);
            if (PrePost)
            {
                std::streamsize cchWritten = m_pBuf->sputn(a_pchSrc, a_cchToWrite);
                if (cchWritten != a_cchToWrite)
                    m_fState |= badbit;
            }
            return *this;
        }

        basic_ostream &flush()
        {
            if (m_pBuf)
                m_pBuf->pubsync();
            return *this;
        }

        pos_type       tellp() RT_NOEXCEPT;
        basic_ostream &seekp(pos_type a_off) RT_NOEXCEPT;
        basic_ostream &seekp(off_type a_off, seekdir enmDir) RT_NOEXCEPT;

        /** @name Internal support methods
         * @{ */
        inline basic_ostream &intWrite(const char *a_pchSrc, std::streamsize a_cchToWrite); /**< Internal method outputting char buffers. */

        /** @returns 8, 10 or 16. */
        inline unsigned intGetIntegerBase() const RT_NOEXCEPT
        {
            switch (m_fFlags & basefield)
            {
                default:
                case dec: return 10;
                case hex: return 16;
                case oct: return 8;
            }
        }

        /** @returns RTSTR_F_XXX . */
        inline unsigned intGetIntegerFlags() const RT_NOEXCEPT
        {
            unsigned fFlags = 0;
            if (m_fFlags & uppercase)
                fFlags |= RTSTR_F_CAPITAL;
            if (m_fFlags & showbase)
                fFlags |= RTSTR_F_SPECIAL;
            if (m_fFlags & showpos)
                fFlags |= RTSTR_F_PLUS;
            return fFlags;
        }

        basic_ostream &formatInteger(uint64_t a_uValue, uint32_t a_fFlags, unsigned a_uBase = 0)
        {
            a_fFlags |= intGetIntegerFlags();
            char szTmp[72];
            int  cchTmp = RTStrFormatNumber(szTmp, a_uValue, !a_uBase ? intGetIntegerBase() : a_uBase, 0, 0, a_fFlags);

            /** @todo apply cchWidth and padding.  */

            return intWrite(szTmp, cchTmp);
        }

        /** @} */
    };

    /** @name Character and string output.
     * @{ */
    /** @todo not sure if this really works...   */
    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, char a_ch)
    {
        return a_rDst.put(a_ch);
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, const char *a_psz)
    {
        return a_rDst.intWrite(a_psz, strlen(a_psz));
    }
    /** @} */

    /** @name Integer formatting.
     * @{ */
    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, signed char a_iValue)
    {
        return a_rDst.formatInteger(a_iValue, RTSTR_F_8BIT | RTSTR_F_VALSIGNED);
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, unsigned char a_uValue)
    {
        return a_rDst.formatInteger(a_uValue, RTSTR_F_8BIT);
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, short a_iValue)
    {
        return a_rDst.formatInteger(a_iValue, RTSTR_F_16BIT | RTSTR_F_VALSIGNED);
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, unsigned short a_uValue)
    {
        return a_rDst.formatInteger(a_uValue, RTSTR_F_16BIT);
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, int a_iValue)
    {
        return a_rDst.formatInteger(a_iValue, RTSTR_F_32BIT | RTSTR_F_VALSIGNED);
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, unsigned int a_uValue)
    {
        return a_rDst.formatInteger(a_uValue, RTSTR_F_32BIT | RTSTR_F_VALSIGNED);
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, long a_iValue)
    {
        return a_rDst.formatInteger(a_iValue, (sizeof(a_iValue) > sizeof(int32_t) ? RTSTR_F_64BIT : RTSTR_F_32BIT));
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, unsigned long a_uValue)
    {
        return a_rDst.formatInteger(a_uValue,
                                    RTSTR_F_VALSIGNED | (sizeof(a_uValue) > sizeof(uint32_t) ? RTSTR_F_64BIT : RTSTR_F_32BIT));
    }

    template<typename a_CharType, typename a_CharTraits = std::char_traits<a_CharType> >
    basic_ostream<a_CharType, a_CharTraits> &operator<<(basic_ostream<a_CharType, a_CharTraits> &a_rDst, void const *a_pvValue)
    {
        return a_rDst.formatInteger((uintptr_t)a_pvValue,
                                    (sizeof(a_pvValue) > sizeof(uint32_t) ? RTSTR_F_64BIT : RTSTR_F_32BIT), 16);
    }
    /** @} */

    template<>
    inline basic_ostream<char> &basic_ostream<char>::intWrite(const char *a_pchSrc, std::streamsize a_cchToWrite)
    {
        return write(a_pchSrc, a_cchToWrite);
    }


}

#endif /* !VBOX_INCLUDED_SRC_nocrt_ostream */

