/*
 *       File name:  libstilton/libcommon.cc
 *         Project:  cnrun
 *          Author:  Andrei Zavada <johnhommer@gmail.com>
 * Initial version:  2014-03-23
 *
 *         Purpose:  sundry bits too big for inlining
 *
 *         License:  GPL
 */

#if HAVE_CONFIG_H && !defined(VERSION)
#  include "config.h"
#endif

#include <cmath>
#include <cstring>
#include <string>
#include <list>

#include <cstdarg>
#include <cerrno>
#include <unistd.h>

#include "string.hh"
#include "alg.hh"
#include "misc.hh"


using namespace std;
using namespace cnrun::stilton;


void
C_verprintf::
vp( int level, const char* fmt, ...) const
{
        if ( level < verbose_threshold() ) {
                va_list ap;
                va_start (ap, fmt);
                vprintf( fmt, ap);
                va_end (ap);
        }
}

void
C_verprintf::
vp( int level, FILE* f, const char* fmt, ...) const
{
        if ( level < verbose_threshold() ) {
                va_list ap;
                va_start (ap, fmt);
                vfprintf( f, fmt, ap);
                va_end (ap);
        }
}


string
cnrun::stilton::str::
svasprintf( const char* fmt, va_list ap)
{
        char *_;
        if (vasprintf( &_, fmt, ap) <= 0)
                abort();
        string ret {_};
        free( (void*)_);
        return move(ret);
}



string
cnrun::stilton::str::
sasprintf( const char* fmt, ...)
{
        char *_;
        va_list ap;
        va_start (ap, fmt);
        if (vasprintf( &_, fmt, ap) <= 0)
                abort();
        va_end (ap);

        string ret {_};
        free( (void*)_);
        return move(ret);
}



string
cnrun::stilton::str::
trim( const string& r0)
{
        string r (r0);
        auto rsize = r.size();
        if ( rsize == 0 )
                return r;
        while ( rsize > 0 && r[rsize-1] == ' ' )
                --rsize;
        r.resize( rsize);
        r.erase( 0, r.find_first_not_of(" \t"));
        return move(r);
}

string
cnrun::stilton::str::
pad( const string& r0, size_t to)
{
        string r (to, ' ');
        memcpy( (void*)r.data(), (const void*)r0.data(), min( to, r0.size()));
        return move(r);
}



list<string>
cnrun::stilton::str::
tokens_trimmed( const string& s_, const char* sep)
{
        string s {s_};
        list<string> acc;
        char   *pp,
               *p = strtok_r( &s[0], sep, &pp);
        while ( p ) {
                acc.emplace_back( trim(p));
                p = strtok_r( NULL, sep, &pp);
        }
        return move(acc);
}

list<string>
cnrun::stilton::str::
tokens( const string& s_, const char* sep)
{
        string s {s_};
        list<string> acc;
        char   *pp,
               *p = strtok_r( &s[0], sep, &pp);
        while ( p ) {
                acc.emplace_back( p);
                p = strtok_r( NULL, sep, &pp);
        }
        return move(acc);
}




void
cnrun::stilton::str::
decompose_double( double value, double *mantissa, int *exponent)
{
        char buf[32];
        snprintf( buf, 31, "%e", value);
        *strchr( buf, 'e') = '|';
        sscanf( buf, "%lf|%d", mantissa, exponent);
}




string&
cnrun::stilton::str::
homedir2tilda( string& inplace)
{
        const char *home = getenv("HOME");
        if ( home )
                if ( inplace.compare( 0, strlen(home), home) == 0 )
                        inplace.replace( 0, strlen(home), "~");
        return inplace;
}

string
cnrun::stilton::str::
homedir2tilda( const string& v)
{
        string inplace (v);
        const char *home = getenv("HOME");
        if ( home )
                if ( inplace.compare( 0, strlen(home), home) == 0 )
                        inplace.replace( 0, strlen(home), "~");
        return inplace;
}

string&
cnrun::stilton::str::
tilda2homedir( string& inplace)
{
        const char *home = getenv("HOME");
        if ( home ) {
                size_t at;
                while ( (at = inplace.find( '~')) < inplace.size() )
                        inplace.replace( at, 1, home);
        }
        return inplace;
}

string
cnrun::stilton::str::
tilda2homedir( const string& v)
{
        string inplace (v);
        const char *home = getenv("HOME");
        if ( home ) {
                size_t at;
                while ( (at = inplace.find( '~')) < inplace.size() )
                        inplace.replace( at, 1, home);
        }
        return inplace;
}



string
cnrun::stilton::str::
dhms( double seconds, int dd)
{
        bool    positive = seconds >= 0.;
        if ( not positive )
                seconds = -seconds;

        int     s = (int)seconds % 60,
                m = (int)seconds/60 % 60,
                h = (int)seconds/60/60 % (60*60),
                d = (int)seconds/60/60/24 % (60*60*24);
        double  f = seconds - floor(seconds);

        using cnrun::stilton::str::sasprintf;
        string  f_ = ( dd == 0 )
                ? ""
                : sasprintf( ".%0*d", dd, (int)(f*pow(10, dd)));
        return ( d > 0 )
                ? sasprintf( "%dd %dh %dm %d%ss", d, h, m, s, f_.c_str())
                : ( h > 0 )
                ? sasprintf( "%dh %dm %d%ss", h, m, s, f_.c_str())
                : ( m > 0 )
                ? sasprintf( "%dm %d%ss", m, s, f_.c_str())
                : sasprintf( "%d%ss", s, f_.c_str());
}

string
cnrun::stilton::str::
dhms_colon( double seconds, int dd)
{
        bool    positive = seconds >= 0.;
        if ( not positive )
                seconds = -seconds;

        int     s = (int)seconds % 60,
                m = (int)seconds/60 % 60,
                h = (int)seconds/60/60 % (60*60),
                d = (int)seconds/60/60/24 % (60*60*24);
        double        f = seconds - floor(seconds);

        using cnrun::stilton::str::sasprintf;
        string  f_ = ( dd == 0 )
                ? ""
                : sasprintf( ".%0*d", dd, (int)(f*pow(10, dd)));

        return sasprintf( "%dd %02d:%02d:%02d%ss", d, h, m, s, f_.c_str());
}





namespace {
int
n_frac_digits( double v)
{
        int d = 0;
        double dummy;
        while ( fabs( modf( v, &dummy)) > 1e-6 ) {
                v *= 10.;
                ++d;
        }
        return d;
}
}

string
cnrun::stilton::str::
double_dot_aligned_s( double val, const size_t int_width, const size_t frac_width)
{
        string ret;

        val = round(val * pow(10., frac_width)) / pow(10., frac_width);

        double  intval;
        double  fracval = modf( val, &intval);
        int     frac_digits = n_frac_digits( val);
        int     frac_pad = frac_width - frac_digits;
        if ( frac_pad < 1 ) {
                frac_digits = frac_width;
                frac_pad = 0;
        }

        if ( frac_digits )
                if ( (int)intval )
                        ret = sasprintf(
                                "% *d.%0*ld%*s",
                                int(int_width), int(intval),
                                frac_digits, (long)round(pow(10., frac_digits) * fabs( fracval)),
                                frac_pad, " ");
                else
                        ret = sasprintf(
                                "%*s.%0*ld%*s",
                                int(int_width), " ",
                                frac_digits, (long)round(pow(10., frac_digits) * fabs( fracval)),
                                frac_pad, " ");

        else
                if ( (int)intval )
                        ret = sasprintf(
                                "%*d.%-*s",
                                int(int_width), int(intval), int(frac_width), " ");
                else
                        ret = sasprintf(
                                "%-*s0%-*s",
                                int(int_width), " ", int(frac_width), " ");

        return move(ret);
}

// Local Variables:
// Mode: c++
// indent-tabs-mode: nil
// tab-width: 8
// c-basic-offset: 8
// End:
