//
// LDASTools AL - A library collection to provide an abstraction layer
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools AL is free software; you may 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 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools AL is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#include <ldastoolsal_config.h>

#include <cctype>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <typeinfo>

#include "util.hh"

#include "ldastoolsal/types.hh"

using namespace std;

//-----------------------------------------------------------------------------
/// \brief  Case-Insensitve String comparison
///
/// Compares the strings S1 and S2 and returns an integer less than, equal to,
/// greater than zero.
///
/// \param S1 Left hand side of comparison
/// \param S2 Right hand side of comparison
///
int
LDASTools::AL::cmp_nocase( const std::string& S1, const std::string& S2 )
{
    std::string::const_iterator p1 = S1.begin( );
    std::string::const_iterator p2 = S2.begin( );

    int N = std::min( S1.size( ), S2.size( ) );

    while ( N-- && ( ( *p1 == *p2 ) || ( toupper( *p1 ) == toupper( *p2 ) ) ) )
    {
        p1++;
        p2++;
    }
    if ( N < 0 )
    {
        return ( S2.size( ) == S1.size( ) )
            ? 0
            : ( ( S1.size( ) < S2.size( ) ) ? -1 : 1 );
    }
    return ( toupper( *p1 ) < toupper( *p2 ) ) ? -1 : 1;
}

//-----------------------------------------------------------------------------
/// \brief  Case-Insensitve String comparison
///
/// Compares the strings S1 and S2 and returns an integer less than, equal to,
/// greater than zero.
///
/// \param S1 Left hand side of comparison
/// \param S2 Right hand side of comparison
///
int
LDASTools::AL::cmp_nocase( const char* lhs, const char* rhs )
{
    //---------------------------------------------------------------------
    // Optimize by minimising the number of comparisons
    // NOTE:
    //    The code could be written more simply at the cost of performance.
    //---------------------------------------------------------------------
    while ( true )
    {
        //-------------------------------------------------------------------
        // Check for equality
        //-------------------------------------------------------------------
        if ( *lhs == *rhs )
        {
            if ( *lhs )
            {
                //---------------------------------------------------------------
                // Still equal and NOT at the end of the string
                //---------------------------------------------------------------
                ++lhs;
                ++rhs;
                continue;
            }
            //-----------------------------------------------------------------
            // Reached the end and everything compared
            //-----------------------------------------------------------------
            return ( 0 );
        }
        //-------------------------------------------------------------------
        // Check for equality ignoring the case
        //-------------------------------------------------------------------
        if ( toupper( *lhs ) == ( toupper( *rhs ) ) )
        {
            if ( *lhs )
            {
                //---------------------------------------------------------------
                // Still equal and NOT at the end of the string
                //---------------------------------------------------------------
                ++lhs;
                ++rhs;
                continue;
            }
            //-----------------------------------------------------------------
            // Reached the end and everything compared
            //-----------------------------------------------------------------
            return ( 0 );
        }
        //-------------------------------------------------------------------
        // There is a difference between the two, so determine what
        //-------------------------------------------------------------------
        if ( *lhs )
        {
            if ( *rhs )
            {
                //---------------------------------------------------------------
                // Terminated due to comparison difference
                //---------------------------------------------------------------
                return ( ( toupper( *lhs ) < toupper( *rhs ) ) ? -1 : 1 );
            }
            else
            {
                //---------------------------------------------------------------
                // Terminated due to length difference (left hand side longer).
                //---------------------------------------------------------------
                return ( -1 );
            }
        }
        if ( *rhs )
        {
            //-----------------------------------------------------------------
            // Terminated due to length difference (right hand side longer).
            //-----------------------------------------------------------------
            return ( 1 );
        }
    }
}

std::string
LDASTools::AL::slower( const std::string& s )
{
    std::string str;

    for ( std::string::const_iterator i = s.begin( ); i != s.end( ); ++i )
    {
        str += tolower( *i );
    }

    return str;
}

//-----------------------------------------------------------------------------
/// \brief  Convert to Lower-case
///
///!param std::string& s
///
void
LDASTools::AL::string2lower( std::string& s )
{
    std::string::iterator iter = s.begin( );
    for ( std::string::size_type size = s.size( ); size != 0; --size )
    {
        *iter = tolower( *iter );
        ++iter;
    }
}

//-------------------------------------------------------------------------------
/// \brief  Is character a space?
///
/// \param c Character to test.
///
/// \return bool True if argument is whitespace, false otherwise.
///
bool
LDASTools::AL::is_space( const char c )
{
    return isspace( c );
}

//-------------------------------------------------------------------------------
/// \brief  Is character a not space?
///
/// \param c Character to test.
///
/// \return bool True if argument is not a whitespace, false otherwise.
///
bool
LDASTools::AL::not_space( const char c )
{
    return !isspace( c );
}

//-----------------------------------------------------------------------
/// \brief  Rethrow a std::exception adding information to the front
///
/// \param Prefix
///	Text to be prepended to the exception text.
/// \param Exception
///	Origional exception that was thrown and used as type
///     of exception to throw.
//-----------------------------------------------------------------------
void
LDASTools::AL::Rethrow( const std::string&    Prefix,
                        const std::exception& Exception )
{
    std::ostringstream err;
    err << Prefix << ": " << Exception.what( );
    try
    {
        throw( Exception );
    }
    /// logic_error: begin
    catch ( const std::length_error& e )
    {
        throw std::length_error( err.str( ) );
    }
    catch ( const std::domain_error& e )
    {
        throw std::domain_error( err.str( ) );
    }
    catch ( const std::out_of_range& e )
    {
        throw std::out_of_range( err.str( ) );
    }
    catch ( const std::invalid_argument& e )
    {
        throw std::invalid_argument( err.str( ) );
    }
    catch ( const std::logic_error& e )
    {
        throw std::logic_error( err.str( ) );
    }
    /// logic_error: end
    /// runtime_error: begin
    catch ( const std::ios_base::failure& e )
    {
        throw std::ios_base::failure( err.str( ) );
    }
    catch ( const std::range_error& e )
    {
        throw std::range_error( err.str( ) );
    }
    catch ( const std::overflow_error& e )
    {
        throw std::overflow_error( err.str( ) );
    }
    catch ( const std::underflow_error& e )
    {
        throw std::underflow_error( err.str( ) );
    }
    catch ( const std::runtime_error& e )
    {
        throw std::runtime_error( err.str( ) );
    }
    /// runtime_error: end
    /// others: begin
    catch ( const std::bad_alloc& e )
    {
        throw std::bad_alloc( e );
    }
    catch ( const std::bad_exception& e )
    {
        throw std::bad_exception( e );
    }
    catch ( const std::bad_cast& e )
    {
        throw std::bad_cast( e );
    }
    catch ( const std::bad_typeid& e )
    {
        throw std::bad_typeid( e );
    }
    /// others: end
    catch ( const std::exception& e )
    {
        throw std::runtime_error( err.str( ) );
    }
}
