#ifndef _TILE_BASE_ENUM_H
#define _TILE_BASE_ENUM_H

#pragma once

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

namespace tile {
template<class T, class = enable_if_t<std::is_enum<T>::value>>
constexpr auto
underlying_value(T v) -> typename std::underlying_type<T>::type
{
    return static_cast<typename std::underlying_type<T>::type>(v);
}

#define TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, Op)                                                                   \
    constexpr Type operator Op(Type left, Type right)                                                                  \
    {                                                                                                                  \
        return static_cast<Type>(::tile::underlying_value(left) Op ::tile::underlying_value(right));                   \
    }
#define TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, Op)                                                                    \
    constexpr Type operator Op(Type v) { return static_cast<Type>(Op ::tile::underlying_value(v)); }
#define TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, Op)                                                            \
    constexpr Type &operator Op(Type & left, Type right) { return left Op right; }

#define TILE_DEFINE_ENUM_BITMASK_OPS(Type)                                                                             \
    TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, |)                                                                        \
    TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, &)                                                                        \
    TILE_DEFINE_ENUM_BITMASK_BINARY_OP(Type, ^)                                                                        \
    TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, |=)                                                                \
    TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, &=)                                                                \
    TILE_DEFINE_ENUM_BITMASK_BINARY_ASSIGN_OP(Type, ^=)                                                                \
    TILE_DEFINE_ENUM_BITMASK_UNARY_OP(Type, ~)                                                                         \
    constexpr bool operator!(Type value) { return !::tile::underlying_value(value); }

}// namespace tile

#endif// _TILE_BASE_ENUM_H
