#ifndef CIRCULAR_INT_HPP_
#define CIRCULAR_INT_HPP_

#include <Serialize/Utils.hpp>
#include <cstdint>
#include <Utilities/Extern.hpp>
#include <Serialize/Serialize.hpp>
#include <type_traits>

/**
 * @brief Represents an int in circular space.
 */
template<typename T>
class cint
{
private:
    T value;

public:
    using UnsignedT = std::make_unsigned_t<T>;

    constexpr cint(T value = 0) : value(value) {}

    bool operator<(const cint& other) const
    {
        return (*this - other).value < 0;
    }

    bool operator>(const cint& other) const
    {
        return (*this - other).value > 0;
    }

    bool operator<=(const cint& other) const
    {
        return !(*this > other);
    }

    bool operator>=(const cint& other) const
    {
        return !(*this < other);
    }

    bool operator==(const cint& other) const
    {
        return value == other.value;
    }

    bool operator==(const T &other) const
    {
        return value == other;
    }

    template<typename RHS>
    cint operator+(RHS rhs) const
    {
        return cint((UnsignedT)value + (UnsignedT)cint(rhs).value);
    }

    template<typename RHS>
    cint operator-(RHS rhs) const
    {
        return cint((UnsignedT)value - (UnsignedT)cint(rhs).value);
    }

    template<typename RHS>
    cint operator*(RHS rhs) const
    {
        return cint((UnsignedT)value * (UnsignedT)cint(rhs).value);
    }

    template<typename RHS>
    cint operator/(RHS rhs) const
    {
        return cint(value / cint(rhs).value);
    }

    template<typename RHS>
    cint operator%(RHS rhs) const
    {
        return cint(value % cint(rhs).value);
    }    

    cint& operator+=(T offset)
    {
        *this = *this + offset;
        return *this;
    }

    cint& operator-=(T offset)
    {
        *this = *this - offset;
        return *this;
    }

    cint& operator<<=(size_t bits)
    {
        value <<= bits;
        return *this;
    }

    cint& operator>>=(size_t bits)
    {
        return *this = *this >> bits;
    }

    template<typename RHS>
    cint operator<<(RHS rhs) const
    {
        size_t bits = size_t(rhs);
        return cint(value << bits);
    }

    template<typename RHS>
    cint operator>>(RHS rhs) const
    {
        size_t bits = size_t(rhs);

        // Implement this as an ARITHEMTIC shift - built-in >> is implementation-defined.
        uint64_t u = (uint64_t)value;
        uint64_t shifted = u >> bits;
        if (value >= 0)
            return cint((int64_t)shifted);

        uint64_t mask = ~((~uint64_t{0}) >> bits);
        return cint((int64_t)(shifted | mask));
    }

    T get() const
    {
        return value;
    }

    operator T() const
    {
        return value;
    }

    static cint leftmost(cint a, cint b)
    {
        return a < b ? a : b;
    }

    static cint rightmost(cint a, cint b)
    {
        return a > b ? a : b;
    }

    friend struct SerialCodec<cint<T>>;
};

template<typename T>
DERIVE_STRUCT_SERIALIZER_PARTIAL(cint<T>, self.value);

using cint32 = cint<int32_t>;
using cint64 = cint<int64_t>;
using wvec2 = glm::tvec2<cint32>;
using wvec3 = glm::tvec3<cint32>;

#endif