/***************************************************************************
 *
 * _money_put.cc - definition of std::num_put members
 *
 * $Id: _money_put.cc 651012 2008-04-23 19:10:45Z sebor $
 *
 ***************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you under  the Apache
 * License, Version  2.0 (the  "License"); you may  not use  this file
 * except in  compliance with the License.   You may obtain  a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the  License is distributed on an  "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
 * implied.   See  the License  for  the  specific language  governing
 * permissions and limitations under the License.
 *
 * Copyright 1994-2008 Rogue Wave Software, Inc.
 * 
 **************************************************************************/

#include <streambuf>
#include <loc/_moneypunct.h>
#include <loc/_ctype.h>


_RWSTD_NAMESPACE (__rw) {

_RWSTD_EXPORT _RWSTD_SIZE_T
__rw_put_num (char**, _RWSTD_SIZE_T, unsigned, int, _RWSTD_STREAMSIZE,
              const void*, const char*);

_RWSTD_EXPORT _RWSTD_SIZE_T
__rw_put_groups (char **, _RWSTD_SIZE_T, _RWSTD_SIZE_T,
                 const char*, const char*);

}   // namespace __rw


_RWSTD_NAMESPACE (std) { 


template <class _CharT, class _OutputIter>
_RW::__rw_facet_id money_put<_CharT, _OutputIter>::id;


template <class _CharT, class _OutputIter>
/* private */ _TYPENAME money_put<_CharT, _OutputIter>::iter_type
money_put<_CharT, _OutputIter>::
_C_put (iter_type __it, int __opts, ios_base &__flags, char_type __fill,
        const char_type *__s, _RWSTD_SIZE_T __n, int __fd,
        const char *__groups, _RWSTD_SIZE_T __ngroups) const
{
    // static const arrays of pointers to moneypunct<charT, Intl> members
    // to avoid "if (intl)" conditionals when retrieving punct values

    const bool __intl = 0 != (__opts & _C_intl);
    const bool __num  = 0 != (__opts & _C_ldbl);

    typedef moneypunct<_CharT, false> _Punct0;
    typedef moneypunct<_CharT, true>  _Punct1;

    const _Punct0* __pun0;
    const _Punct1* __pun1;

#ifdef __GNUG__
    // silence bogus gcc -Wuninitialized warning:
    //   object may be used uninitialized in this function
    // while avoiding bogus HP aCC/cadvise warning 20200:
    //   potential null pointer dereference 
    __pun0 = 0;
    __pun1 = 0;
#endif   // gcc

    if (__intl) {
        __pun1 = &_RWSTD_USE_FACET (_Punct1, __flags.getloc ());
    }
    else {
        __pun0 = &_RWSTD_USE_FACET (_Punct0, __flags.getloc ());
    }

    const ctype<_CharT> &__ctp =
        _RWSTD_USE_FACET (ctype<_CharT>,__flags.getloc ());

    const char __signchar = __ctp.narrow (*__s, '\0');

    money_base::pattern __fmat;   // negative or positive format
    string_type         __sign;   // negative or positive sign

    if ('-' == __signchar) {
        if (__intl) {
            __fmat = __pun1->neg_format ();
            __sign = __pun1->negative_sign ();
        }
        else {
            __fmat = __pun0->neg_format ();
            __sign = __pun0->negative_sign ();
        }
        ++__s;
        --__n;
    }
    else {
        if (__intl) {
            __fmat = __pun1->pos_format ();
            __sign = __pun1->positive_sign ();
        }
        else {
            __fmat = __pun0->pos_format ();
            __sign = __pun0->positive_sign ();
        }
        if ('+' == __signchar) {
            ++__s;
            --__n;
        }
    }

    // optional currency sumbol
    const string_type __curr = (__flags.flags () & _RWSTD_IOS_SHOWBASE) ?
          __intl ? __pun1->curr_symbol () : __pun0->curr_symbol ()
        : string_type ();

    // size of fractional and integral parts, respectively, to output
    long __dint = long (__n - __fd);

    // must have at least one group (even if there are no thousands_sep)
    _RWSTD_ASSERT (__ngroups > 0);

    // adjust padding by the sizes of each component, including the
    // number of thousands separators to be inserted
    long __pad = long (
          __flags.width () - __curr.size () - __sign.size ()
        - !__dint - !!__fd - (__dint > 0 ? __dint : -__dint) - __fd
        - (   money_base::space == __fmat.field [1]
           || money_base::space == __fmat.field [2])
        - (__ngroups - 1));

    const int __adj = __flags.flags () & _RWSTD_IOS_ADJUSTFIELD;

    if (__adj != _RWSTD_IOS_INTERNAL && __adj != _RWSTD_IOS_LEFT) {

        for (; __pad > 0; --__pad, ++__it)
            *__it = __fill;
    }

    for (_RWSTD_SIZE_T __i = 0, __j; __i != sizeof __fmat; ++__i) {

        switch (__fmat.field [__i]) {

        case money_base::symbol:   // exactly one occurrence required
            for (__j = 0; __j != __curr.size (); ++__j, ++__it)
                *__it = __curr [__j];
            break;

        case money_base::sign:   // exactly one occurrence required
            if (__sign.size ()) {
                // first character of sign only
                *__it = __sign [0];
                ++__it;
            }
            break;

        case money_base::space:   // optional (required if none not present)
            // 22.2.6.3, p1: the value space, if present,
            //               is neither first nor last.

            _RWSTD_REQUIRES (0 != __i && sizeof __fmat != __i - 1,
                             (_RWSTD_ERROR_RUNTIME_ERROR,
                              _RWSTD_FUNC ("std::num_put<>::do_put()")));

            // an ordinary space, not `fill', is required here
            // will precede any required padding
            *__it = __ctp.widen (' ');
            ++__it;

        case money_base::none:   // optional (required if space not present)
            // 22.2.6.3, p1: the value none, if present, is not first

            _RWSTD_REQUIRES (0 != __i,
                             (_RWSTD_ERROR_RUNTIME_ERROR,
                              _RWSTD_FUNC ("std::num_put<>::do_put()")));

            if (   (__adj & _RWSTD_IOS_ADJUSTFIELD) == _RWSTD_IOS_INTERNAL
                && sizeof __fmat != __i - 1)
                for (; __pad > 0; --__pad, ++__it)
                    *__it = __fill;

            break;

        case money_base::value: {   // exactly one occurrence required

            if (__dint < 0 || 0 == __dint && __fd > 0) {
                const char_type __zero = __ctp.widen ('0');

                // insert leading zero and decimal point
                *__it = __zero;
                ++__it;

                *__it = __intl ?
                    __pun1->decimal_point () : __pun0->decimal_point ();
                ++__it;

                // insert leading fractional zeros
                for (; __dint++; ++__it)
                    *__it = __zero;

                // prevent insertion of the decimal point below
                __dint = -1;
            }

            _RWSTD_SIZE_T __grplen = 0;

            for (const char_type *__e = __s + __n; __s != __e; ++__it, ++__s) {

                if (!__num && !__ctp.is (ctype_base::digit, *__s))
                    break;

                typedef unsigned char _UChar;

                if (0 < __dint) {

                    _RWSTD_ASSERT (0 != __groups);

                    if (*__groups && __grplen == _UChar (*__groups)) {
                        *__it = __intl ? __pun1->thousands_sep ()
                                       : __pun0->thousands_sep ();
                        __grplen = 0;
                        ++__groups;
                        ++__it;
                    }
                }
                else if (0 == __dint) {
                    *__it = __intl ? __pun1->decimal_point ()
                                   : __pun0->decimal_point ();
                    ++__it;
                }

                --__dint;
                ++__grplen;

                switch (*__s) {
                    // replace either the period or the comma with the
                    // decimal point in case a setlocale() call made by
                    // the program changed the default '.' to ','
                case '.':
                case ',':
                    *__it = __intl ? __pun1->decimal_point ()
                                   : __pun0->decimal_point ();
                    break;

                case ';':
                    *__it = __intl ? __pun1->thousands_sep ()
                                   : __pun0->thousands_sep ();
                    break;

                default:  *__it = *__s;
                }
            }

            break;
        }
        }
    }

    // output the remaining characters of sign (if any)
    for (_RWSTD_SIZE_T __k = 1; __k < __sign.size (); ++__k, ++__it)
        *__it = __sign [__k];

    // left ajustment
    for (; __pad > 0; --__pad, ++__it)
        *__it = __fill;

    // 22.2.6.2.2, p1: reset width()
    __flags.width (0);

    return __it;
}


template <class _CharT, class _OutputIter>
/* virtual */ _TYPENAME money_put<_CharT, _OutputIter>::iter_type
money_put<_CharT, _OutputIter>::
do_put (iter_type __i, bool __intl, ios_base &__flags, char_type __fill,
        long double __val) const
{
    int __fd;
    string __grouping;

    if (__intl) {
        typedef moneypunct<_CharT, true> _Punct;
        const _Punct &__pun = _RWSTD_USE_FACET (_Punct, __flags.getloc ());

        __fd       = __pun.frac_digits ();
        __grouping = __pun.grouping ();
    }
    else {
        typedef moneypunct<_CharT, false> _Punct;
        const _Punct &__pun = _RWSTD_USE_FACET (_Punct, __flags.getloc ());

        __fd       = __pun.frac_digits ();
        __grouping = __pun.grouping ();
    }

    char       __buf [304];
    char_type __wbuf [sizeof __buf];

    char *__pbuf = __buf;

    // format a floating point number in fixed precision into narrow buffer
    // will insert thousands_sep placeholders (';') accroding to grouping
    const _RWSTD_SIZE_T __n =
        _RW::__rw_put_num (&__pbuf, sizeof __buf, _RWSTD_IOS_FIXED,
                           _C_ldouble | _C_ptr,
                           _RWSTD_STATIC_CAST (_RWSTD_STREAMSIZE, -__fd),
                           &__val, __grouping.c_str ());

    // widen narrow buffer (necessary even if char_type == char)
    const ctype<_CharT> &__ctp =
        _RWSTD_USE_FACET (ctype<_CharT>, __flags.getloc ());

    __ctp.widen (__buf, __buf + __n, __wbuf);

    // write the widened buffer out, replacing any commas
    // with the actual thousands_sep punct character
    return _C_put (__i, (__intl ? _C_intl : 0) | _C_ldbl,
                   __flags, __fill, __wbuf, __n, 0, "", 1);
}


template <class _CharT, class _OutputIter>
/* virtual */ _TYPENAME money_put<_CharT, _OutputIter>::iter_type
money_put<_CharT, _OutputIter>::
do_put (iter_type __i, bool __intl, ios_base &__flags, char_type __fill,
        const string_type &__str) const
{
    // fractional part does not undergo grouping and will be removed
    int __fd;

    string __grouping;

    if (__intl) {
        typedef moneypunct<_CharT, true> _Punct;

        const _Punct &__pun = _RWSTD_USE_FACET (_Punct, __flags.getloc ());

        __fd       = __pun.frac_digits ();
        __grouping = __pun.grouping ();
    }
    else {
        typedef moneypunct<_CharT, false> _Punct;

        const _Punct &__pun = _RWSTD_USE_FACET (_Punct, __flags.getloc ());

        __fd       = __pun.frac_digits ();
        __grouping = __pun.grouping ();
    }

    _RWSTD_SIZE_T  __ngroups = 1;          // always at least one group
    _RWSTD_SIZE_T  __strdigs = 0;          // number of digits in `str'

    typedef string::traits_type                   _Traits;
    typedef _TYPENAME string_type::const_iterator _StringIter;

    char        __buf [304];            // buffer for groups
    static char __nul    = '\0';        // `groups' must be non-0
    char       *__groups = __grouping.size () ? __buf : &__nul;

    const ctype<_CharT> &__ctp =
        _RWSTD_USE_FACET (ctype<_CharT>, __flags.getloc ());

    // narrow digits into a temporary buffer to determine grouping
    for (_StringIter __it = __str.begin (); __it != __str.end (); ++__it) {
        if (__ctp.is (ctype_base::digit, *__it)) {
            if (__groups != &__nul)
                _Traits::assign (*__groups++, __ctp.narrow (*__it, '\0'));
        }
        else if (__str.begin () != __it || '-' != __ctp.narrow (*__it, '\0'))
            break;
        ++__strdigs;
    }

    if (__groups != &__nul && __fd >= 0 && __fd < __groups - __buf) {
        __groups -= __fd;

        // compute the size of each group relative to the formatted number
        // e.g., with `str' of "9876543210" and `grouping' of "\1\2\3",
        // `groups' will point at "\1\3\3\2\1" since `str' will be
        // formatted as "9,876,543,21,0" with thousands_sep's inserted

        char *__pbuf = __buf;

        *__pbuf = '\0';

        __ngroups =
            _RW::__rw_put_groups (&__pbuf, __groups - __pbuf, sizeof __buf,
                                  0, __grouping.c_str ());
        __pbuf [__ngroups] = '\0';
        __groups           = __pbuf;
    }

    return _C_put (__i, __intl ? _C_intl : 0,
                   __flags, __fill, __str.c_str (), __strdigs,
                   __fd, __groups, __ngroups);
}


}   // namespace std
