/
/******************************************************************************
 * Copyright (C) 2012 Matthew Goff                                            *
 *                                                                            *
 * This software is provided 'as-is', without any express or implied          *
 * warranty.  In no event will the authors be held liable for any damages     *
 * arising from the use of this software.                                     *
 *                                                                            *
 * Permission is granted to anyone to use this software for any purpose,      *
 * including commercial applications, and to alter it and redistribute it     *
 * freely, subject to the following restrictions:                             *
 *                                                                            *
 * 1. The origin of this software must not be misrepresented; you must not    *
 *    claim that you wrote the original software. If you use this software    *
 *    in a product, an acknowledgment in the product documentation would be   *
 *    appreciated but is not required.                                        *
 *                                                                            *
 * 2. Altered source versions must be plainly marked as such, and must not    *
 *    be misrepresented as being the original software.                       *
 *                                                                            *
 * 3. This notice may not be removed or altered from any source distribution. *
 *                                                                            *
 * Matthew Goff (matt@goff.cc) <http://www.ackmud.net/>                       *
 ******************************************************************************/
/**
 * @file utils.h
 * @brief The Utils namespace.
 *
 *  This file contains the Utils namespace and template functions.
 */
#ifndef DEC_UTILS_H
#define DEC_UTILS_H

#include <algorithm>
#include <bitset>
#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <sstream>
#include <vector>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>

using namespace std;

enum UTILS_OPTS {
    UTILS_DEBUG       = 0,  /**< Enables debugging output in Utils::_Logger(). This typically includes the calling function's file and line number. */
    UTILS_RAW         = 1,  /**< Raw data output. Skips prepending / appending anything on Utils::_Logger writes (no timestamp, etc). */
    UTILS_TYPE_ERROR  = 2,  /**< Indicates an error and prepends #CFG_STR_UTILS_ERROR to Utils::_Logger() output. */
    UTILS_TYPE_INFO   = 3,  /**< Indicates an info message and prepends #CFG_STR_UTILS_INFO to Utils::_Logger() output. */
    UTILS_TYPE_SOCKET = 4,  /**< Indicates a socket related message and prepends #CFG_STR_UTILS_SOCKET to Utils::_Logger() output. */
    UTILS_TIME_S      = 5,  /**< Will return time as @a seconds from Utils::DiffTime(). */
    UTILS_TIME_MS     = 6,  /**< Will return time as @a milliseconds from Utils::DiffTime(). */
    UTILS_TIME_US     = 7,  /**< Will return time as @a microseconds from Utils::DiffTime(). */
    MAX_UTILS         = 8   /**< Safety limit for looping. */
};

#define UTILS_IS_DIRECTORY  true
#define UTILS_IS_FILE       false

/**
 * @def CFG_MEM_MAX_BITSET
 * @brief Maximum size of all bitset elements within the server.
 * @par Default: 32
 */
#define CFG_MEM_MAX_BITSET  32

/**
 * @def CFG_STR_UTILS_ERROR
 * @brief String to prepend to logs flagged UTILS_TYPE_ERROR.
 * @par Default: "[ERROR ] "
 */
#define CFG_STR_UTILS_ERROR     "[ERROR] "

/**
 * @def CFG_STR_UTILS_INFO
 * @brief String to prepend to logs flagged UTILS_TYPE_INFO.
 * @par Default: "[INFO  ] "
 */
#define CFG_STR_UTILS_INFO      "[INFO  ] "

/**
 * @def CFG_STR_UTILS_SOCKET
 * @brief String to prepend to logs flagged UTILS_TYPE_SOCKET.
 * @par Default: "[SOCKET] "
 */
#define CFG_STR_UTILS_SOCKET    "[SOCKET] "

/**
 * @def PP_NARG
 * @author Laurent Deniau @ http://goo.gl/3jkAm
 * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list.
 * @param[in] ... A variable length argument list to determine the count of.
 */
#define PP_NARG(...) \
         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())

/**
 * @def PP_NARG_
 * @author Laurent Deniau @ http://goo.gl/3jkAm
 * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list.
 */
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)

/**
 * @def PP_ARG_N
 * @author Laurent Deniau @ http://goo.gl/3jkAm
 * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list.
 */
#define PP_ARG_N( \
         _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N

/**
 * @def PP_RSEQ_N
 * @author Laurent Deniau @ http://goo.gl/3jkAm
 * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list.
 */
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

/**
 * @def STR
 * @brief Stringify the calling function's file and line number for debugging.
 * @param[in] x An item to be stringified.
 */
#define STR(x) #x

/**
 * @def SX
 * @brief Stringify the calling function's file and line number for debugging.
 * @parampin] x An item to be stringified.
 */
#define SX(x) STR(x)

/**
 * @def _caller_
 * @brief Stringify the calling function's file and line number for debugging.
 */
#define _caller_ __FILE__ ":" SX(__LINE__)

/**
 * @def BSET
 * @brief Define a bitset variable (name) with values (value) already set.
 * @param[in] name  The name to use for declaring a local variable of bitset<#CFG_MEM_MAX_BITSET>.
 * @param[in] value Values from #UTILS_OPTS to be enabled on name.
 */
#define BSET( name, value ) \
        bitset<CFG_MEM_MAX_BITSET> name; \
        name.set( value )

/**
 * @def CSTR
 * @brief Output std::string to const char*.
 * @param[in] str A std::string object.
 */
#define CSTR( str ) ( str ).c_str()

/**
 * @def LOGSTR
 * @brief Wrap Utils::Logger() for brevity and ease of future maintenance.
 * @param[in] flags A local variable name of type bitset<#CFG_MEM_MAX_BITSET> with #UTILS_OPTS enabled as appropriate. 0 may be used if no options are needed.
 * @param[in] message Any string, char, const char, numeric value, etc. This message will be written to log.
 */
#define LOGSTR( flags, message ) Utils::Logger( flags, message )

/**
 * @def LOGFMT
 * @brief Wrap Utils::FormatString() within Utils::Logger() for brevity and ease of future maintenance.
 * @param[in] flags A local variable name of type bitset<#CFG_MEM_MAX_BITSET> with #UTILS_OPTS enabled as appropriate. 0 may be used if no options are needed.
 * @param[in] message Any string that contains printf style format variables.
 * @param[in] ... The list of arguments to format into message.
 */
#define LOGFMT( flags, message, ... ) LOGSTR( flags, Utils::FormatString( flags, message, __VA_ARGS__ ) )

/**
 * @def LOGERRNO
 * @brief Wrap Utils::Logger() based on a locally generated errno value from system functions.
 * @param[in] flags A local variable name of type bitset<#CFG_MEM_MAX_BITSET> with #UTILS_OPTS enabled as appropriate. 0 may be used if no options are needed.
 * @param[in] message Any string that contains printf style format variables.
 */
#define LOGERRNO( flags, message ) LOGFMT( flags, message " returned errno %d: %s", errno, strerror( errno ) )

/**
 * @def UFLAGS_DE
 * @brief Define a bitset variable (name) with #UTILS_DEBUG and #UTILS_TYPE_ERROR already enabled.
 * @param[in] name The name to use for declaring a local variable of bitset<#CFG_MEM_MAX_BITSET>.
 */
#define UFLAGS_DE( name ) BSET( name, UTILS_DEBUG & UTILS_TYPE_ERROR )

/**
 * @brief The Utils namespace contains all general purpose, multi-use, and non-class functions.
 */
namespace Utils {
    /**
     * @brief Returns a string with all whitespace characters removed.
     * @param[in] t Any type of string to remove whitespace from.
     * @retval string A string with all whitespace characters removed.
     */
    template <class T> inline const string DelSpaces( const T& t )
    {
        string output( t );

        output.erase( remove_if( output.begin(), output.end(), ::isspace ), output.end() );

        return output;
    }
    /**
     * @brief Returns a string converted to all lowercase letters.
     * @param[in] t Any type of string to convert to lowercase.
     * @retval string A string converted to all lowercase letters.
     */
    template <class T> inline const string Lower( const T& t )
    {
        string output( t );

        transform( output.begin(), output.end(), output.begin(), ::tolower );

        return output;
    }
    /**
     * @brief Returns a string converted to all uppercase letters.
     * @param[in] t Any type of string to convert to uppercase.
     * @retval string A string converted to all uppercase letters.
     */
    template <class T> inline const string Upper( const T& t )
    {
        string output( t );

        transform( output.begin(), output.end(), output.begin(), ::toupper );

        return output;
    }
    /**
     * @brief Returns a string object from any data type: bool, int, char, etc.
     * @param[in] t Any type of data that can be represented as an alphanumeric string.
     * @retval string A string containing the converted data.
     */
    template <class T> inline const string String( const T& t )
    {
        stringstream ss( t );

        return ss.str();
    }
    const timeval CurrentTime();
    const long unsigned int DiffTime( const timeval& prev, const timeval& current, const long unsigned int& granularity );
    const string DirPath( const string& directory, const string& file, const string& ext = "" );
    const string _FormatString( const long unsigned int& narg, const bitset<CFG_MEM_MAX_BITSET>& flags, const string& caller, const string& fmt, ... );
    const string __FormatString( const long unsigned int& narg, const bitset<CFG_MEM_MAX_BITSET>& flags, const string& caller, const string& fmt, va_list& val );
    #define FormatString( flags, fmt, ... ) _FormatString( PP_NARG( __VA_ARGS__ ), flags, _caller_, fmt, ##__VA_ARGS__ )
    const void _Logger( const long unsigned int& narg, const bitset<CFG_MEM_MAX_BITSET>& flags, const string& caller, const string& fmt, ... );
    #define Logger( flags, fmt, ... ) _Logger( PP_NARG( __VA_ARGS__ ), flags, _caller_, fmt, ##__VA_ARGS__ )
    const long unsigned int NumChar( const string& input, const string& item );
    const vector<string> StrNewlines( const string& input );
    const string StrTime( const timeval& now );
    const vector<string> StrTokens( const string& input );
    const bool iDirectory( const string& dir );
    const bool iFile( const string& file );
    const bool iNumber( const string& input );
    const bool iReadable( const string& file );
    /**
     * @brief The DeleteObject class implements only operator() to be used in easily deleteing lists of pointers or other objects.
     */
    class DeleteObject
    {
        public:
            /**
             * @brief Deletes a pointer.
             * @param[in] ptr The pointer to be deleted.
             * @retval void
             */
            template <class T> inline const void operator() ( const T* ptr ) const
            {
                delete ptr;
            }
    };
    /**
     * @brief Splits a string in the format of key=value. Retains any whitespace in the value.
     * @param[in] key The object to be populated with the key extracted from item.
     * @param[in] val The object to be populated with the value extracted from item.
     * @param[in] item A char or string in the form of key=value to extract data from.
     * @retval false Returned if item does not contain an equal sign.
     * @retval true Returned if data was successfully extracted from item.
     */
    template <class K, class V, class I> inline const bool KeyValue( K& key, V& val, const I& item )
    {
        size_t loc = 0;

        if ( ( loc = item.find( "=" ) ) == string::npos )
            return false;

        key = item.substr( 0, loc - 1 );
        val = item.substr( loc + 1, item.length() );
        loc = key.find_last_not_of( " " );
        key.erase( loc + 1 );
        loc = val.find_first_not_of( " " );
        val.erase( 0, loc );

        return true;
    }
    /**
     * @brief If the contents of keyd == valu, assigns loc = item.
     * @param[in] igncase If true, performs case-insensitive matching.
     * @param[in] found For loop control. If keyd == valu, set to true, otherwise false.
     * @param[in] keyd A string or number that valu must match.
     * @param[in] valu A string or number that is checked for a match against keyd.
     * @param[in] item A string to be copied to to loc if keyd == valu.
     * @param[in] loc A string to be assigned the value of item if keyd == valu.
     * @retval void
     */
    template <class K, class V> inline const void KeySet( const bool& igncase, bool& found, const K& keyd, const V& valu, const string& item, string& loc )
    {
        string key( keyd );
        string val( valu );

        if ( igncase )
        {
            transform( key.begin(), key.end(), key.begin(), ::tolower );
            transform( val.begin(), val.end(), val.begin(), ::tolower );
        }

        if ( key.compare( val ) == 0 )
        {
            loc = item;
            found = true;

            return;
        }

        return;
    }
   /**
     * @brief If the contents of keyd == valu, assigns loc = item.
     * @param[in] igncase If true, performs case-insensitive matching.
     * @param[in] found For loop control. If keyd == valu, set to true, otherwise false.
     * @param[in] keyd A string or number that valu must match.
     * @param[in] valu A string or number that is checked for a match against keyd.
     * @param[in] item Any string, digit, etc to be copied to to loc if keyd == valu.
     * @param[in] loc Any string, digit, etc to be assigned the value of item if keyd == valu.
     * @retval void
     */
    template <class K, class V, class I, class L> inline const void KeySet( const bool& igncase, bool& found, const K& keyd, const V& valu, const I& item, L& loc )
    {
        string key( keyd );
        string val( valu );

        if ( igncase )
        {
            transform( key.begin(), key.end(), key.begin(), ::tolower );
            transform( val.begin(), val.end(), val.begin(), ::tolower );
        }

        if ( key.compare( val ) == 0 )
        {
            string tf( item );

            // Allow bools be any of: true / 1 or false / 0
            transform( tf.begin(), tf.end(), tf.begin(), ::tolower );

            if ( tf.compare( "true" ) == 0 )
                loc = true;
            else if ( tf.compare( "false" ) == 0 )
                loc = false;
            else
                stringstream( item ) >> loc;
            found = true;

            return;
        }

        return;
    }
    const multimap<bool,string> ListDirectory( const string& dir, const bool& recursive, multimap<bool,string>& output, long unsigned int& dir_close, long unsigned int& dir_open );
};

#endif