#ifndef _TILE_BASE_TYPE_INDEX_H
#define _TILE_BASE_TYPE_INDEX_H

#pragma once

#include <functional>
#include <type_traits>
#include <typeindex>

namespace tile {
namespace detail {
struct TypeIndexEntity {
    std::type_index runtime_type_index;
};

}// namespace detail

class TypeIndex {
public:
    TypeIndex() noexcept : entity_(nullptr) {}

    std::type_index GetRuntimeTypeIndex() const { return entity_->runtime_type_index; }

    constexpr bool operator==(TypeIndex other) const { return entity_ == other.entity_; }

    constexpr bool operator!=(TypeIndex other) const { return entity_ != other.entity_; }

    constexpr bool operator<(TypeIndex other) const { return entity_ < other.entity_; }

private:
    template<typename T>
    friend TypeIndex GetTypeIndex();
    friend struct std::hash<TypeIndex>;

    constexpr explicit TypeIndex(const detail::TypeIndexEntity *entity) : entity_(entity) {}

private:
    const detail::TypeIndexEntity *entity_;
};

template<typename T>
TypeIndex
GetTypeIndex()
{
    static detail::TypeIndexEntity entity{std::type_index(typeid(T))};
    return TypeIndex(&entity);
}
}// namespace tile

namespace std {
template<>
struct hash<tile::TypeIndex> {
    size_t operator()(const tile::TypeIndex &type_index) const noexcept
    {
        return std::hash<const void *>{}(type_index.entity_);
    }
};
}// namespace std

#endif// _TILE_BASE_TYPE_INDEX_H
