/* Misc. utility functions for internal use
   Chris Toney <chris.toney at usda.gov>
   Copyright (c) 2023-2025 gdalraster authors
*/

#ifndef RCPP_UTIL_H_
#define RCPP_UTIL_H_

#include <Rcpp.h>
#include <RcppInt64>

#ifndef GDALRASTER_TYPES_H_
#include <cpl_port.h>
#include <cpl_string.h>
Rcpp::CharacterVector wrap_gdal_string_list_(const CPLStringList &string_list);
std::string get_data_ptr(const Rcpp::RObject &x);
#endif  // GDALRASTER_TYPES_H_

#include <algorithm>
#include <cctype>
#include <cstdint>
#include <limits>
#include <string>

constexpr int64_t MAX_INT_AS_R_NUMERIC_ = 9007199254740991;

// as defined in the bit64 package src/integer64.h:
// #define NA_INTEGER64 LLONG_MIN
// #define ISNA_INTEGER64(X)((X) == NA_INTEGER64)
// #define MIN_INTEGER64 LLONG_MIN+1
// #define MAX_INTEGER64 LLONG_MAX
// replaced here with:
constexpr int64_t NA_INTEGER64 = std::numeric_limits<int64_t>::min();
constexpr bool ISNA_INTEGER64(int64_t x) {return (x == NA_INTEGER64);}
constexpr int64_t MIN_INTEGER64 = std::numeric_limits<int64_t>::min() + 1;
constexpr int64_t MAX_INTEGER64 = std::numeric_limits<int64_t>::max();


Rcpp::NumericMatrix df_to_matrix_(const Rcpp::DataFrame &df);
Rcpp::IntegerMatrix df_to_int_matrix_(const Rcpp::DataFrame &df);

Rcpp::NumericMatrix xy_robject_to_matrix_(const Rcpp::RObject &xy);

Rcpp::CharacterVector path_expand_(const Rcpp::CharacterVector &path);

Rcpp::CharacterVector normalize_path_(const Rcpp::CharacterVector &path,
                                      int must_work);
Rcpp::CharacterVector normalize_path_(const Rcpp::CharacterVector &path,
                                      int must_work = NA_LOGICAL);

Rcpp::CharacterVector enc_to_utf8_(const Rcpp::CharacterVector &x);

Rcpp::CharacterVector strsplit_(const Rcpp::CharacterVector &x,
                                const Rcpp::CharacterVector &split);

Rcpp::String paste_collapse_(const SEXP &x, const Rcpp::String &s);

std::string str_toupper_(const std::string &s);
std::string str_tolower_(const std::string &s);

Rcpp::CharacterVector remove_leading_dashes_(const Rcpp::CharacterVector &x);

bool contains_str_(const Rcpp::CharacterVector &v, const Rcpp::String &s,
                   bool match_if_substr);
bool contains_str_(const Rcpp::CharacterVector &v, const Rcpp::String &s,
                   bool match_if_substr = false);

bool has_space_char_(const std::string &s);

bool is_namespace_loaded_(const Rcpp::String &pkg);

bool is_gdalraster_obj_(const Rcpp::RObject &x);

// cli::cli_* wrappers for use from C++
void cli_alert_(const std::string &text);
void cli_alert_info_(const std::string &text);
void cli_alert_warning_(const std::string &text);
void cli_alert_danger_(const std::string &text);
void cli_alert_success_(const std::string &text);
void cli_text_(const std::string &text);
void cli_ul_();
void cli_li_(const std::string &text);
void cli_end_();
void cli_cat_line_();

// case-insensitive comparator for std::map
// https://stackoverflow.com/questions/1801892/how-can-i-make-the-mapfind-operation-case-insensitive
struct _ci_less {
    struct nocase_compare {
        bool operator() (const unsigned char& c1,
                         const unsigned char& c2) const {
            return std::tolower(c1) < std::tolower(c2);
        }
    };
    bool operator() (const std::string & s1, const std::string & s2) const {
        return std::lexicographical_compare(s1.begin(), s1.end(),
                                            s2.begin(), s2.end(),
                                            nocase_compare());
    }
};

#endif  // RCPP_UTIL_H_
