#ifndef MONOFORMAT_BITHELPERS_HPP
#define MONOFORMAT_BITHELPERS_HPP

#include <cstddef>
#include <cstdint>
#include <climits>
#include <concepts>

namespace monoformat {

template<std::integral Int, std::integral OtherInt>
constexpr inline bool isBitSet(Int value, OtherInt bit) {
    if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
        return false;
    }

    Int mask = Int{1} << static_cast<Int>(bit);
    return (value & mask) == mask;
}

template<std::integral OtherInt>
constexpr inline bool isBitSet(std::byte value, OtherInt bit) {
    return isBitSet(static_cast<std::uint8_t>(value), bit);
}

template<std::integral Int, std::integral OtherInt>
constexpr inline void setBit(Int& value, OtherInt bit) {
    if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
        return;
    }

    Int mask = Int{1} << static_cast<Int>(bit);
    value |= mask;
}

template<std::integral OtherInt>
constexpr inline void setBit(std::byte& value, OtherInt bit) {
    if (bit < 0 || bit >= CHAR_BIT) [[unlikely]] {
        return;
    }

    std::uint8_t mask = std::uint8_t{1} << static_cast<std::uint8_t>(bit);
    value = static_cast<std::byte>(static_cast<std::uint8_t>(value) | mask);
}

template<std::integral Int, std::integral OtherInt>
constexpr inline void unsetBit(Int& value, OtherInt bit) {
    if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
        return;
    }

    Int mask = Int{1} << static_cast<Int>(bit);
    value &= ~mask;
}

template<std::integral OtherInt>
constexpr inline void unsetBit(std::byte& value, OtherInt bit) {
    if (bit < 0 || bit >= CHAR_BIT) [[unlikely]] {
        return;
    }

    std::uint8_t mask = std::uint8_t{1} << static_cast<std::uint8_t>(bit);
    value = static_cast<std::byte>(static_cast<std::uint8_t>(value) & ~mask);
}

template<std::integral Int, std::integral OtherInt>
constexpr inline void maybeSetBit(Int& value, OtherInt bit, bool set) {
    if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
        return;
    }

    Int mask = Int{1} << static_cast<Int>(bit);
    if (set) {
        value |= mask;
    } else {
        value &= ~mask;
    }
}

template<std::integral OtherInt>
constexpr inline void maybeSetBit(std::byte& value, OtherInt bit, bool set) {
    if (bit < 0 || bit >= CHAR_BIT) [[unlikely]] {
        return;
    }

    std::uint8_t mask = std::uint8_t{1} << static_cast<std::uint8_t>(bit);
    if (set) {
        value = static_cast<std::byte>(static_cast<std::uint8_t>(value) | mask);
    } else {
        value = static_cast<std::byte>(static_cast<std::uint8_t>(value) & ~mask);
    }
}

} // namespace monoformat

#endif // MONOFORMAT_BITHELPERS_HPP
