//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
/*
* Name....: ntmbs.h
* Date....: 08/31/1999
* Creator.: rdifalco
*
* Routines to handle NTMBS (null-terminated multi-byte character sequences)
* as well as mappings to work between NTMBS and like-sized character-type
* sequences.
*/

#ifndef __NTMBS_H
#define __NTMBS_H


/// Requirements.

#include "error.h"                  // for: TSS_EXCEPTION
#include "package.h"                // for: TSS_GetString
#include "corestrings.h"            // for: Utility Strings


/// Type Definitions for Multiple (?), Double (16), and Wide Characters (32)

//--Null-terminated Multibyte Character Sequence

#ifndef NTMBS_T_DEFINED
#define NTMBS_T_DEFINED
    #ifdef TSS_NTMBS_IS_UNSIGNED    // Natural-sign by default
        typedef unsigned char           mbchar_t;
        typedef unsigned char*          ntmbs_t;
        typedef const unsigned char*    const_ntmbs_t;
    #else //TSS_NTMBS_IS_UNSIGNED
        typedef char                    mbchar_t;
        typedef char*                   ntmbs_t;
        typedef const char*             const_ntmbs_t;
    #endif//TSS_NTMBS_IS_UNSIGNED
#endif//NTMBS_T_DEFINED


//--Null-terminated Wide Character Sequence (Could be double or quad byte)

#ifndef NTWCS_T_DEFINED
#define NTWCS_T_DEFINED
    //typedef wchar_t           wchar_t;
    typedef wchar_t*            ntwcs_t;
    typedef const wchar_t*      const_ntwcs_t;
#endif//NTWCS_T_DEFINED



/// NOTE: Size Specific (2 [double] or 4 [quad] byte wide characters)

//--Null-terminated double(2)-byte Character Sequence

#ifndef NTDBS_T_DEFINED
#define NTDBS_T_DEFINED
    #if WCHAR_IS_16_BITS
        typedef wchar_t         dbchar_t;   // Same size but use NT's type
    #else
        typedef uint16          dbchar_t;
    #endif
    typedef dbchar_t*           ntdbs_t;
    typedef const dbchar_t*     const_ntdbs_t;
#endif//NTDBS_T_DEFINED


//--Internal, "size-specific" types for type dispatched specializations

#ifndef NTQBS_T_DEFINED
#define NTQBS_T_DEFINED
    #if WCHAR_IS_32_BITS
        typedef wchar_t         qbchar_t;   // Same size but use NT's type
    #else
        typedef uint32          qbchar_t;
    #endif
    typedef qbchar_t*           ntqbs_t;
    typedef const qbchar_t*     const_ntqbs_t;
#endif//NTQBS_T_DEFINED



//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// NTMBS Manipulators
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

TSS_EXCEPTION( eCharacter,         eError );
TSS_EXCEPTION( eCharacterEncoding, eCharacter );

namespace tss
{
    /// Specific Routines (Add as needed)

    ntmbs_t mbsdec( const_ntmbs_t, const_ntmbs_t );
    ntmbs_t mbsinc( const_ntmbs_t );

    size_t mbsbytes( const_ntmbs_t, size_t );
    size_t mbscount( const_ntmbs_t, size_t );

    size_t mbsbytes( const_ntmbs_t );
    size_t mbscount( const_ntmbs_t );

    inline size_t mbslen( const_ntmbs_t psz ) {     // RAD: Yeesh!
        return tss::mbscount( psz );
    }
}//tss::


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Character Sequence Independent Mappings
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// NOTE: In each, we first do the homogeneous charsize ver, then the hetero

namespace tss
{
    /// Increment and Decrement Pointers by (N)

    //--DEC

    template< class CT >
    inline
    CT* strdec( const CT*, const CT* psz ) {
        return const_cast<CT*>( psz - 1 );
    }

#if 0
    template<>
    inline
    ntmbs_t strdec( const_ntmbs_t beg, const_ntmbs_t cur ) {
        return tss::mbsdec( beg, cur );
    }
#endif


    //--INC

    template< class CT >
    inline
    CT* strinc( const CT* psz )
    {
        ASSERT( psz );
        return const_cast<CT*>( ( *psz ? psz + 1 : psz ) ); 
    }

    template< class CT, class SIZET >
    inline
    CT* strinc( const CT* psz, SIZET N )
    {
        ASSERT( psz );
        return const_cast<CT*>( ( *psz ? psz + N : psz ) ); 
    }

    
    template<>
    inline
    ntmbs_t strinc( const_ntmbs_t psz )
    {
        return tss::mbsinc( psz );
    }

    template<>
    inline
    ntmbs_t strinc( const_ntmbs_t psz, size_t N )
    {
        ntmbs_t at = const_cast<ntmbs_t>( psz );
        while ( N-- )
            at = tss::mbsinc( at );
        return at;
    }


    /// Byte and Character Count Mappings


    //--BYTES

    template< class CT >
    size_t strsize( const CT* psz )
    {
        const CT* at = psz;
        while ( *at ) at++;
        return (size_t)((char*)at - (char*)psz); 
    }

    template< class CT >
    size_t strsize( const CT*, size_t N )
    {
        return N * sizeof(CT);
    }

    template<>
    inline size_t strsize( const_ntmbs_t psz ) {
        return mbsbytes( psz );
    }
    template<>
    inline size_t strsize( const_ntmbs_t psz, size_t N ) {
        return mbsbytes( psz, N );
    }


    //--COUNT

    template< class CT >
    size_t strcount( const CT* psz )
    {
        const CT* at = psz;
        while ( *at ) at++;
        return (size_t)(at - psz); 
    }

    template< class CT >
    size_t strcount( const CT*, size_t N )
    {
        return N;
    }
   
    template<>
    inline size_t strcount( const_ntmbs_t psz ) {
        return mbscount( psz );
    }
    template<>
    inline size_t strcount( const_ntmbs_t psz, size_t N ) {
        return mbscount( psz, N );
    }


/* These are needed! However, it would be better to just create
a NTMBS aware string class (basic_string *can't* be) and be 
done with it */

#ifdef TSS_NTMBS_AWARE_SEACH_COMPLETE

    /* CAUTION:RAD -- I changed the traversal logic from "!=" to 
    "<" to accomodate a basic_string whose end() member does not
    correctly point after the last valid trail-byte in a string.
    Really, at != end should be correct, but I don't want to leave
    any room for error. At some point, these routines should be put
    under unit test and have all occurances of "at < end" changed 
    to the "more on-purpose" "at != end". */

    /// Various MULTIBYTE aware string searching routines..

    //--FIND_FIRST: ITER

    template< class InputT, class E >
    InputT
    find_first( InputT beg, InputT end, const E& item )
    {
        for ( ; beg < end && *beg != item; beg = (InputT)tss::strinc( beg ) );
        return beg;
    }

    
    //--FIND_LAST: ITER

    template< class InputT, class E >
    InputT
    find_last( InputT beg, InputT end, const E& item )
    {
        InputT at = end;
        for ( ; beg < end ; beg = (InputT)tss::strinc( beg ) );
            if ( *beg == item )
                at = beg;

        return at;
    }


    //--FIND_FIRST: STRING

    template< class StrT >
    StrT::size_type
    find_first( const StrT& sin, StrT::const_reference item )
    {
        StrT::const_iterator 
            beg = sin.begin();
            end = sin.end();

        StrT::size_type N;
        for ( N = 0; beg < end; ++N, beg = (InputT)tss::strinc( beg ) )
            if ( *beg == item )
                return N;

        return StrT::npos;
    }

    
    //--FIND_LAST: STRING

    template< class StrT >
    StrT::size_type
    find_last( const StrT& sin, StrT::const_reference item )
    {
        StrT::size_type N = 0;
        StrT::size_type nResult = StrT::npos;
        StrT::const_iterator 
            beg = sin.begin();
            end = sin.end();

        for ( ; beg < end ; beg = (InputT)tss::strinc( beg ) );
            if ( *beg == item )
                nResult = N;

        return N;
    }


    //--FIND_FIRST_OF: ITER

    template< class InputT >
    InputT
    find_first_of( 
        InputT beg, 
        InputT end,
        InputT setbeg,
        InputT setend )
    {
        InputT at;
        for ( ; beg < end; beg = (InputT)tss::strinc( beg ) );
            for ( at = setbeg; setbeg < setend; at = (InputT)tss::strinc( at ) )
                if ( *beg == *at )
                    return beg;
            
        return end;
    }


    //--FIND_FIRST_OF: STRING (SAFE!!!)

    template< class StrT >
    StrT::size_type
    find_first_of( const StrT& sin, StrT::const_iterator set )
    {
        // Point to beg of input
        StrT::iterator beg = sin.begin();

        // Start Search
        StrT::size_type N = 0;
        for ( ; beg < sin.end(); ++N, beg = tss::strinc( beg ) )
            for ( StrT::const_iterator at = set; *at; at = tss::strinc( at ) )
                if ( *beg == *at )
                    return N;
            
        return StrT::npos;
    }


    //--FIND_FIRST_OF: STRING (NOT SAFE!!!)

    template< class StrT >
    StrT::size_type
    find_first_of( 
        const StrT&          sin,
        StrT::const_iterator set,
        StrT::size_type      nPos,
        StrT::size_type      nCount )
    {
        ASSERT( nPos < tss::strcount( sin.begin() );

        if ( nCount > 0 && nPos < sin.size() )
        {
            ASSERT( nCount > tss::strcount( set ) );
            StrT::const_iterator endset; // Get end of set
            while ( nCount-- ) endset++;

            // Advance to nPos
            StrT::const_iterator at = tss::strinc( sin.begin(), nPos );
            StrT::const_iterator end = sin.end();

            // Start Search
            StrT::size_type N = 0;
            for ( ; at < end; ++N, at = tss::strinc( at ) )
            {
                if ( tss::find( set, endset, *at ) != 0 )
                    return N; 
            }
        }
            
        return StrT::npos;
    }


    //--FIND_LAST_OF: ITER

    template< class InputT1, class InputT2 >
    InputT
    find_last_of( 
        const InputT1 beg, const InputT1 end,
        const InputT2 setbeg, const InputT2 setend )
    {
        const InputT1 ans = end;
        for ( ; beg < end; beg = tss::strinc( beg ) );
            for ( InputT2 at = setbeg; setbeg != setend; at = tss::strinc( at ) )
                if ( *beg == *at )
                    ans = beg;
            
        return ans;
    }


    //--FIND_LAST_OF: STRING (SAFE!!!)

    template< class StrT >
    StrT::size_type
    find_last_of( const StrT& sin, StrT::const_iterator set )
    {
        StrT::size_type nResult = StrT::npos;
        StrT::size_type N = 0;
        for ( ; at < end; ++N, at = tss::strinc( at ) )
        {
            for ( StrT::const_iterator at = set; *at; at = tss::strinc( at ) )
                if ( *beg == *at )
                    nResult = N;
        }

        return nResult;
    }


    template< class StrT >
    StrT::size_type
    find_last_of( 
        const StrT& sin,
        StrT::const_iterator set,
        StrT::size_type nStart,
        StrT::size_type nCount )
    {
        if ( nCount > 0 && sin.size() )
        {
            for ( StrT::const_iterator at = sin.begin()
                + (nStart < _Len ? nStart : _Len - 1); ; --_U)
                if (_Tr::find(set, nCount, *_U) != 0)
                    return (_U - _Ptr);
                else if (_U == _Ptr)
                    break;
        }

        return StrT::npos; 
    }

    // TODO:RAD -- find_first_not_of;
    // TODO:RAD -- find_last_not_of;

#endif//TSS_NTMBS_AWARE_SEACH_COMPLETE

    namespace util
    {
        // Preserves bit values when enlarging a type to a size_t
        // Good for preventing sign extension
        template< class E >
        inline
        size_t 
        char_to_size( E ch )
        {
            // if this fails, 'ch' can't be cast to a 
            // size_t and preserve bit values
            // if this fails, then you must find another way 
            ASSERT( sizeof( size_t ) >= sizeof( E ) ); 

            // assert that either 'ch' is an unsigned value (no sign extension possible)
            // or that 'ch' is the same size as a size_t (no sign extension is possible as well)
            // if this fails, then you must specialize this function like we did for 'char'
            ASSERT( (int)(E)-1 > (int)0 || sizeof( size_t ) == sizeof( E ) ); 

            return (size_t)ch;
        }

        
        inline
        size_t 
        char_to_size( char ch )
        {
            return (size_t)(unsigned char)ch;
        }
    }
}//tss::


#endif//__NTMBS_H

