#pragma once
#include "./std_headers.h"

struct NavPath {
  vector<string> path;
  string &operator [](size_t i);
  NavPath &operator =(vector<string> const &_path);
  string pop_front();
};

// Return a string with each line of s indented with indent
string indentString(string const &s, string const &indent);

string quoteStringJs(string const &s);
// Return a string quoted by C's convention, ie, \\n or \\" or \\012
string quoteStringC(string const &s);

// Realpath(3), but c++ified.
string myRealpath(string const &path);
string myDirname(string const &path);
string firstPathComponent(string const &path);
string notFirstPathComponent(string const &path);

// Join two strings with a ., unless one is empty in which case no dot is added.
string dotJoin(string const &a, string const &b);

/*
  Create a readable string of the given length with a secure (SHA-1) hash of the data.
  Should be collision-resistant up to a small fraction of 58^(len/2) items
*/
string base58Hash(U8 *raw, size_t rawLen, size_t strLen);
string secureHashToIdentSuffix(string const &data, int len=20);

void writeFile(int fd, string const &contents, string const &errmsgfn);
void writeFile(string const &fn, string const &contents);
/*
  Write a file, but read it first and do nothing if the contents haven't changed.
*/
bool writeFileIfChanged(string const &fn, string const &contents);

/*
  Read a file, returning the contents as a string.
  Throws a runtime_error if read(2) returns an error
*/
string readFile(int fd, string const &errmsgfn);
/*
  Read a file, returning the contents as a string.
  Throws a runtime_error if it can't open the file, or reading fails
*/
string readFile(string const &fn);


string getRandTok(size_t len);
string getTimeTok(struct tm *t);
string getTimeStamp(struct tm *t);

// true if a word can't be transparently interpolated into a shell command
bool needsShellEscape(string const &a);
// Escape a as needed for shell
string shellEscape(string const &a);
// Escape all the args, and return in one string separated by spaces
string shellEscape(vector<string> const &args);

string cppEscape(string_view const &s);

/*
  Cook up a relative path starting from the `from` directory to the `to` path
  Inspired by Node's path.relative, but not guaranteed to be the same.
  https://nodejs.org/dist/latest-v12.x/docs/api/path.html#path_path_relative_from_to
*/
string relativePath(string from, string to);

/*
  Call the function for each line. Including blank lines.
  The newline isn't included in the callback arg.
*/
void forEachLine(string const &buf, std::function<void(string const &s)> onLine);

vector<string> splitChar(string const &buf, char sep);
vector<string> splitString(string const &buf, string const &sep);
vector<string> splitSpaces(string const &buf);

/*
  Split s into two strings, at dot number dotCount. For example:
    splitAtDotNumber("foo.bar.buz"s, 2) == make_tuple("foo.bar"s, "buz"s)
*/
tuple<string, string> splitAtDotNumber(string const &s, int dotCount);

// Replace multiple `\n`s with a single one
string withoutBlankLines(string const &buf);

// Join the strings with a separator character.
string joinChar(vector<string> const &tokens, char sep);
// Join the strings with a separator string
string joinString(vector<string> const &tokens, string const &sep);
// Join the strings with spaces
string joinSpaces(vector<string> const &tokens);

// Return true if s starts with prefix
bool startsWith(string const &s, string const &prefix);
// Return true if s ends with suffix
bool endsWith(string const &s, string const &suffix);

string withNewSuffix(string const &s, string const &oldSuffix, string const &newSuffix);

bool isHexDigit(u_char c);
int fromHexDigit(u_char c);
u_char toHexDigit(int x);
u_char toOctalDigit(int x);

template<typename TT, size_t... I>
void emitTuple(ostream &s, TT const &it, std::index_sequence<I...>);

template <typename A, typename B> ostream &operator<<(ostream &s, pair<A, B> const &it);
template <typename... Ts> ostream &operator<<(ostream &s, tuple<Ts...> const &it);
template <typename T> ostream &operator<<(ostream &s, vector<T> const &it);
template <> ostream &operator<<(ostream &s, vector<bool> const &it);
template <typename T, size_t N> ostream &operator<<(ostream &s, array<T, N> const &it);
template <typename A, typename B> ostream &operator<<(ostream &s, pair<A, B> const &it);
template <typename K, typename V> ostream &operator<<(ostream &s, map<K, V> const &it);


template<typename... Ts>
ostream &operator<<(ostream &s, tuple<Ts...> const &it)
{
  auto is = std::make_index_sequence<sizeof...(Ts)>();
  emitTuple(s, it, is);
  return s;
}

template<typename T>
ostream &operator<<(ostream &s, vector<T> const &it)
{
  s << "[";
  char const *sep = "";
  for (auto const &a : it) {
    s << sep << a;
    sep = " ";
  }
  s << "]";
  return s;
}

template <typename T, size_t N>
ostream &operator<<(ostream &s, array<T, N> const &it)
{
  s << "[";
  char const *sep = "";
  for (auto const &a : it) {
    s << sep << a;
    sep = " ";
  }
  s << "]";
  return s;
}


template <typename A, typename B>
ostream &operator<<(ostream &s, pair<A, B> const &it)
{
  s << it.first << ":" << it.second;
  return s;
}

template <typename K, typename V>
ostream &operator<<(ostream &s, map<K, V> const &it)
{
  s << "{";
  char const *sep = "";
  for (auto const &a : it) {
    s << sep << a.first << ": " << a.second;
    sep = ", ";
  }
  s << "}";
  return s;
}

template <typename T>
ostream & operator <<(ostream &s, shared_ptr<T> const &it)
{
  if (!it) return s << "null";
  return s << *it;
}

template<typename TT, size_t... I>
void emitTuple(ostream &s, TT const &it, std::index_sequence<I...>)
{
  s << "(";
  (..., (s << (I==0 ? "" : ", ") << std::get<I>(it)));
  s << ")";
}


/*
  I wish this is what std::to_string did.
 */
template <typename T>
string repr(T const &it)
{
  ostringstream oss;
  oss << it;
  return oss.str();
}

template <typename T>
string repr(T const *it)
{
  if (it == nullptr) return "null";
  return repr(*it);
}

template<> string repr(char const *it);
template<> string repr(bool const &it);
template<> string repr(int const &it);
template<> string repr(float const &it);
template<> string repr(double const &it);
template<> string repr(size_t const &it);

string repr_0_6f(double value);
string repr_0_3f(double value);
string repr_0_1f(double value);
string repr_g(double value);
string repr_0_1g(double value);
string repr_0_2g(double value);
string repr_0_3g(double value);
string repr_02x(uint32_t value);
string repr_04x(uint32_t value);
string repr_08x(uint32_t value);
string repr_016x(uint64_t value);
string repr_ptr(void * value);
string repr_filesize(size_t value);
string repr_clock(long t);
string repr_clockper(long t, long iters);
string repr_tscper(long t, long iters);
string repr_time(double t);

string leftPadTo(string const &s, size_t width, char pad=' ');
string rightPadTo(string const &s, size_t width, char pad=' ');

void console(string const &line);

template<typename T>
vector<T> concat(vector<T> const &a, vector<T> const &b)
{
  vector<T> ret;
  for (auto &it : a) ret.push_back(it);
  for (auto &it : b) ret.push_back(it);
  return ret;
}

/*
  Thread-safe stream IO.
  Could replace with C++20 osyncstream
*/
struct L : ostringstream {
  // line, time, color
  using lineq = deque<tuple<string, double, U32>>;

  L(ostream &_outStream);
  L(ostream &_outStream, lineq &_outLineq, U32 _color);
  L(); // use cerr
  ~L();


  ostream * outStream;
  lineq * outLineq;
  U32 color;
};

#define assertlog(COND, MSG) \
  do { if (__builtin_expect(!(COND), 0)) { \
    L() << __FILE__ << ":" << __LINE__ << " in " << __func__ << " checking " << #COND << ": " << MSG; \
    abort(); \
  } } while(0)

#define dielog(MSG) \
  do { \
    L() << __FILE__ << ":" << __LINE__ << " in " << __func__ << ": " << MSG; \
    abort(); \
  } while(0)

#define LOGV(X) " " #X "=" << (X)
