#pragma GCC system_header

#ifndef _LIBCXX_IOS
#define _LIBCXX_IOS

#include <__config>
#include <iosfwd>

_LIBCXX_BEGIN_NAMESPACE_STD

typedef size_t streamsize;

class ios_base {
public:
    typedef uint32_t openmode;
    static constexpr openmode app = 0x1;
    static constexpr openmode binary = 0x2;
    static constexpr openmode in = 0x4;
    static constexpr openmode out = 0x8;
    static constexpr openmode trunc = 0x10;
    static constexpr openmode ate = 0x20;

    typedef uint32_t fmtflags;
    static constexpr fmtflags dec = 0x1;
    static constexpr fmtflags oct = 0x2;
    static constexpr fmtflags hex = 0x4;
    static constexpr fmtflags basefield = dec | oct | hex;
    static constexpr fmtflags left = 0x8;
    static constexpr fmtflags right = 0x10;
    static constexpr fmtflags internal = 0x20;
    static constexpr fmtflags adjustfield = left | right | internal;
    static constexpr fmtflags scientific = 0x40;
    static constexpr fmtflags fixed = 0x80;
    static constexpr fmtflags floatfield = scientific | fixed;
    static constexpr fmtflags boolalpha = 0x100;
    static constexpr fmtflags showbase = 0x200;
    static constexpr fmtflags showpoint = 0x400;
    static constexpr fmtflags showpos = 0x800;
    static constexpr fmtflags skipws = 0x1000;
    static constexpr fmtflags unitbuf = 0x2000;
    static constexpr fmtflags uppercase = 0x4000;

    typedef uint32_t iostate;
    static constexpr iostate goodbit = 0x0;
    static constexpr iostate badbit = 0x1;
    static constexpr iostate failbit = 0x2;
    static constexpr iostate eofbit = 0x4;

    typedef uint32_t seekdir;
    static constexpr seekdir beg = 0x1;
    static constexpr seekdir end = 0x2;
    static constexpr seekdir cur = 0x4;

    ios_base() = default;
    virtual ~ios_base() = default;

    fmtflags flags() const { return m_fl; }
    fmtflags flags(fmtflags flags) { return m_fl = flags; }

    fmtflags setf(fmtflags flags) { return m_fl |= flags; }
    fmtflags setf(fmtflags flags, fmtflags mask) { return m_fl = (m_fl & ~mask) | (flags & mask); }

    void unsetf(fmtflags flags) { m_fl = (m_fl & ~flags); }

    streamsize precision() const { return m_precision; }
    streamsize precision(streamsize n_p) { return m_precision = n_p; }

    streamsize width() const { return m_width; }
    streamsize width(streamsize n_w) { return m_width = n_w; }

    bool good() const { return rdstate() == goodbit; }
    bool eof() const { return (m_rdstate & eofbit) != 0; }
    bool fail() const { return (m_rdstate & failbit) != 0; }
    bool bad() const { return (m_rdstate & badbit) != 0; }
    bool operator!() const { return fail(); }
    explicit operator bool() const { return !fail(); }

    iostate rdstate() const { return m_rdstate; }
    void setstate(iostate state) { m_rdstate = state; }
    void clear(iostate state = goodbit) { m_rdstate = state; }

    // TODO: Support locales

private:
    fmtflags m_fl { 0 };
    iostate m_rdstate { 0 };
    iostate m_state { 0 };
    streamsize m_precision { 0 };
    streamsize m_width { 0 };
};

template <class CharT, class Traits>
class basic_ios : public ios_base {
public:
    using char_type = CharT;
    using traits_type = Traits;
    using int_type = typename traits_type::int_type;
    using pos_type = typename traits_type::pos_type;
    using off_type = typename traits_type::off_type;

    explicit basic_ios(basic_streambuf<char_type, traits_type>* rdbuf)
        : ios_base()
        , m_rdbuf(rdbuf)
    {
    }

    virtual ~basic_ios() = default;

    basic_streambuf<CharT, Traits>* rdbuf() const { return m_rdbuf; }
    basic_streambuf<CharT, Traits>* rdbuf(basic_streambuf<CharT, Traits>* sb) { return m_rdbuf = sb; }
    // TODO: Add more API to match C++20 standard.

protected:
    basic_ios() = default;

private:
    basic_streambuf<char_type, traits_type>* m_rdbuf { nullptr };
};

_LIBCXX_END_NAMESPACE_STD

#endif // _LIBCXX_IOS