/*  Copyright (C) 2004 Garrett A. Kajmowicz
 *
 * This file is part of the uClibc++ Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <basic_definitions>
#include <cstddef>
#include <locale>
#include <iosfwd>

#ifndef __HEADER_STD_IOS
#define __HEADER_STD_IOS 1

#pragma GCC visibility push(default)

extern "C++"
{
namespace std
{
  typedef signed long int streamoff;

  template <class stateT> class fpos;

  class _UCXXEXPORT ios_base
  {
  public:
    class failure;
#ifdef CONFIG_UCLIBCXX_EXCEPTION
    class failure : public exception 
    {
    public:
      explicit failure(const std::string&) { }
      explicit failure() { }
      virtual const char* what() const throw() {
        return "std::ios_base failure exception";
      }
    };
#endif
#ifdef __UCLIBCXX_SUPPORT_CDIR__
    class _UCXXLOCAL Init
    {
    public:
      _UCXXEXPORT Init();
      _UCXXEXPORT ~Init();
    private:
      static int init_cnt;
    };
#endif

  public:

    typedef unsigned short int fmtflags;

    static const fmtflags skipws = 0x0001;

    static const fmtflags left = 0x0002;
    static const fmtflags right = 0x0004;
    static const fmtflags internal = 0x0008;

    static const fmtflags boolalpha = 0x0010;

    static const fmtflags dec = 0x0020;
    static const fmtflags oct = 0x0040;
    static const fmtflags hex = 0x0080;

    static const fmtflags scientific = 0x0100;
    static const fmtflags fixed = 0x0200;

    static const fmtflags showbase = 0x0400;
    static const fmtflags showpoint = 0x0800;
    static const fmtflags showpos = 0x1000;
    static const fmtflags uppercase = 0x2000;

    static const fmtflags adjustfield = left | right | internal;
    static const fmtflags basefield = dec | oct | hex;
    static const fmtflags floatfield = fixed | scientific;

    static const fmtflags unitbuf = 0x4000;

    typedef unsigned char iostate;
    static const iostate goodbit = 0x00;
    static const iostate badbit = 0x01;
    static const iostate eofbit = 0x02;
    static const iostate failbit = 0x04;

    typedef unsigned char openmode;
    static const openmode app = 0x01;
    static const openmode ate = 0x02;
    static const openmode binary = 0x04;
    static const openmode in = 0x08;
    static const openmode out = 0x10;
    static const openmode trunc = 0x20;

    typedef unsigned char seekdir;
    static const seekdir beg = 0x01;
    static const seekdir cur = 0x02;
    static const seekdir end = 0x04;

    _UCXXEXPORT fmtflags flags() const
    {
      return mformat;
    }

    _UCXXEXPORT fmtflags flags(fmtflags fmtfl);

    fmtflags setf(fmtflags fmtfl);
    fmtflags setf(fmtflags fmtfl, fmtflags mask);

    _UCXXEXPORT void unsetf(fmtflags mask)
    {
      mformat&= ~mask;
    }

    _UCXXEXPORT streamsize precision() const
    {
      return mprecision;
    }

    _UCXXEXPORT streamsize precision(streamsize prec);

    _UCXXEXPORT streamsize width() const
    {
      return mwidth;
    }

    _UCXXEXPORT streamsize width(streamsize wide);

    _UCXXEXPORT locale imbue(const locale& loc);

    _UCXXEXPORT locale getloc() const
    {
      return mLocale;
    }

//  FIXME - These need to be implemented
//  static int xalloc();
//  long&  iword(int index);
//  void*& pword(int index);

    _UCXXEXPORT ~ios_base() { }

    enum event { erase_event, imbue_event, copyfmt_event };

    typedef void (*event_callback)(event, ios_base&, int index);
//  void register_callback(event_call_back fn, int index);

    //We are going to wrap stdio so we don't need implementation of the following:
    inline static bool sync_with_stdio(bool = true) { return true; }

  protected:
    _UCXXEXPORT ios_base() : mLocale(), mformat(dec | skipws), mstate(goodbit),
      mmode(), mdir(), mprecision(6), mwidth(0)
#ifdef __UCLIBCXX_SUPPORT_CDIR__
      ,mInit()
#endif
    {

    }
    locale mLocale;
    fmtflags mformat;
    iostate mstate;
    openmode mmode;
    seekdir mdir;
    streamsize mprecision;
    streamsize mwidth;
#ifdef __UCLIBCXX_SUPPORT_CDIR__
    Init mInit;
#endif
  };

  // ios_base manipulators

  inline ios_base& boolalpha(ios_base& str)
  {
    str.setf(ios_base::boolalpha);
    return str;
  }

  inline ios_base& noboolalpha(ios_base& str)
  {
    str.unsetf(ios_base::boolalpha);
    return str;
  }

  inline ios_base& showbase(ios_base& str){
    str.setf(ios_base::showbase);
    return str;
  }

  inline ios_base& noshowbase(ios_base& str)
  {
    str.unsetf(ios_base::showbase);
    return str;
  }

  inline ios_base& showpoint(ios_base& str)
  {
    str.setf(ios_base::showpoint);
    return str;
  }

  inline ios_base& noshowpoint(ios_base& str)
  {
    str.unsetf(ios_base::showpoint);
    return str;
  }

  inline ios_base& showpos(ios_base& str)
  {
    str.setf(ios_base::showpos);
    return str;
  }

  inline ios_base& noshowpos(ios_base& str)
  {
    str.unsetf(ios_base::showpos);
    return str;
  }

  inline ios_base& skipws(ios_base& str)
  {
    str.setf(ios_base::skipws);
    return str;
  }

  inline ios_base& noskipws(ios_base& str)
  {
    str.unsetf(ios_base::skipws);
    return str;
  }

  inline ios_base& uppercase(ios_base& str)
  {
    str.setf(ios_base::uppercase);
    return str;
  }

  inline ios_base& nouppercase(ios_base& str)
  {
    str.unsetf(ios_base::uppercase);
    return str;
  }

  inline ios_base& unitbuf(ios_base& str)
  {
    str.setf(ios_base::unitbuf);
    return str;
  }

  inline ios_base& nounitbuf(ios_base& str)
  {
    str.unsetf(ios_base::unitbuf);
    return str;
  }

  inline ios_base& internal(ios_base& str)
  {
    str.setf(ios_base::internal, ios_base::adjustfield);
    return str;
  }

  inline ios_base& left(ios_base& str)
  {
    str.setf(ios_base::left, ios_base::adjustfield);
    return str;
  }

  inline ios_base& right(ios_base& str)
  {
    str.setf(ios_base::right, ios_base::adjustfield);
    return str;
  }

  inline ios_base& dec(ios_base& str)
  {
    str.setf(ios_base::dec, ios_base::basefield);
    return str;
  }

  inline ios_base& hex(ios_base& str)
  {
    str.setf(ios_base::hex, ios_base::basefield);
    return str;
  }

  inline ios_base& oct(ios_base& str)
  {
    str.setf(ios_base::oct, ios_base::basefield);
    return str;
  }

  inline ios_base& fixed(ios_base& str)
  {
    str.setf(ios_base::fixed, ios_base::floatfield);
    return str;
  }

  inline ios_base& scientific(ios_base& str)
  {
    str.setf(ios_base::scientific, ios_base::floatfield);
    return str;
  }

  // basic_ios class definition

  template <class charT, class traits > class _UCXXEXPORT basic_ios
    : public ios_base
  {
  public:
    // Types:

    typedef charT char_type;
    typedef typename traits::int_type int_type;
    typedef typename traits::pos_type pos_type;
    typedef typename traits::off_type off_type;
    typedef traits traits_type;

    _UCXXEXPORT operator void*() const
    {
      if (fail())
      {
        return 0;
      }

      return (void *)(1);  // Must return a non-NULL pointer (though it can be *any* pointer)
    }

    _UCXXEXPORT bool operator!() const
    {
      return fail();
    }

    _UCXXEXPORT iostate rdstate() const
    {
      return mstate;
    }

    _UCXXEXPORT void clear(iostate state = goodbit)
    {
      if (rdbuf()!=0)
      {
        mstate = state;
      }
      else
      {
        mstate = state|ios_base::badbit;
      }
    }

    _UCXXEXPORT void setstate(iostate state)
    {
      clear(rdstate() | state);
#ifdef CONFIG_UCLIBCXX_EXCEPTION
      if (rdstate() & throw_mask)
      {
        throw failure();
      }
#endif
    }

    _UCXXEXPORT bool good() const
    {
      return (rdstate() == 0);
    }

    _UCXXEXPORT bool eof() const
    {
      if (rdstate() & eofbit)
      {
        return true;
      }

      return false;
    }

    _UCXXEXPORT bool fail() const
    {
      if (mstate & (failbit | badbit))
      {
        return true;
      }

      return false;
    }

    _UCXXEXPORT bool bad() const
    {
      if (mstate & badbit)
      {
        return true;
      }

      return false;
    }

    _UCXXEXPORT iostate exceptions() const
    {
      return throw_mask;
    }

    _UCXXEXPORT void exceptions(iostate except)
    {
      throw_mask = except;
    }

    explicit _UCXXEXPORT basic_ios(basic_streambuf<charT,traits>* sb) : fill_char(' '), mtied(0), mstreambuf(0)
    {
      init(sb);
    }

    basic_ios() : mtied(0), mstreambuf(0){ }

    virtual _UCXXEXPORT ~basic_ios()
    {
    }

    _UCXXEXPORT basic_ostream<charT,traits>* tie() const
    {
      return mtied;
    }

    _UCXXEXPORT basic_ostream<charT,traits>* tie(basic_ostream<charT,traits>* tiestr)
    {
      basic_ostream<charT,traits>* retval= mtied;
      mtied = tiestr;
      return retval;
    }

    _UCXXEXPORT basic_streambuf<charT,traits>* rdbuf() const
    {
      return mstreambuf;
    }

    _UCXXEXPORT basic_streambuf<charT,traits>* rdbuf(basic_streambuf<charT,traits>* sb)
    {
      basic_streambuf<charT,traits>* retval = mstreambuf;
      mstreambuf = sb;
      return retval;
    }

    _UCXXEXPORT basic_ios& copyfmt(const basic_ios& rhs);

    _UCXXEXPORT char_type fill() const
    {
      return fill_char;
    }

    _UCXXEXPORT char_type fill(char_type ch)
    {
      char_type temp = fill_char;
      fill_char = ch;
      return temp;
    }

    _UCXXEXPORT locale imbue(const locale& loc)
    {
      return ios_base::imbue(loc);
    }

    _UCXXEXPORT char narrow(char_type c, char dfault) const;
    _UCXXEXPORT char_type widen(char c) const;

  protected:
    char_type fill_char;
    basic_ostream<charT,traits>* mtied;
    basic_streambuf<charT,traits>* mstreambuf;
    iostate throw_mask;
    _UCXXEXPORT basic_ios(const basic_ios<charT,traits> &){ }
    _UCXXEXPORT basic_ios<charT,traits> & operator=(const basic_ios<charT,traits> &){ return *this; }

    _UCXXEXPORT void init(basic_streambuf<charT,traits>* sb)
    {
      ios_base::mformat = skipws|dec;
      mstreambuf = sb;
      mstate = goodbit;
      throw_mask = goodbit;
    }
  };

#ifdef __UCLIBCXX_EXPAND_IOS_CHAR__
#ifndef __UCLIBCXX_COMPILE_IOS__

  template <> _UCXXEXPORT void basic_ios<char, char_traits<char> >::clear(iostate state);
  template <> _UCXXEXPORT void basic_ios<char, char_traits<char> >::setstate(iostate state);

#endif
#endif

  template <class charT, class traits>
    inline char basic_ios<charT, traits>::narrow(char_type c, char dfault) const
  {
    return dfault;
  }

  template <>
    inline char basic_ios<char, char_traits<char> >::narrow(char_type c, char) const
  {
    return c;
  }

#ifdef __UCLIBCXX_HAS_WCHAR__

  template <>
    inline char basic_ios<wchar_t, char_traits<wchar_t> >::narrow(char_type c, char dfault) const
  {
    char retval = wctob(c);
    if (retval == EOF)
    {
      retval = dfault;
    }

    return retval;
  }

#endif  //__UCLIBCXX_HAS_WCHAR__

  template <class charT, class traits>
    inline typename basic_ios<charT, traits>::char_type
    basic_ios<charT, traits>::widen(char c) const
  {
    return c;
  }

  template <>
    inline basic_ios<char, char_traits<char> >::char_type
    basic_ios<char, char_traits<char> >::widen(char c) const
  {
    return c;
  }

#ifdef __UCLIBCXX_HAS_WCHAR__

  template <>
    inline basic_ios<wchar_t, char_traits<wchar_t> >::char_type
    basic_ios<wchar_t, char_traits<wchar_t> >::widen(char c) const
  {
    return btowc(c);
  }

#endif //__UCLIBCXX_HAS_WCHAR__


  template <class stateT> class _UCXXEXPORT fpos{
  public:
    _UCXXEXPORT fpos(stateT s)
    {
      st = s;
    }

    _UCXXEXPORT stateT state() const
    {
      return st;
    }

    _UCXXEXPORT void state(stateT s)
    {
      st = s;
    }

    _UCXXEXPORT bool operator==(const fpos &rhs)
    {
      return st == rhs.st;
    }

    _UCXXEXPORT bool operator!=(const fpos &rhs)
    {
      return st != rhs.st;
    }

    _UCXXEXPORT fpos & operator+(const streamoff & o)
    {
      st += o;
      return *this;
    }

    _UCXXEXPORT fpos & operator-(const streamoff & o)
    {
      st -= o;
      return *this;
    }

    _UCXXEXPORT streamoff operator-(const fpos & rhs)
    {
      return st - rhs.st;
    }

  private:
    stateT st;
  };

} // namespace
} // extern "C++"

#pragma GCC visibility pop

#endif

