/** @file utils.cpp
 *
 *  @author Cory Alexander Balaton (coryab)
 *  @author Janita Ovidie Sandtrøen Willumsen (janitaws)
 *
 *  @version 1.0
 *
 *  @brief Implementation of the utils
 *
 *  @bug No known bugs
 * */
#include "utils.hpp"

namespace utils {

std::string scientific_format(double d, int width, int prec)
{
    std::stringstream ss;
    ss << std::setw(width) << std::setprecision(prec) << std::scientific << d;
    return ss.str();
}

std::string scientific_format(const std::vector<double> &v, int width, int prec)
{
    std::stringstream ss;
    for (double elem : v) {
        ss << scientific_format(elem, width, prec);
    }
    return ss.str();
}

bool mkpath(std::string path, int mode)
{
    std::string cur_dir;
    std::string::size_type pos = -1;
    struct stat buf;

    if (path.back() != '/') {
        path += '/';
    }
    while (true) {
        pos++;
        pos = path.find('/', pos);
        if (pos != std::string::npos) {
            cur_dir = path.substr(0, pos);
            if (mkdir(cur_dir.c_str(), mode) != 0
                && stat(cur_dir.c_str(), &buf) != 0) {
                return false;
            }
        }
        else {
            break;
        }
    }
    return true;
}

std::string dirname(const std::string &path)
{
    return path.substr(0, path.find_last_of("/"));
}

std::string concatpath(const std::string &left, const std::string &right)
{
    if (left.back() != '/' and right.front() != '/') {
        return left + '/' + right;
    }
    else {
        return left + right;
    }
}

void print_sp_matrix_structure(const arma::sp_cx_mat &A)
{
    using namespace std;
    using namespace arma;

    // Declare a C-style 2D array of strings.
    string S[A.n_rows][A.n_cols];

    // Initialise all the strings to " ".
    for (int i = 0; i < A.n_rows; i++) {
        for (int j = 0; j < A.n_cols; j++) {
            S[i][j] = " ";
        }
    }

    // Next, we want to set the string to a dot at each non-zero element.
    // To do this we use the special loop iterator from the sp_cx_mat class
    // to help us loop over only the non-zero matrix elements.
    sp_cx_mat::const_iterator it = A.begin();
    sp_cx_mat::const_iterator it_end = A.end();

    int nnz = 0;
    for (; it != it_end; ++it) {
        S[it.row()][it.col()] = "•";
        nnz++;
    }

    // Finally, print the matrix to screen.
    cout << endl;
    for (int i = 0; i < A.n_rows; i++) {
        cout << "| ";
        for (int j = 0; j < A.n_cols; j++) {
            cout << S[i][j] << " ";
        }
        cout << "|\n";
    }

    cout << endl;
    cout << "matrix size: " << A.n_rows << "x" << A.n_cols << endl;
    cout << "non-zero elements: " << nnz << endl;
    cout << endl;
}

std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> result;
    std::stringstream ss(s);
    std::string item;

    while (getline(ss, item, delim)) {
        result.push_back(trim(item));
    }

    return result;
}

inline std::string& ltrim(std::string& s, const char* t)
{
    s.erase(0, s.find_first_not_of(t));
    return s;
}

inline std::string& rtrim(std::string& s, const char* t)
{
    s.erase(s.find_last_not_of(t) + 1);
    return s;
}

inline std::string& trim(std::string& s, const char* t)
{
    return ltrim(rtrim(s, t), t);
}

inline std::string ltrim_copy(std::string s, const char* t)
{
    return ltrim(s, t);
}

inline std::string rtrim_copy(std::string s, const char* t)
{
    return rtrim(s, t);
}

inline std::string trim_copy(std::string s, const char* t)
{
    return trim(s, t);
}
} // namespace utils
