#ifndef _TILE_BASE_SLICE_H
#define _TILE_BASE_SLICE_H

#pragma once

#include "tile/base/internal/format.h"

#include <algorithm>
#include <array>
#include <cassert>
#include <cstring>
#include <mutex>
#include <string>
#include <utility>

namespace tile {
namespace detail {
static constexpr char kChars[] = {
    char(-128), char(-127), char(-126), char(-125), char(-124), char(-123), char(-122), char(-121), char(-120),
    char(-119), char(-118), char(-117), char(-116), char(-115), char(-114), char(-113), char(-112), char(-111),
    char(-110), char(-109), char(-108), char(-107), char(-106), char(-105), char(-104), char(-103), char(-102),
    char(-101), char(-100), char(-99),  char(-98),  char(-97),  char(-96),  char(-95),  char(-94),  char(-93),
    char(-92),  char(-91),  char(-90),  char(-89),  char(-88),  char(-87),  char(-86),  char(-85),  char(-84),
    char(-83),  char(-82),  char(-81),  char(-80),  char(-79),  char(-78),  char(-77),  char(-76),  char(-75),
    char(-74),  char(-73),  char(-72),  char(-71),  char(-70),  char(-69),  char(-68),  char(-67),  char(-66),
    char(-65),  char(-64),  char(-63),  char(-62),  char(-61),  char(-60),  char(-59),  char(-58),  char(-57),
    char(-56),  char(-55),  char(-54),  char(-53),  char(-52),  char(-51),  char(-50),  char(-49),  char(-48),
    char(-47),  char(-46),  char(-45),  char(-44),  char(-43),  char(-42),  char(-41),  char(-40),  char(-39),
    char(-38),  char(-37),  char(-36),  char(-35),  char(-34),  char(-33),  char(-32),  char(-31),  char(-30),
    char(-29),  char(-28),  char(-27),  char(-26),  char(-25),  char(-24),  char(-23),  char(-22),  char(-21),
    char(-20),  char(-19),  char(-18),  char(-17),  char(-16),  char(-15),  char(-14),  char(-13),  char(-12),
    char(-11),  char(-10),  char(-9),   char(-8),   char(-7),   char(-6),   char(-5),   char(-4),   char(-3),
    char(-2),   char(-1),   char(0),    char(1),    char(2),    char(3),    char(4),    char(5),    char(6),
    char(7),    char(8),    char(9),    char(10),   char(11),   char(12),   char(13),   char(14),   char(15),
    char(16),   char(17),   char(18),   char(19),   char(20),   char(21),   char(22),   char(23),   char(24),
    char(25),   char(26),   char(27),   char(28),   char(29),   char(30),   char(31),   char(32),   char(33),
    char(34),   char(35),   char(36),   char(37),   char(38),   char(39),   char(40),   char(41),   char(42),
    char(43),   char(44),   char(45),   char(46),   char(47),   char(48),   char(49),   char(50),   char(51),
    char(52),   char(53),   char(54),   char(55),   char(56),   char(57),   char(58),   char(59),   char(60),
    char(61),   char(62),   char(63),   char(64),   char(65),   char(66),   char(67),   char(68),   char(69),
    char(70),   char(71),   char(72),   char(73),   char(74),   char(75),   char(76),   char(77),   char(78),
    char(79),   char(80),   char(81),   char(82),   char(83),   char(84),   char(85),   char(86),   char(87),
    char(88),   char(89),   char(90),   char(91),   char(92),   char(93),   char(94),   char(95),   char(96),
    char(97),   char(98),   char(99),   char(100),  char(101),  char(102),  char(103),  char(104),  char(105),
    char(106),  char(107),  char(108),  char(109),  char(110),  char(111),  char(112),  char(113),  char(114),
    char(115),  char(116),  char(117),  char(118),  char(119),  char(120),  char(121),  char(122),  char(123),
    char(124),  char(125),  char(126),  char(127)};

// char [-128, 127]
static std::array<char, 256> &
GetChars()
{
    static std::array<char, 256> chars;
    static std::once_flag flag;
    std::call_once(flag, [] {
        for (int i = 0; i < 256; i++) { chars[i] = static_cast<char>(i - 128); }
    });
    return chars;
}
}// namespace detail

class Slice {
public:
    using string_t                    = ::std::string;
    static constexpr std::size_t npos = std::string::npos;

    constexpr Slice() : data_(""), len_(0) {}

    // char
    constexpr Slice(const char &ch) : data_(&detail::kChars[128 + ch]), len_(1) {}

    // std::string
    Slice(const std::string &str) : data_(str.data()), len_(str.size()) {}

    // const char*
    Slice(const char *c_str) : data_(c_str), len_(strlen(c_str)) {}

    Slice(const void *data, size_t len) : data_(reinterpret_cast<const char *>(data)), len_(len) {}

    constexpr Slice(const char *begin, const char *end) : data_(begin), len_(end - begin) {}

    constexpr Slice(const char *data, size_t len) : data_(data), len_(len) {}

    constexpr Slice(const Slice &other) : data_(other.data_), len_(other.len_) {}

    char front() const
    {
        assert(len_ > 0);
        return *data_;
    }

    char back() const
    {
        assert(len_ > 0);
        return data_[len_ - 1];
    }

    Slice &operator=(const Slice &other)
    {
        data_ = other.data_;
        len_  = other.len_;
        return *this;
    }

    void RemovePrefix(size_t n)
    {
        assert(len_ >= n && n >= 0);
        data_ += n;
        len_ -= n;
    }

    void RemoveSuffix(size_t n)
    {
        assert(len_ >= n && n >= 0);
        len_ -= n;
    }

    Slice substr(size_t pos, size_t n) const
    {
        if (n == npos) { return substr(pos, len_ - pos); }

        assert(pos <= len_ && n <= len_ - pos);
        return Slice(data_ + pos, n);
    }

    Slice substr(size_t pos) const
    {
        assert(pos <= len_);
        return Slice(data_ + pos, len_ - pos);
    }

    std::string ToString() const { return std::string(data_, len_); }

    void clear()
    {
        data_ = "";
        len_  = 0;
    }

    std::size_t find(Slice s, std::size_t pos = 0) const;

    std::size_t find_first_of(Slice s) const { return find(s); }

    // TODO: Optimize it
    std::size_t find_last_of(Slice s) const
    {
        if (s.empty() || s.size() > size()) { return npos; }

        auto ptr = data_ + len_ - s.size();
        for (; ptr >= data_; --ptr) {
            if (std::find(s.begin(), s.end(), *ptr) != s.end()) { return ptr - data_; }
        }

        return npos;
    }

    const char *cbegin() const { return data_; }

    const char *cend() const { return data_ + len_; }

    const char *begin() const { return data_; }

    const char *end() const { return data_ + len_; }

    const char *data() const { return data_; }

    size_t size() const { return len_; }

    bool empty() const { return len_ == 0; }

    const char &operator[](size_t pos) const
    {
        assert(pos < len_ && pos >= 0);
        return data_[pos];
    }

    inline operator string_t() const { return ToString(); }

private:
    const char *data_{nullptr};
    size_t len_{0};
};

inline bool
operator==(const Slice &lhs, const Slice &rhs)
{
    return lhs.size() == rhs.size() && memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}

inline bool
operator!=(const Slice &lhs, const Slice &rhs)
{
    return !(lhs == rhs);
}

inline bool
operator<(const Slice &lhs, const Slice &rhs)
{
    const size_t min_len = std::min(lhs.size(), rhs.size());
    const int r          = memcmp(lhs.data(), rhs.data(), min_len);
    return r < 0 || (r == 0 && lhs.size() < rhs.size());
}

inline bool
operator==(const char *lhs, const Slice &rhs)
{
    return Slice(lhs) == rhs;
}

inline bool
operator!=(const char *lhs, const Slice &rhs)
{
    return Slice(lhs) != rhs;
}

inline std::ostream &
operator<<(std::ostream &os, const Slice &s)
{
    return os << s.ToString();
}

inline std::string
operator+(const Slice &lhs, const Slice &rhs)
{
    return lhs.ToString() + rhs.ToString();
}

static_assert(std::is_constructible<Slice, char>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, char &>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const char>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const char &>::value, "Can't Build Sliece");

static_assert(std::is_constructible<Slice, char[]>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const char[]>::value, "Can't Build Sliece");

static_assert(std::is_constructible<Slice, char[2]>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const char[2]>::value, "Can't Build Sliece");

static_assert(std::is_constructible<Slice, char *>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const char *>::value, "Can't Build Sliece");

static_assert(std::is_constructible<Slice, void *, int>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const void *, int>::value, "Can't Build Sliece");

static_assert(std::is_constructible<Slice, std::string>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, std::string &>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const std::string>::value, "Can't Build Sliece");
static_assert(std::is_constructible<Slice, const std::string &>::value, "Can't Build Sliece");

}// namespace tile

namespace std {
template<>
struct hash<::tile::Slice> {
    std::size_t operator()(const ::tile::Slice &s) const noexcept { return 0; }
};
}// namespace std

#endif// _TILE_BASE_SLICE_H
