//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// This program is free software; you can redistribute it and/or
// modify it under 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.
//
// This program 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 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.
//-----------------------------------------------------------------------------
// package	libh/map
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 112 columns wide.
//-----------------------------------------------------------------------------

#include "map_sca.h"

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	map_sca_compare_str (internal)
//
// purpose	Compare the array keys in two AVL tree nodes.  This function
//		is not intended for direct reference by application programs.
//		It is used only to establish the comparison method for the
//		AVL tree used to hold this mapping.
//
// features	This compare function provides for optional features to do the
//		comparison in varied ways.  These include:
//
//		lower:	Compare alphabetic characters as if converted to lower
//			case (even if compared to non-alphabetic).
//
//		upper:	Compare alphabetic characters as if converted to upper
//			case (even if compared to non-alphabetic).
//
//		digits:	Compare sequences of 1 or more digits by prefixing the
//			shorter sequence with 0 digits to make the comparison
//			work on the numeric value.
//
//		spaces:	Compare sequences of 1 or more non-graphical characters
//			as being equal to a single binary 0 character.
//
//		paths:	Compare file path directory separator characters as
//			being lower in collating order than other characters.
//
//		Options are set in the map_sca_cmp_func_opt function.
//
// arguments	1 (struct map_link_sca *)
//		2 (struct map_link_sca *)
//		3 (AVL *) AVL link pointer
//
// returns	(int) comparison status, -1, 0, 1
//-----------------------------------------------------------------------------
int
map_sca_compare_str (
    avl_link *		arg_one
    ,
    avl_link *		arg_two
    ,
    AVL *		arg_avl
    )
__PROTO_END__
{
    signed char *		str1	;
    signed char *		str2	;
    size_t		rem1	;
    size_t		rem2	;

#ifdef MAP_OPT_LOWER
    int			opt_lower	;
#endif
#ifdef MAP_OPT_UPPER
    int			opt_upper	;
#endif
#ifdef MAP_OPT_DIGITS
    int			opt_digits	;
#endif
#ifdef MAP_OPT_SPACES
    int			opt_spaces	;
#endif
#ifdef MAP_OPT_PATHS
    int			opt_paths	;
#endif

    str1 = (signed char *)( ( (struct map_link_sca *) (void *) arg_one )->key );
    rem1 = ( (struct map_link_sca *) (void *) arg_one )->keylen;

    str2 = (signed char *)( ( (struct map_link_sca *) (void *) arg_two )->key );
    rem2 = ( (struct map_link_sca *) (void *) arg_two )->keylen;

#ifdef MAP_OPT_LOWER
    opt_lower	= map_from_avl( arg_avl )->options & MAP_OPT_LOWER;
#endif
#ifdef MAP_OPT_UPPER
    opt_upper	= map_from_avl( arg_avl )->options & MAP_OPT_UPPER;
#endif
#ifdef MAP_OPT_DIGITS
    opt_digits	= map_from_avl( arg_avl )->options & MAP_OPT_DIGITS;
#endif
#ifdef MAP_OPT_SPACES
    opt_spaces	= map_from_avl( arg_avl )->options & MAP_OPT_SPACES;
#endif
#ifdef MAP_OPT_PATHS
    opt_paths	= map_from_avl( arg_avl )->options & MAP_OPT_PATHS;
#endif

    //-----------------------------
    // Begin the main compare loop.
    //-----------------------------
    for (;;) {

        //-- If either string is exhausted then return now.
        if ( rem1 == 0 ) return ( rem2 == 0 ) ? 0 : -1;
        if ( rem2 == 0 ) return 1;

#ifdef MAP_OPT_DIGITS
        //-- If both characters are digits then handle as numeric substrings.
        if ( opt_digits && isdigit( * str1 ) && isdigit( * str2 ) ) {
            const signed char *	ptr1    ;
            const signed char *	ptr2    ;
            size_t              len1    ;
            size_t              len2    ;

            //-- Find the end of each numeric substring to know which is longer.
            ptr1 = str1;
            while ( -- rem1 && isdigit( * ptr1 ) ) ++ ptr1;
            len1 = ptr1 - str1;
            ptr2 = str2;
            while ( -- rem2 && isdigit( * ptr2 ) ) ++ ptr2;
            len2 = ptr2 - str2;

            //-- Compare the leading longer digits against all zero padding digits.
            if ( len1 > len2 ) {
                while ( len1 > len2 ) {
                    if ( * str1 > '0' ) return 1;
                    ++ str1; -- len1;
                }
            } else if ( len2 > len1 ) {
                while ( len2 > len1 ) {
                    if ( * str2 > '0' ) return -1;
                    ++ str2; -- len2;
                }
            }

            //-- Compare the rest of the digits very simply.
            while ( str1 < ptr1 ) {
                if ( * str1 != * str2 ) return ( * str1 < * str2 ) ? -1 : 1;
                ++ str1, ++ str2;
            }

	    continue;
        }
#endif

        //-- Otherwise compare as non-digits.
        {
            signed int	ch1     ;
            signed int	ch2     ;

            //-- If substring of spaces, then collapse and collate lower.
            ch1 = * str1 ++;
            ch2 = * str2 ++;

#ifdef MAP_OPT_SPACES
	    if ( opt_spaces ) {
		if ( ! isgraph( ch1 ) ) {
		    while ( -- rem1 && ! isgraph( * str1 ) ) ++ str1;
		    ch1 = 0;
		}
		if ( ! isgraph( ch2 ) ) {
		    while ( -- rem2 && ! isgraph( * str2 ) ) ++ str2;
		    ch2 = 0;
		}
	    }
#endif

#ifdef MAP_OPT_LOWER
	    //-- Remap to lower case for caseless comparison.
	    if ( opt_lower ) {
		ch1 = tolower( ch1 );
		ch2 = tolower( ch2 );
	    }
#endif

#ifdef MAP_OPT_UPPER
	    //-- Remap to upper case for caseless comparison.
	    if ( opt_upper ) {
		ch1 = toupper( ch1 );
		ch2 = toupper( ch2 );
	    }
#endif

#ifdef MAP_OPT_PATHS
	    //-- Remap collation for filepath separators even lower than spaces.
	    if ( opt_paths ) {
		if ( ch1 == '/' ) ch1 = -1;
		if ( ch2 == '/' ) ch2 = -1;
	    }
#endif

	    //-- If unequal then determine the difference orientation and return.
	    if ( ch1 != ch2 ) return ( ch1 < ch2 ) ? -1 : 1;

	    -- rem1;
	    -- rem2;
        }
    }
    //-- Never get here.
}


