#ifndef SLED_UTILITY_SLICE_H
#define SLED_UTILITY_SLICE_H

#pragma once

#include <cassert>
#include <cstring>
#include <string>

namespace sled {
class Slice {
public:
    Slice() : data_(""), size_(0) {}

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

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

    Slice(const char *str) : data_(str), size_(strlen(str)) {}

    Slice(const Slice &)            = default;
    Slice &operator=(const Slice &) = default;

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

    size_t size() const { return size_; }

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

    // <  0 iff *this <  b
    // == 0 iff *this == b
    // >  0 iff *this >  b
    int compare(const Slice &b) const;

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

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

    void Clear()
    {
        data_ = "";
        size_ = 0;
    }

    void TrimLeft(const Slice &trim = " \t\n\r")
    {
        while (size_ > 0 && strchr(trim.data(), data_[0]) != nullptr) {
            data_++;
            size_--;
        }
    }

    void TrimRight(const Slice &trim = " \t\n\r")
    {
        while (size_ > 0 && strchr(trim.data(), data_[size_ - 1]) != nullptr) { size_--; }
    }

    void Trim(const Slice &trim = " \t\n\r")
    {
        TrimLeft(trim);
        TrimRight(trim);
    }

    void RemovePrefix(size_t len)
    {
        assert(len >= 0 && len <= size());
        data_ += len;
        size_ -= len;
    }

    void RemoveSuffix(size_t len)
    {
        assert(len >= 0 && len <= size());
        size_ -= len;
    }

    bool StartsWithIngnoreCase(const Slice &prefix) const
    {
        return ((size_ >= prefix.size_) && (strncasecmp(data_, prefix.data_, prefix.size_)) == 0);
    }

    bool EndsWithIgnoreCase(const Slice &suffix) const
    {
        return ((size_ >= suffix.size_)
                && (strncasecmp(data_ + size_ - suffix.size_, suffix.data_, suffix.size_)) == 0);
    }

    bool StartsWith(const Slice &prefix) const
    {
        return ((size_ >= prefix.size_) && (memcmp(data_, prefix.data_, prefix.size_)) == 0);
    }

    bool EndsWith(const Slice &suffix) const
    {
        return ((size_ >= suffix.size_) && (memcmp(data_ + size_ - suffix.size_, suffix.data_, suffix.size_)) == 0);
    }

private:
    const char *data_;
    size_t size_;
};

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

inline bool
operator!=(const Slice &x, const Slice &y)
{
    return !(x == y);
}

inline int
Slice::compare(const Slice &y) const
{
    const size_t min_len = (size_ < y.size_) ? size_ : y.size_;
    int res              = memcmp(data_, y.data_, min_len);
    if (res == 0 && size_ != y.size_) {
        // size_ < y.size_ -> -1
        // size_ > y.size_ -> 1
        return size_ < y.size_ ? -1 : +1;
    }
    return res;
}

}// namespace sled

#endif// SLED_UTILITY_SLICE_H
