/***************************************************************************
 *
 * _punct.cc
 *
 * $Id: _punct.cc 597438 2007-11-22 15:44:53Z 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 1994-2006 Rogue Wave Software.
 * 
 **************************************************************************/


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


_RWSTD_NAMESPACE (__rw) {


_EXPORT
template <class _CharT, class _InputIter>
_InputIter __rw_match_name (_InputIter __it, _InputIter __end,
                            const _CharT* const  *__names,
                            const _RWSTD_SIZE_T  *__sizes,
                            _RWSTD_SIZE_T         __count,
                            _RWSTD_SIZE_T        &__inx,
                            int                  &__err,
                            _STD::ios_base       *__flags)
{
    const _RWSTD_SIZE_T __badval = _RWSTD_SIZE_MAX;

    // make sure there are enough bits for all `count' `names'
    _RWSTD_ASSERT (__count <= sizeof (_RWSTD_SIZE_T) * _RWSTD_CHAR_BIT);

    // on input, `err' contains the maximum allowed number of matches
    const _RWSTD_SIZE_T __matchmax = (_RWSTD_SIZE_T)__err;

    __err = 0;

    _RWSTD_SIZE_T __bits   = ~0UL;      // a bit for each `name'
    _RWSTD_SIZE_T __nmatch = __count;   // total number of positive matches
    _RWSTD_SIZE_T __pos    = 0;         // position within input
    _RWSTD_SIZE_T __num    = __inx;     // input as a number

    __inx = __badval;   // invalidate

    const _STD::ctype<_CharT> *__ctp = __flags ?
        &_RWSTD_USE_FACET (_STD::ctype<_CharT>, __flags->getloc ()) : 0;

    _CharT __ch;

    for (; (__bits || __num != __badval) && !__err; ++__it, ++__pos) {

        if (__it == __end) {
            __err |= _RWSTD_IOS_EOFBIT;
            __ch   = _CharT ();
        }
        else
            __ch = *__it;

        if (__ctp && __num != __badval) {
            if (__ctp->is (_STD::ctype_base::digit, __ch))
                __num = __num * 10 + __ctp->narrow (__ch, '0') - '0';
            else
                __num = __badval;
        }

        // iterate over all names, clear bits of those that do not match
        for (_RWSTD_SIZE_T __k = 0; __k != __count; ++__k) {

            typedef _STD::char_traits<_CharT> _Traits;

            const _RWSTD_SIZE_T __mask =
                _RWSTD_STATIC_CAST (_RWSTD_SIZE_T, 1UL) << __k;

            if (__bits & __mask) {
                // `name' is still in the set, see if the next char matches
                // (case insensitive comparison done if `ctp' is nonzero)
                if (   __pos < __sizes [__k]
                    && !__err
                    && (__ctp ?
                            _Traits::eq ((__ctp->toupper)(__names [__k][__pos]),
                                         (__ctp->toupper)(__ch))
                          : _Traits::eq (__names [__k][__pos], __ch))) {

                    // next character on `name' mactches input
                    if (__badval == __inx) {
                        // no prior match, remember this one
                        __inx = __k;
                    }
                    else if (   __k != __inx
                             && __sizes [__k] == __sizes [__inx]
                             && __pos + 1 == __sizes [__inx]) {

                        // this match is a duplicate of the last best one
                        // remove this match from the set
                        __bits &= ~__mask;
                        --__nmatch;
                    }
                    else if (   __sizes [__k] < __sizes [__inx]
                             || __pos >= __sizes [__inx]) {
                        // this match is either shorter than the last one
                        // or the last one will be eliminated
                        __inx = __k;
                    }
                }
                else {
                    if (1 == __nmatch) {
                        if (__badval == __inx && __badval == __num)
                            return __it;
                        goto __endloop;
                    }

                    if (__inx == __k) {
                        // if this `name' is the current shortest match...
                        if (__err && __pos == __sizes [__k]) {
                            // ...and we're at eof, return successfully
                            return __it;
                        }
                        // otherwise forget the match
                        __inx = __badval;
                    }

                    // clear the bit for the `name' that failed to match
                    // and decrement the numeber of matches
                    __bits &= ~__mask;
                    --__nmatch;
                }
            }
        }

        if (1 == __nmatch && __pos + 1 == __sizes [__inx])
            return ++__it;
    }

__endloop:

    if (__matchmax < __nmatch || __pos < __sizes [__inx])
        __inx = __badval;

    if (__ctp && __badval == __inx && !__err && __num != __badval) {

        // if symbolic matching fails, fall back on numeric parsing
        for ( ; ; ) {
            if (++__it == __end) {
                __err |= _RWSTD_IOS_EOFBIT;
                break;
            }

            __ch = *__it;

            if (__ctp->is (_STD::ctype_base::digit, __ch))
                __num = __num * 10 + __ctp->narrow (__ch, '0') - '0';
            else
                break;
        }

        __inx = __count + __num;
    }

    return __it;
}


}   // namespace __rw
