#pragma once

#include <stdarg.h>
#include <stdio.h>
#include <string>
#include <vector>

namespace base {

inline std::string join(std::string val) {
  return val;
}
template<class ...ARGS>
inline std::string join(std::string val, ARGS ...args) {
  return val + my_sum(args...);
}

/*  UTF - 8，是一种变长编码，具体规则如下：
    1）对于单字节的符号，字节的第一位设为0，后面7位为这个符号的unicode码。因此对于英语字母，UTF
   - 8编码和ASCII码是相同的。 2）对于n字节的符号（n >
   1），第一个字节的前n位都设为1，第n +
   1位设为0，后面字节的前两位一律设为10。剩下的没有提及的二进制位，全部为这个符号的unicode码。

    如表 :
    1字节 0xxxxxxx
    2字节 110xxxxx 10xxxxxx
    3字节 1110xxxx 10xxxxxx 10xxxxxx
    4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/

// 将string中的中文分解成单个字, 适用于UTF-8编码
inline void split_word(const std::string& words, std::vector<std::string>* characters) {
  int num = words.size();
  int i = 0;
  while (i < num) {
    int size = 1;
    if (words[i] & 0x80) {
      char temp = words[i];
      temp <<= 1;
      do {
        temp <<= 1;
        ++size;
      } while (temp & 0x80);
    }
    characters->push_back(words.substr(i, size));
    i += size;
  }
}

inline bool endswith(const std::string& str, const std::string& end) {
  int srclen = str.size();
  int endlen = end.size();
  if(srclen >= endlen) {
    std::string temp = str.substr(srclen - endlen, endlen);
    if(temp.compare(end) == 0)
      return true;
  }
  return false;
}

/**
 * 删除输入字符串指定长度，返回被删除部分，
 */
inline std::string cut(std::string* in, ::size_t n) {
  if (in == NULL) {
    return "";
  }
  if (n >= in->length()) {
    n = in->length();
  }
  std::string cuts = in->substr(0, n);
  in->erase(0, n);
  return cuts;
}

inline bool startswith(const std::string& str, const std::string& start) {
  int srclen = str.size();
  int startlen = start.size();
  if(srclen >= startlen) {
    std::string temp = str.substr(0, startlen);
    if(temp.compare(start) == 0)
      return true;
  }
  return false;
}

inline std::string trimsuffix(const std::string &str, const std::string &suffix) {
  ::size_t p = str.find_last_of(suffix);
  if (p != (str.size() - suffix.size())) {
    return str;
  }
  return str.substr(0, p);
}

inline std::string trimprefix(const std::string &str, const std::string &prefix) {
  if (startswith(str, prefix)) {
    return str.substr(prefix.size());
  }
  return str;
}

inline std::string trim_left_unicode(const std::string& str,
    const std::vector<std::string>& cutset) {
  std::string s = str;
  for (auto it = cutset.begin(); it != cutset.end();) {
    if (startswith(s, *it)) {
      s.erase(0, it->length());
      it = cutset.begin();
      continue;
    }
    ++it;
  }
  return s;
}

inline std::string trim_left(const std::string &str, const std::string &cutset) {
  if (str.empty() || cutset.empty()) {
      return str;
  }
  std::vector<std::string> words;
  split_word(cutset, &words);
  return trim_left_unicode(str, words);
}

inline std::string trim_right_unicode(const std::string& str,
    const std::vector<std::string>& cutset) {
  std::string s = str;
  for (auto it = cutset.begin(); it != cutset.end();) {
    if (endswith(s, *it)) {
      s.erase(s.length() - it->length(), it->length());
      it = cutset.begin();
      continue;
    }
    ++it;
  }
  return s;
}

inline std::string trim_right(const std::string &str, const std::string &cutset) {
  if (str.empty() || cutset.empty()) {
      return str;
  }
  std::vector<std::string> words;
  split_word(cutset, &words);
  return trim_right_unicode(str, words);
}

inline std::string trim(const std::string &str,
    const std::string& cutset = "\n \t\r") {
  if (str.empty() || cutset.empty()) {
    return str;
  }
  std::vector<std::string> words;
  split_word(cutset, &words);
  return trim_right_unicode(trim_left_unicode(str, words), words);
}

inline std::string replace(const std::string& str, const std::string &old_str, const std::string& new_str) {
  std::string::size_type pos = 0;
  if (old_str.empty()) {
    return str;
  }
  std::string temp = str;
  while (std::string::npos != (pos = temp.find(old_str))) {
    temp = temp.replace(pos, old_str.size(), new_str);
  }
  return temp;
}

inline std::string join(const std::vector<std::string> v) {
  std::string t;
  for (::size_t i = 0; i < v.size(); ++i) {
    t += v[i];
  }
  return t;
}

inline std::string upper(const std::string& text) {
  std::string dest = text;
  char* p = (char*)dest.data();
  size_t length = dest.length();
  for (size_t i = 0; i < length; ++i) {
    int c = (unsigned char)p[i];
    p[i] = toupper(c);
  }
  return dest;
}

inline std::string lower(const std::string& text) {
  std::string dest = text;
  char* p = (char*)dest.data();
  size_t length = dest.length();
  for (size_t i = 0; i < length; ++i) {
    int c = (unsigned char)p[i];
    p[i] = tolower(c);
  }
  return dest;
}

inline std::string format(const char* format, ...) {
  std::string tmp;
  va_list vl;
  va_start(vl, format);
  size_t num = vsnprintf(0, 0, format, vl);
  if (num >= tmp.capacity()) tmp.reserve(num + sizeof(char));

  tmp.resize(num);
  vsnprintf((char*)tmp.data(), tmp.capacity(), format, vl);
  va_end(vl);
  return tmp;
}

inline std::string to_style_string(const char* bytes, ::size_t len) {
  std::string out;
  char buf[8] = {0};
  int base = 16;
  for (size_t i = 0; i < len; ++i) {
    if (base == 16) {
      if (false) {
        snprintf(buf, sizeof(buf), "0x%02X,", bytes[i]);
      } else {
        snprintf(buf, sizeof(buf), "%02X ", bytes[i]);
      }
    } else if (base == 10) {
      snprintf(buf, sizeof(buf), "%03d ", bytes[i]);
    }
    out += buf;
  }
  return out;
}

inline std::string from_style_string(const std::string& str) {
  const char* data = str.c_str();
  ::size_t offset = 0;
  ::size_t index = 0;
  std::string out;
  while (offset < str.length()) {
    char* end = NULL;
    long h = strtol(data + offset, &end, 16);
    if (*end == '\0') {
      //结束
      out.push_back((char)h);
      break;
    }
    //出错
    if ((h == 0) && (end == (data + offset))) {
      return out;
    }
    out.push_back((char)h);
    index = index + 1;
    offset = end - data;
  }
  return out;
}

} //namespace base
