/******************************************************************************
 * $LastChangedDate: 2011-04-22 14:46:09 -0400 (Fri, 22 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Misc functions.
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 ******************************************************************************/

#define BASE_MISC_CC 1
#include <cstdlib>
#include <ctype.h>
#include "base/module.hh"
#include "base/misc.hh"

namespace base {

/*****************************************************************************
 * Safely process a cmd-line arg.
 * @return Zero if arg idx is invalid.
 *****************************************************************************/
int
Argi( int idx, int argc_, char** argv_ )  // careful, "argv" is a global var in C
{
    if ( idx >= argc_ ) return 0;
    return int(strtol( argv_[idx], NULL, 0 )); // base=0 to convert decimal/hex/octal
}

float
Argf( int idx, int argc_, char** argv_ )
{
    if ( idx >= argc_ ) return 0;
    return atof( argv_[idx] );
}

string
Argstr( int idx, int argc_, char** argv_ )
{
    if ( idx >= argc_ ) return string( "" );
    return string( argv_[idx] );
}

/*****************************************************************************
 * Functions for marking a command-line arg as "done".
 * For helping to catch unrecognized args.
 * @param   i
 *          argv[i]
 * @param   n
 *          How many consecutive args are "done" (default n=1).
 *****************************************************************************/
INTERN bool sCmdargDone[100];

void
CmdargDone( int i, int n )
{
    for ( int j = 0; j < n; ++j ) sCmdargDone[i+j] = true;
}

bool
IfCmdargDone( int i )
{
    return sCmdargDone[i];
}

/*****************************************************************************
 * cygwin lacks strcasestr().
 * This code came from FreeBSD.
 * http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/string/strcasestr.c?rev=1.4;content-type=text%2Fplain
 *****************************************************************************/
#if OS_CYGWIN
char*
STRCASESTR( const char *s, const char* find )
{
    char c, sc;
    size_t len;

    if ((c = *find++) != 0)
    {
        c = tolower((unsigned char)c);
        len = strlen(find);
        do
        {
            do
            {
                if ((sc = *s++) == 0)
                    return (NULL);
            } while ((char)tolower((unsigned char)sc) != c);
        } while (strncasecmp(s, find, len) != 0);
        s--;
    }
    return ((char *)s);
}
#endif // OS_CYGWIN

/*****************************************************************************
 * Convert bytes for printing as "n.nMB".
 *****************************************************************************/
fp
FormatMegabyte( const long bytes )
{
    // One fractional digit.
    return fp(long(bytes*10) / long(MEGABYTE)) / fp(10.0f);
}

/*****************************************************************************
 * Conver string in-place to lower-case.
 *****************************************************************************/
void
LowerCase( string& s )
{
    for ( uint i = 0; i < s.size(); ++i )
    {
        s[i] = char(tolower(s[i]));
    }
}

/*****************************************************************************
 * Convert string in-place to upper-case.
 *****************************************************************************/
void
UpperCase( string& s )
{
    for ( uint i = 0; i < s.size(); ++i )
    {
        s[i] = char(toupper(s[i]));
    }
}

/*****************************************************************************
 * @return Signed hex number as a C++ string.
 * Neither sprintf() nor C++ streams will produce a signed hex number.
 *****************************************************************************/
string
SignedHexNumber2String( const long signedHexValue,
                        const uint minWidth,
                        const eNumberFormat format )
{
    string hexStr;

    // Must be unsigned to use unsigned shift instruction that inserts 0 bit at MSB.
    unsigned long unsignedHexValue = ABS( signedHexValue );
    do
    {
        char digit = unsignedHexValue & 0xf;  // hexValue % 16
        if ( digit > 9 )
            digit = 'a' + digit - 10;
        else
            digit = '0' + digit;
        hexStr.insert( hexStr.begin(), digit );
        unsignedHexValue >>= 4;  // hexValue /= 16
    }
    while ( unsignedHexValue );

    string::size_type padPos = 0;
    if ( (format & eNumberFormat_NO_SHOW_BASE) == 0 )
    {
        hexStr.insert( 0, "0x" );
        padPos = 2;
    }

    // Zero has no sign.
    if ( signedHexValue < 0 )
    {
        hexStr.insert( hexStr.begin(), '-' );  // negative
        ++padPos;
    }
    else if ( (signedHexValue > 0) and (format & eNumberFormat_SHOW_PLUS) )
    {
        hexStr.insert( hexStr.begin(), '+' );  // positive
        ++padPos;
    }

    if ( minWidth > 0 )
    {
        const char padChar = (format & eNumberFormat_PAD_ZEROS) ? '0' : ' ';
        const int delta = int(minWidth) - int(hexStr.size());
        ASSERT( delta < 99 );
        if ( delta > 0 )
            hexStr.insert( padPos, delta, padChar );
    }

    return hexStr;
}

} // namespace base
