#ifndef _TILE_BASE_STRING_H
#define _TILE_BASE_STRING_H

#pragma once

#include "tile/base/internal/format.h"
#include "tile/base/internal/meta.h"
#include "tile/base/optional.h"
#include "tile/base/slice.h"

#include <cctype>
#include <limits>
#include <string>
#include <vector>

namespace tile {

template<typename... Args>
std::string
Format(Slice fmt, Args &&...args)
{
    return fmt::format(fmt.ToString(), std::forward<Args>(args)...);
}

template<typename OutputIt, typename... Args>
OutputIt
FormatTo(OutputIt out, Slice fmt, Args &&...args)
{
    return fmt::format_to(out, fmt.ToString(), std::forward<Args>(args)...);
}

// 一些基本函数

bool StartsWith(Slice s, Slice prefix);
bool EndsWith(Slice s, Slice prefix);
bool StartsWithIgnoreCase(Slice s, Slice prefix);
bool EndsWithIgnoreCase(Slice s, Slice prefix);

void Replace(Slice from, Slice to, std::string *str, std::size_t count = std::numeric_limits<std::size_t>::max());
std::string Replace(Slice str, Slice from, Slice to, std::size_t count = std::numeric_limits<std::size_t>::max());

template<typename Pred = int(int)>
enable_if_t<!std::is_constructible<Slice, Pred>::value, Slice>
TrimLeft(Slice s, Pred pred = isspace)
{
    while (!s.empty() && pred(s[0])) { s.RemovePrefix(1); }
    return s;
}

template<typename Pred = int(int)>
enable_if_t<!std::is_constructible<Slice, Pred>::value, Slice>
TrimRight(Slice s, Pred pred = isspace)
{
    while (!s.empty() && pred(s[s.size() - 1])) { s.RemoveSuffix(1); }
    return s;
}

template<typename Pred = int(int)>
enable_if_t<!std::is_constructible<Slice, Pred>::value, Slice>
Trim(Slice s, Pred pred = isspace)
{
    return TrimRight(TrimLeft(s, pred), pred);
}

template<typename T>
enable_if_t<std::is_constructible<Slice, T>::value, Slice>
TrimLeft(Slice s, T slice_like)
{
    Slice cutset(slice_like);
    return TrimLeft(s, [&cutset](char c) { return cutset.find(c) != Slice::npos; });
}

template<typename T>
enable_if_t<std::is_constructible<Slice, T>::value, Slice>
TrimRight(Slice s, T slice_like)
{
    Slice cutset(slice_like);
    return TrimRight(s, [&cutset](char c) { return cutset.find(c) != Slice::npos; });
}

template<typename T>
enable_if_t<std::is_constructible<Slice, T>::value, Slice>
Trim(Slice s, T slice_like)
{
    Slice cutset(slice_like);
    return Trim(s, [&cutset](char c) { return cutset.find(c) != Slice::npos; });
}

std::vector<Slice> Split(Slice s,
                         char delim,
                         bool keep_empty             = false,
                         std::size_t max_split_parts = std::numeric_limits<std::size_t>::max());
std::vector<Slice> Split(Slice s,
                         Slice delim,
                         bool keep_empty             = false,
                         std::size_t max_split_parts = std::numeric_limits<std::size_t>::max());

std::string Join(const std::vector<std::string> &parts, Slice delim);
std::string Join(const std::vector<Slice> &parts, Slice delim);
std::string Join(const std::initializer_list<Slice> &parts, Slice delim);

char ToUpper(char c);
char ToLower(char c);
void ToUpper(std::string *str);
void ToLower(std::string *str);
std::string ToUpper(Slice s);
std::string ToLower(Slice s);

bool Equals(Slice a, Slice b, std::size_t len = std::numeric_limits<std::size_t>::max());
bool EqualsIgnoreCase(Slice a, Slice b, std::size_t len = std::numeric_limits<std::size_t>::max());

// TryParse
template<typename T, typename = void>
struct TryParseTraits;

template<typename T, typename... Args>
std::optional<T>
TryParse(Slice s, const Args &...args)
{
    return TryParseTraits<T>::TryParse(s, args...);
}

template<typename T, typename>
struct TryParseTraits {
    template<typename... Args>
    static std::optional<T> TryParse(Slice s, const Args &...args)
    {
        return TryParse(std::common_type<T>(), s, args...);
    }
};

// String to bool
// 1. "true" or "false"
// 2. "yes" or "no"
// 3. "on" or "off"
// 4. non-zero or zero
template<>
struct TryParseTraits<bool> {
    static std::optional<bool> TryParse(Slice s, bool recognizes_alpbabet_symbol = true, bool ignore_case = true);
};

// string to number
template<typename T>
struct TryParseTraits<T, typename std::enable_if<std::is_integral<T>::value>::type> {
    static std::optional<T> TryParse(Slice s, int base = 10);
};

// string to decimal
template<typename T>
struct TryParseTraits<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
    static std::optional<T> TryParse(Slice s);
};

}// namespace tile

#endif// _TILE_BASE_STRING_H
