/***************************************************************************
 *
 * _money_get.cc - definition of std::money_get members
 *
 * $Id: _money_get.cc 637130 2008-03-14 15:16:33Z faridz $
 *
 ***************************************************************************
 *
 * 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 2001-2008 Rogue Wave Software, Inc.
 * 
 **************************************************************************/

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


_RWSTD_NAMESPACE (__rw) {

_RWSTD_EXPORT int
__rw_get_num (void*, const char*, int, int,
              const char*, _RWSTD_SIZE_T, const char*, _RWSTD_SIZE_T);

_RWSTD_EXPORT int
__rw_check_grouping (const char*, _RWSTD_SIZE_T,
                     const char*, _RWSTD_SIZE_T);

}   // namespace __rw


_RWSTD_NAMESPACE (std) { 


template <class _CharT, class _InputIter>
_RW::__rw_facet_id money_get<_CharT, _InputIter>::id;


template <class _CharT, class _InputIter>
_TYPENAME money_get<_CharT, _InputIter>::iter_type
money_get<_CharT, _InputIter>::
_C_get (iter_type __it, iter_type __end, bool __intl, ios_base &__flags,
        _RWSTD_IOSTATE &__err, void *__pval, string_type *__pstr) const
{
    typedef moneypunct<_CharT, false> _Punct0;
    typedef moneypunct<_CharT, true> _Punct1;

    const _Punct0 &__pun = __intl ? 
          _RWSTD_REINTERPRET_CAST (const _Punct0&,
                                   _RWSTD_USE_FACET (_Punct1,
                                                     __flags.getloc ()))
        : _RWSTD_USE_FACET (_Punct0, __flags.getloc ());

    // retrieve positive and negative sign, currency symbol,
    // the grouping string and the pattern format
    const string_type __ps = __pun.positive_sign ();
    const string_type __ns = __pun.negative_sign ();
    const string_type __cs = __pun.curr_symbol ();
    const string      __gr = __pun.grouping ();
    const money_base::pattern __pat = __pun.neg_format ();

    _RWSTD_IOSTATE __ebits = _RW::__rw_goodbit;

    bool __needws = false;   // whitespace required?
    bool __seendp = false;   // seen decimal point in input

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

    char __buf [304];
    char *__pcur = __buf;

    typedef _TYPENAME string_type::traits_type _Traits;

    char __grpbuf [sizeof __buf];    // holds sizes of discovered groups
    char       *__pgrp = __grpbuf;   // current group
    const char *__grpstart = 0;      // the start of the last group
    const char *__grpend   = 0;      // the end of the last group


    int __sign = 0;    // the sign of the result if detected (-1, 0, or +1)

    // buffer must always start with a sign (__rw_get_num requirement)
    // use a '+' and overwrite it with a '-' if necessary
    *__pcur++ = '+';

    const int __fl = __flags.flags ();

    typedef _RWSTD_SIZE_T _SizeT;

    for (_SizeT __i = 0; !__ebits && __i != sizeof __pat.field; ++__i) {

        switch (__pat.field [__i]) {

        case /* '\1' */ money_base::space:
            __needws = true;
            // fall through...

        case /* '\0' */ money_base::none: {

            // optional or even required whitespace is consumed only if
            // other characters may be required to complete the format
            if (   sizeof __pat.field == __i + 1
                || 2 == __i && money_base::symbol == __pat.field [3]
                && (!(__fl & _RW::__rw_showbase) || 0 == __cs.size ())
                && !(   __sign < 0 && __ns.size () > 1
                     || __sign > 0 && __ps.size () > 1)
                || 1 == __i && money_base::value == __pat.field [0]
                && 0 == __ns.size () && 0 == __ps.size ()
                && (0 == __cs.size () || !(__fl & _RW::__rw_showbase)))
                break;

            _SizeT __nc = 0;

            while (__it != __end && __ctp.is (ctype_base::space, *__it)) {
                ++__it;
                ++__nc;
            }

            if (__needws && !__nc)
                __ebits |= _RW::__rw_failbit;
            break;
        }

        case /* '\2' */ money_base::symbol: {

            // if optional, currency symbol is not extracted unless
            // it is consumed only if it is followed by characters
            // required to complete the pattern (see example in
            // 22.2.6.1.2, p3)
            if (   __fl & _RW::__rw_showbase
                || __i < 2
                || 2 == __i && (money_base::none != __pat.field [3])
                || __sign < 0 && __ns.size () > 1
                || __sign > 0 && __ps.size () > 1) {

                for (_SizeT __nc = 0; __nc != __cs.size ();
                     ++__nc, ++__it) {
                    if (__it == __end || !_Traits::eq (*__it, __cs [__nc])) {

                        // 22.2.6.1.2, p2: unless showbase is set,
                        //                 curr_symbol is optional
                        if (__nc || __fl & _RW::__rw_showbase)
                            __ebits |= _RW::__rw_failbit;
                        break;
                    }
                }
            }
            break;
        }

        case /* '\3' */ money_base::sign: {

            if (__it == __end) {
                if (__ps.size () && __ns.size ())
                    __ebits |= _RW::__rw_failbit;
                else
                    __sign = __ps.empty () - 1;
                break;
            }

            const char_type __c = *__it;

            // 22.2.6.1.2, p3: if the first character of positive
            // and negative sign is the same, the result is positive

            if (__ps.size () && _Traits::eq (__c, __ps [0])) {
                __sign = 1;
                ++__it;
            }
            else if (__ns.size () && _Traits::eq (__c, __ns [0])) {
                __sign = -1;
                ++__it;
            }

            break;
        }

        case /* '\4' */ money_base::value: {

            const char_type __ts =
                __gr.size () && __gr [0] && __gr [0] != _RWSTD_CHAR_MAX ?
                __pun.thousands_sep () : __ctp.widen ('0');

            int __fd = __pun.frac_digits ();

            for (; __it != __end; ++__it) {

                // read and narrow a character (note that narrow() may
                // yield the same narrow char for more than one wide
                // character; e.g., there may be two sets of digits)
                const char_type __c  = *__it;
                const char      __ch = __ctp.narrow (__c, '\0');

                if (__ch >= '0' && __ch <= '9') {
                    if (!__seendp || __fd-- > 0)
                        *__pcur++ = __ch;
                }
                else if (   !__seendp && __fd > 0
                         && _Traits::eq (__c, __pun.decimal_point ())) {
                    __grpend = __pcur;
                    __seendp = true;
                }
                else if (!__seendp && _Traits::eq (__c, __ts)) {
                    // add the length of the current group to the array groups
                    // store UCHAR_MAX if group length exceeds the size of char
                    _RWSTD_PTRDIFF_T __len;

                    if (__grpstart)
                        __len = __pcur - __grpstart;
                    else {
                        __grpstart = __pcur;
                        __len      = __pcur - __buf - 1;
                    }

                    typedef unsigned char _UChar;

                    *__pgrp++  = char (__len < _UChar (-1) ? __len : -1);
                    __grpstart = __pcur;
                }
                else
                    break;
            }

            if (__pcur - __buf > 1) {
                // append zeros to a non-empty string of digits
                // up to the number of frac_digits
                while (__fd-- > 0)
                    *__pcur++ = '0';
            }

            *__pcur = '\0';
            break;
        }

        default:
            __ebits = _RW::__rw_failbit;
            break;

        }
    }

    if (!(__ebits & _RW::__rw_failbit)) {

        if (__buf [1]) {

            // process the remainder of a multicharacter sign
            const _SizeT __sizes [] = {
                __ps.size () ? __ps.size () -1 : 0,
                __ns.size () ? __ns.size () -1 : 0
            };

            const char_type* const __names [] = {
                __ps.data () + !!__sizes [0],
                __ns.data () + !!__sizes [1]
            };

            if (__sign && (__sizes [0] || __sizes [1])) {

                // if the first character of a multi-character sign
                // has been seen, try to extract the rest of the sign

                _SizeT __inx = 0;

                int __errtmp = 1;   // no duplicates allowed

                __it = _RW::__rw_match_name (__it, __end, __names, __sizes,
                                             sizeof __names / sizeof *__names,
                                             __inx, __errtmp, 0);

                if (1 < __inx && __sizes [__sign < 0]) {

                    // failed to match the remainder of the sign to
                    // the input  and the first character of the sign
                    // does not form the complete (positive or negative)
                    // sign
                    __ebits = _RW::__rw_failbit;
                    __buf [1]  = '\0';
                }
                else if (!_Traits::eq (*__ps.data (), *__ns.data ())) {

                    // if the first extracted character of the sign string
                    // forms the complete sign whose first character doesn't
                    // form an initial substring of the other sign string
                    // the resulting sign is determined by the one-character
                    // sign string
                    if (1 < __inx)
                        __inx = 0 < __sizes [__sign < 0];

                    // if both signs begin with the same character,
                    // the result is positive (22.2.6.1.2, p3)
                    *__buf = __inx ? '-' : '+';
                    __sign = -int (__inx);
                }
            }
            else if (__sign < 0) {
                *__buf = '-';
            }

            if (__pstr && !(__ebits & _RW::__rw_failbit)) {
                // skip over the leading sign and any redundant zeros after it
                const char *__start = __buf + 1;
                for (; '0' == *__start && '0' == __start [1]; ++__start);

                // invert the sign if negative
                __sign = __sign < 0;

                // widen narrow digits optionally preceded by the minus sign
                // into the basic_string object as required by 22.2.6.1.2, p1
                __pstr->resize ((__pcur - __start) + _SizeT (__sign));

                if (__sign)
                    _Traits::assign ((*__pstr)[0], __ctp.widen ('-'));

                __ctp.widen (__start, __pcur, &(*__pstr)[_SizeT (__sign)]);
            }

            const char *__grs = "";
            _SizeT      __grn = 0;

            // 22.2.6.1.2, p1: thousands separators are optional

            // add the length of the last group to the array of groups
            // store UCHAR_MAX if group length exceeds the size of char
            if (__grpstart) {
                const _RWSTD_PTRDIFF_T __len =
                    (__grpend ? __grpend : __pcur) - __grpstart;

                typedef unsigned char _UChar;

                *__pgrp++ = char (__len < _UChar (-1) ? __len : -1);
                *__pgrp   = '\0';
                __grs     = __gr.data ();
                __grn     = __gr.size ();
            }

            if (__pval) {
                // produce a number from the extracted string of digits
                // and (optionally) check grouping

                const int __errtmp =
                    _RW::__rw_get_num (__pval, __buf, _C_ldouble, 0,
                                       __grpbuf, __pgrp - __grpbuf,
                                       __grs, __grn);

                __ebits |= _RWSTD_IOSTATE (__errtmp);
            }
            else if (0 > _RW::__rw_check_grouping (__grpbuf, __pgrp - __grpbuf,
                                                   __grs, __grn))
                __ebits |= _RW::__rw_failbit;
        }
        else
            __ebits |= _RW::__rw_failbit;
    }

    if (__it == __end)
        __ebits |= _RW::__rw_eofbit;

    __err |= __ebits;

    return __it;
}


}   // namespace std
