/**
 *
 * @file src/common/Tools.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Samuel Thibault
 * @author Johnny Jazeix
 * @author Mathieu Faverge
 * @author Philippe Swartvagher
 * @author Olivier Lagrasse
 * @author Augustin Degomme
 *
 * @date 2024-07-17
 */

#include <cstdio>
#include <iostream>
/* -- */
#include <locale.h> // For dots or commas separator in double numbers
#include <time.h>
/* -- */
#include "common.hpp"
#include "Tools.hpp"

#if defined WIN32 && !defined(__MINGW32__)
#define sscanf sscanf_s
#endif
/* -- */
using namespace std;

bool convert_to_double(const std::string &arg, double *val) {
    if (arg.compare("-nan") == 0 || arg.compare("nan") == 0 || arg.compare("inf") == 0) {
        *val = 0;
        return true;
    }

    int nb_read = 0;
    // Try to convert first in the current locale
    sscanf(arg.c_str(), "%lf%n", val, &nb_read);

    if ((size_t)nb_read == arg.size()) {
        return true; // It is the good format
    }
    else { // We change the locale to test
        bool is_english_system_needed = false; // We have dot as separator of decimal and integer.

        if (arg.find('.') != string::npos) {
            is_english_system_needed = true;
        }

        // We had dots initially, we need to have the english system
        if (is_english_system_needed) {
            if ((setlocale(LC_NUMERIC, "C") == nullptr) && (setlocale(LC_NUMERIC, "en_GB.UTF-8") == nullptr)) {
                vite_warning("The locale en_GB.UTF-8 is unavailable so the decimal pointed will not be printed");
            }
        }
        else { // It is comma separated

            if ((setlocale(LC_NUMERIC, "fr_FR.UTF-8") == nullptr) && (setlocale(LC_NUMERIC, "French") == nullptr)) {
                vite_warning("The locale fr_FR.UTF-8 is unavailable so the decimal with comma will not be printed");
            }
        }

        // Reads the value in the new locale
        sscanf(arg.c_str(), "%lf%n", val, &nb_read);
        return (size_t)nb_read == arg.size();
    }
}

double clockGet() {
#if (defined X_ARCHalpha_compaq_osf1) || (defined X_ARCHi686_mac)
    struct rusage data;
    getrusage(RUSAGE_SELF, &data);
    return (((double)data.ru_utime.tv_sec + (double)data.ru_stime.tv_sec) + ((double)data.ru_utime.tv_usec + (double)data.ru_stime.tv_usec) * 1.0e-6L);
#elif defined _POSIX_TIMERS && defined CLOCK_REALTIME
    struct timespec tp;

    clock_gettime(CLOCK_REALTIME, &tp); /* Elapsed time */

    return ((double)tp.tv_sec + (double)tp.tv_nsec * (double)1.0e-9L);
#else
    return 0.;
#endif
}
